Mozilla.java :  » IDE-Eclipse » swt » org » eclipse » swt » browser » Java Open Source

Java Open Source » IDE Eclipse » swt 
swt » org » eclipse » swt » browser » Mozilla.java
/*******************************************************************************
 * Copyright (c) 2003, 2009 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 java.lang.reflect.*;
import java.util.*;

import org.eclipse.swt.*;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.internal.*;
import org.eclipse.swt.internal.mozilla.*;
import org.eclipse.swt.internal.mozilla.init.*;
import org.eclipse.swt.layout.*;

class Mozilla extends WebBrowser {
  int /*long*/ embedHandle;
  nsIWebBrowser webBrowser;
  Object webBrowserObject;
  MozillaDelegate delegate;

  /* Interfaces for this Mozilla embedding notification */
  XPCOMObject supports;
  XPCOMObject weakReference;
  XPCOMObject webProgressListener;
  XPCOMObject  webBrowserChrome;
  XPCOMObject webBrowserChromeFocus;
  XPCOMObject embeddingSiteWindow;
  XPCOMObject interfaceRequestor;
  XPCOMObject supportsWeakReference;
  XPCOMObject contextMenuListener;  
  XPCOMObject uriContentListener;
  XPCOMObject tooltipListener;
  XPCOMObject domEventListener;
  XPCOMObject badCertListener;
  int chromeFlags = nsIWebBrowserChrome.CHROME_DEFAULT;
  int refCount, lastKeyCode, lastCharCode, authCount;
  int /*long*/ request;
  Point location, size;
  boolean visible, isChild, ignoreDispose, isRetrievingBadCert, isViewingErrorPage;
  Shell tip = null;
  Listener listener;
  Vector unhookedDOMWindows = new Vector ();
  String lastNavigateURL;
  byte[] htmlBytes;

  static nsIAppShell AppShell;
  static AppFileLocProvider LocationProvider;
  static WindowCreator2 WindowCreator;
  static int BrowserCount, NextJSFunctionIndex = 1;
  static Hashtable AllFunctions = new Hashtable (); 
  static boolean Initialized, IsPre_1_8, IsPre_1_9, PerformedVersionCheck, XPCOMWasGlued, XPCOMInitWasGlued;

  /* XULRunner detect constants */
  static final String GRERANGE_LOWER = "1.8.1.2"; //$NON-NLS-1$
  static final String GRERANGE_LOWER_FALLBACK = "1.8"; //$NON-NLS-1$
  static final boolean LowerRangeInclusive = true;
  static final String GRERANGE_UPPER = "1.9.*"; //$NON-NLS-1$
  static final boolean UpperRangeInclusive = true;

  static final int MAX_PORT = 65535;
  static final String SEPARATOR_OS = System.getProperty ("file.separator"); //$NON-NLS-1$
  static final String ABOUT_BLANK = "about:blank"; //$NON-NLS-1$
  static final String DISPOSE_LISTENER_HOOKED = "org.eclipse.swt.browser.Mozilla.disposeListenerHooked"; //$NON-NLS-1$
  static final String PREFIX_JAVASCRIPT = "javascript:"; //$NON-NLS-1$
  static final String PREFERENCE_CHARSET = "intl.charset.default"; //$NON-NLS-1$
  static final String PREFERENCE_DISABLEOPENDURINGLOAD = "dom.disable_open_during_load"; //$NON-NLS-1$
  static final String PREFERENCE_DISABLEWINDOWSTATUSCHANGE = "dom.disable_window_status_change"; //$NON-NLS-1$
  static final String PREFERENCE_LANGUAGES = "intl.accept_languages"; //$NON-NLS-1$
  static final String PREFERENCE_PROXYHOST_FTP = "network.proxy.ftp"; //$NON-NLS-1$
  static final String PREFERENCE_PROXYPORT_FTP = "network.proxy.ftp_port"; //$NON-NLS-1$
  static final String PREFERENCE_PROXYHOST_HTTP = "network.proxy.http"; //$NON-NLS-1$
  static final String PREFERENCE_PROXYPORT_HTTP = "network.proxy.http_port"; //$NON-NLS-1$
  static final String PREFERENCE_PROXYHOST_SSL = "network.proxy.ssl"; //$NON-NLS-1$
  static final String PREFERENCE_PROXYPORT_SSL = "network.proxy.ssl_port"; //$NON-NLS-1$
  static final String PREFERENCE_PROXYTYPE = "network.proxy.type"; //$NON-NLS-1$
  static final String PROFILE_AFTER_CHANGE = "profile-after-change"; //$NON-NLS-1$
  static final String PROFILE_BEFORE_CHANGE = "profile-before-change"; //$NON-NLS-1$
  static final String PROFILE_DIR = SEPARATOR_OS + "eclipse" + SEPARATOR_OS; //$NON-NLS-1$
  static final String PROFILE_DO_CHANGE = "profile-do-change"; //$NON-NLS-1$
  static final String PROPERTY_PROXYPORT = "network.proxy_port"; //$NON-NLS-1$
  static final String PROPERTY_PROXYHOST = "network.proxy_host"; //$NON-NLS-1$
  static final String SEPARATOR_LOCALE = "-"; //$NON-NLS-1$
  static final String SHUTDOWN_PERSIST = "shutdown-persist"; //$NON-NLS-1$
  static final String STARTUP = "startup"; //$NON-NLS-1$
  static final String TOKENIZER_LOCALE = ","; //$NON-NLS-1$
  static final String URI_FROMMEMORY = "file:///"; //$NON-NLS-1$
  static final String XULRUNNER_PATH = "org.eclipse.swt.browser.XULRunnerPath"; //$NON-NLS-1$

  // TEMPORARY CODE
  static final String GRE_INITIALIZED = "org.eclipse.swt.browser.XULRunnerInitialized"; //$NON-NLS-1$

  static {
    MozillaClearSessions = new Runnable () {
      public void run () {
        if (!Initialized) return;
        int /*long*/[] result = new int /*long*/[1];
        int rc = XPCOM.NS_GetServiceManager (result);
        if (rc != XPCOM.NS_OK) error (rc);
        if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
        nsIServiceManager serviceManager = new nsIServiceManager (result[0]);
        result[0] = 0;
        byte[] aContractID = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_COOKIEMANAGER_CONTRACTID, true);
        rc = serviceManager.GetServiceByContractID (aContractID, nsICookieManager.NS_ICOOKIEMANAGER_IID, result);
        if (rc != XPCOM.NS_OK) error (rc);
        if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
        serviceManager.Release ();

        nsICookieManager manager = new nsICookieManager (result[0]);
        result[0] = 0;
        rc = manager.GetEnumerator (result);
        if (rc != XPCOM.NS_OK) error (rc);

        nsISimpleEnumerator enumerator = new nsISimpleEnumerator (result[0]);
        int[] moreElements = new int[1]; /* PRBool */
        rc = enumerator.HasMoreElements (moreElements);
        if (rc != XPCOM.NS_OK) error (rc);
        while (moreElements[0] != 0) {
          result[0] = 0;
          rc = enumerator.GetNext (result);
          if (rc != XPCOM.NS_OK) error (rc);
          nsICookie cookie = new nsICookie (result[0]);
          long[] expires = new long[1];
          rc = cookie.GetExpires (expires);
          if (expires[0] == 0) {
            /* indicates a session cookie */
            int /*long*/ domain = XPCOM.nsEmbedCString_new ();
            int /*long*/ name = XPCOM.nsEmbedCString_new ();
            int /*long*/ path = XPCOM.nsEmbedCString_new ();
            cookie.GetHost (domain);
            cookie.GetName (name);
            cookie.GetPath (path);
            rc = manager.Remove (domain, name, path, 0);
            XPCOM.nsEmbedCString_delete (domain);
            XPCOM.nsEmbedCString_delete (name);
            XPCOM.nsEmbedCString_delete (path);
            if (rc != XPCOM.NS_OK) error (rc);
          }
          cookie.Release ();
          rc = enumerator.HasMoreElements (moreElements);
          if (rc != XPCOM.NS_OK) error (rc);
        }
        enumerator.Release ();
        manager.Release ();
      }
    };

    MozillaGetCookie = new Runnable() {
      public void run() {
        if (!Initialized) return;

        int /*long*/[] result = new int /*long*/[1];
        int rc = XPCOM.NS_GetServiceManager (result);
        if (rc != XPCOM.NS_OK) error (rc);
        if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

        nsIServiceManager serviceManager = new nsIServiceManager (result[0]);
        result[0] = 0;
        rc = serviceManager.GetService (XPCOM.NS_IOSERVICE_CID, nsIIOService.NS_IIOSERVICE_IID, result);
        if (rc != XPCOM.NS_OK) error (rc);
        if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

        nsIIOService ioService = new nsIIOService (result[0]);
        result[0] = 0;
        byte[] bytes = MozillaDelegate.wcsToMbcs (null, CookieUrl, false);
        int /*long*/ aSpec = XPCOM.nsEmbedCString_new (bytes, bytes.length);
        rc = ioService.NewURI (aSpec, null, 0, result);
        XPCOM.nsEmbedCString_delete (aSpec);
        ioService.Release ();
        if (rc != XPCOM.NS_OK) {
          serviceManager.Release ();
          return;
        }
        if (result[0] == 0) error (XPCOM.NS_ERROR_NULL_POINTER);

        nsIURI aURI = new nsIURI (result[0]);
        result[0] = 0;
        byte[] aContractID = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_COOKIESERVICE_CONTRACTID, true);
        rc = serviceManager.GetServiceByContractID (aContractID, nsICookieService.NS_ICOOKIESERVICE_IID, result);
        int /*long*/ cookieString;
        if (rc == XPCOM.NS_OK && result[0] != 0) {
          nsICookieService cookieService = new nsICookieService (result[0]);
          result[0] = 0;
          rc = cookieService.GetCookieString (aURI.getAddress(), 0, result);
          cookieService.Release ();
          if (rc != XPCOM.NS_OK) error (rc);
          if (result[0] == 0) {
            aURI.Release ();
            serviceManager.Release ();
            return;
          }
          cookieString = result[0];
        } else {
          result[0] = 0;
          rc = serviceManager.GetServiceByContractID (aContractID, nsICookieService_1_9.NS_ICOOKIESERVICE_IID, result);
          if (rc != XPCOM.NS_OK) error (rc);
          if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
          nsICookieService_1_9 cookieService = new nsICookieService_1_9 (result[0]);
          result[0] = 0;
          rc = cookieService.GetCookieString(aURI.getAddress(), 0, result);
          cookieService.Release ();
          if (rc != XPCOM.NS_OK) error (rc);
          if (result[0] == 0) {
            aURI.Release ();
            serviceManager.Release ();
            return;
          }
          cookieString = result[0];
        }
        aURI.Release ();
        serviceManager.Release ();
        result[0] = 0;

        int length = C.strlen (cookieString);
        bytes = new byte[length];
        XPCOM.memmove (bytes, cookieString, length);
        C.free (cookieString);
        String allCookies = new String (MozillaDelegate.mbcsToWcs (null, bytes));
        StringTokenizer tokenizer = new StringTokenizer (allCookies, ";"); //$NON-NLS-1$
        while (tokenizer.hasMoreTokens ()) {
          String cookie = tokenizer.nextToken ();
          int index = cookie.indexOf ('=');
          if (index != -1) {
            String name = cookie.substring (0, index).trim ();
            if (name.equals (CookieName)) {
              CookieValue = cookie.substring (index + 1).trim ();
              return;
            }
          }
        }
      }
    };

    MozillaSetCookie = new Runnable() {
      public void run() {
        if (!Initialized) return;

        int /*long*/[] result = new int /*long*/[1];
        int rc = XPCOM.NS_GetServiceManager (result);
        if (rc != XPCOM.NS_OK) error (rc);
        if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

        nsIServiceManager serviceManager = new nsIServiceManager (result[0]);
        result[0] = 0;
        rc = serviceManager.GetService (XPCOM.NS_IOSERVICE_CID, nsIIOService.NS_IIOSERVICE_IID, result);
        if (rc != XPCOM.NS_OK) error (rc);
        if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

        nsIIOService ioService = new nsIIOService (result[0]);
        result[0] = 0;
        byte[] bytes = MozillaDelegate.wcsToMbcs (null, CookieUrl, false);
        int /*long*/ aSpec = XPCOM.nsEmbedCString_new (bytes, bytes.length);
        rc = ioService.NewURI (aSpec, null, 0, result);
        XPCOM.nsEmbedCString_delete (aSpec);
        ioService.Release ();
        if (rc != XPCOM.NS_OK) {
          serviceManager.Release ();
          return;
        }
        if (result[0] == 0) error (XPCOM.NS_ERROR_NULL_POINTER);

        nsIURI aURI = new nsIURI(result[0]);
        result[0] = 0;
        byte[] aCookie = MozillaDelegate.wcsToMbcs (null, CookieValue, true);
        byte[] aContractID = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_COOKIESERVICE_CONTRACTID, true);
        rc = serviceManager.GetServiceByContractID (aContractID, nsICookieService.NS_ICOOKIESERVICE_IID, result);
        if (rc == XPCOM.NS_OK && result[0] != 0) {
          nsICookieService cookieService = new nsICookieService (result[0]);
          rc = cookieService.SetCookieString (aURI.getAddress(), 0, aCookie, 0);
          cookieService.Release ();
        } else {
          result[0] = 0;
          rc = serviceManager.GetServiceByContractID (aContractID, nsICookieService_1_9.NS_ICOOKIESERVICE_IID, result);
          if (rc != XPCOM.NS_OK) error (rc);
          if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
          nsICookieService_1_9 cookieService = new nsICookieService_1_9 (result[0]);
          rc = cookieService.SetCookieString(aURI.getAddress(), 0, aCookie, 0);
          cookieService.Release ();
        }
        result[0] = 0;
        aURI.Release ();
        serviceManager.Release ();
        CookieResult = rc == 0;
      }
    };
  }

public void create (Composite parent, int style) {
  delegate = new MozillaDelegate (browser);
  final Display display = parent.getDisplay ();

  int /*long*/[] result = new int /*long*/[1];
  if (!Initialized) {
    boolean initLoaded = false;
    boolean IsXULRunner = false;

    String greInitialized = System.getProperty (GRE_INITIALIZED); 
    if ("true".equals (greInitialized)) { //$NON-NLS-1$
      /* 
       * Another browser has already initialized xulrunner in this process,
       * so just bind to it instead of trying to initialize a new one.
       */
      Initialized = true;
    }

    String mozillaPath = System.getProperty (XULRUNNER_PATH);
    /*
    * Browser clients that ship XULRunner in a plug-in must have an opportunity 
    * to set the org.eclipse.swt.browser.XULRunnerPath system property to point
    * at their XULRunner before the first Mozilla-based Browser is created.  To
    * facilitate this, reflection is used to reference non-existent class
    * org.eclipse.swt.browser.XULRunnerInitializer the first time a Mozilla-
    * based Browser is created.   A client wishing to use this hook can do so
    * by creating a fragment of org.eclipse.swt that implements this class and
    * sets the system property in its static initializer.
    */
    if (mozillaPath == null) {
      try {
        Class.forName ("org.eclipse.swt.browser.XULRunnerInitializer"); //$NON-NLS-1$
        mozillaPath = System.getProperty (XULRUNNER_PATH);
      } catch (ClassNotFoundException e) {
        /* no fragment is providing this class, which is the typical case */
      }
    }

    if (mozillaPath == null) {
      try {
        String libName = delegate.getSWTInitLibraryName ();
        Library.loadLibrary (libName);
        initLoaded = true;
      } catch (UnsatisfiedLinkError e) {
        /* 
        * If this library failed to load then do not attempt to detect a
        * xulrunner to use.  The Browser may still be usable if MOZILLA_FIVE_HOME
        * points at a GRE. 
        */
      }
    } else {
      mozillaPath += SEPARATOR_OS + delegate.getLibraryName ();
      IsXULRunner = true;
    }

    if (initLoaded) {
      /* attempt to discover a XULRunner to use as the GRE */
      GREVersionRange range = new GREVersionRange ();
      byte[] bytes = MozillaDelegate.wcsToMbcs (null, GRERANGE_LOWER, true);
      int /*long*/ lower = C.malloc (bytes.length);
      C.memmove (lower, bytes, bytes.length);
      range.lower = lower;
      range.lowerInclusive = LowerRangeInclusive;

      bytes = MozillaDelegate.wcsToMbcs (null, GRERANGE_UPPER, true);
      int /*long*/ upper = C.malloc (bytes.length);
      C.memmove (upper, bytes, bytes.length);
      range.upper = upper;
      range.upperInclusive = UpperRangeInclusive;

      int length = XPCOMInit.PATH_MAX;
      int /*long*/ greBuffer = C.malloc (length);
      int /*long*/ propertiesPtr = C.malloc (2 * C.PTR_SIZEOF);
      int rc = XPCOMInit.GRE_GetGREPathWithProperties (range, 1, propertiesPtr, 0, greBuffer, length);

      /*
       * A XULRunner was not found that supports wrapping of XPCOM handles as JavaXPCOM objects.
       * Drop the lower version bound and try to detect an earlier XULRunner installation.
       */
      if (rc != XPCOM.NS_OK) {
        C.free (lower);
        bytes = MozillaDelegate.wcsToMbcs (null, GRERANGE_LOWER_FALLBACK, true);
        lower = C.malloc (bytes.length);
        C.memmove (lower, bytes, bytes.length);
        range.lower = lower;
        rc = XPCOMInit.GRE_GetGREPathWithProperties (range, 1, propertiesPtr, 0, greBuffer, length);
      }

      C.free (lower);
      C.free (upper);
      C.free (propertiesPtr);
      if (rc == XPCOM.NS_OK) {
        /* indicates that a XULRunner was found */
        length = C.strlen (greBuffer);
        bytes = new byte[length];
        C.memmove (bytes, greBuffer, length);
        mozillaPath = new String (MozillaDelegate.mbcsToWcs (null, bytes));
        IsXULRunner = mozillaPath.length () > 0;

        /*
         * Test whether the detected XULRunner can be used as the GRE before loading swt's
         * XULRunner library.  If it cannot be used then fall back to attempting to use
         * the GRE pointed to by MOZILLA_FIVE_HOME.
         * 
         * One case where this will fail is attempting to use a 64-bit xulrunner while swt
         * is running in 32-bit mode, or vice versa.
         */
        if (IsXULRunner) {
          byte[] path = MozillaDelegate.wcsToMbcs (null, mozillaPath, true);
          rc = XPCOMInit.XPCOMGlueStartup (path);
          if (rc != XPCOM.NS_OK) {
            mozillaPath = mozillaPath.substring (0, mozillaPath.lastIndexOf (SEPARATOR_OS));
            if (Device.DEBUG) System.out.println ("cannot use detected XULRunner: " + mozillaPath); //$NON-NLS-1$

            /* attempt to XPCOMGlueStartup the GRE pointed at by MOZILLA_FIVE_HOME */
            int /*long*/ ptr = C.getenv (MozillaDelegate.wcsToMbcs (null, XPCOM.MOZILLA_FIVE_HOME, true));
            if (ptr == 0) {
              IsXULRunner = false;
            } else {
              length = C.strlen (ptr);
              byte[] buffer = new byte[length];
              C.memmove (buffer, ptr, length);
              mozillaPath = new String (MozillaDelegate.mbcsToWcs (null, buffer));
              /*
               * Attempting to XPCOMGlueStartup a mozilla-based GRE != xulrunner can
               * crash, so don't attempt unless the GRE appears to be xulrunner.
               */
              if (mozillaPath.indexOf("xulrunner") == -1) { //$NON-NLS-1$
                IsXULRunner = false;  
              } else {
                mozillaPath += SEPARATOR_OS + delegate.getLibraryName ();
                path = MozillaDelegate.wcsToMbcs (null, mozillaPath, true);
                rc = XPCOMInit.XPCOMGlueStartup (path);
                if (rc != XPCOM.NS_OK) {
                  IsXULRunner = false;
                  mozillaPath = mozillaPath.substring (0, mozillaPath.lastIndexOf (SEPARATOR_OS));
                  if (Device.DEBUG) System.out.println ("failed to start as XULRunner: " + mozillaPath); //$NON-NLS-1$
                }
              }
            } 
          }
          if (IsXULRunner) {
            XPCOMInitWasGlued = true;
          }
        }
      }
      C.free (greBuffer);
    }

    if (IsXULRunner) {
      if (Device.DEBUG) System.out.println ("XULRunner path: " + mozillaPath); //$NON-NLS-1$
      try {
        Library.loadLibrary ("swt-xulrunner"); //$NON-NLS-1$
      } catch (UnsatisfiedLinkError e) {
        SWT.error (SWT.ERROR_NO_HANDLES, e);
      }
      byte[] path = MozillaDelegate.wcsToMbcs (null, mozillaPath, true);
      int rc = XPCOM.XPCOMGlueStartup (path);
      if (rc != XPCOM.NS_OK) {
        browser.dispose ();
        error (rc);
      }
      XPCOMWasGlued = true;

      /*
       * Remove the trailing xpcom lib name from mozillaPath because the
       * Mozilla.initialize and NS_InitXPCOM2 invocations require a directory name only.
       */ 
      mozillaPath = mozillaPath.substring (0, mozillaPath.lastIndexOf (SEPARATOR_OS));
    } else {
      if ((style & SWT.MOZILLA) != 0) {
        browser.dispose ();
        String errorString = (mozillaPath != null && mozillaPath.length () > 0) ?
          " [Failed to use detected XULRunner: " + mozillaPath + "]" :
          " [Could not detect registered XULRunner to use]";  //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
        SWT.error (SWT.ERROR_NO_HANDLES, null, errorString);
      }

      /* attempt to use the GRE pointed at by MOZILLA_FIVE_HOME */
      int /*long*/ ptr = C.getenv (MozillaDelegate.wcsToMbcs (null, XPCOM.MOZILLA_FIVE_HOME, true));
      if (ptr != 0) {
        int length = C.strlen (ptr);
        byte[] buffer = new byte[length];
        C.memmove (buffer, ptr, length);
        mozillaPath = new String (MozillaDelegate.mbcsToWcs (null, buffer));
      } else {
        browser.dispose ();
        SWT.error (SWT.ERROR_NO_HANDLES, null, " [Unknown Mozilla path (MOZILLA_FIVE_HOME not set)]"); //$NON-NLS-1$
      }
      if (Device.DEBUG) System.out.println ("Mozilla path: " + mozillaPath); //$NON-NLS-1$

      /*
      * Note.  Embedding a Mozilla GTK1.2 causes a crash.  The workaround
      * is to check the version of GTK used by Mozilla by looking for
      * the libwidget_gtk.so library used by Mozilla GTK1.2. Mozilla GTK2
      * uses the libwidget_gtk2.so library.   
      */
      if (Compatibility.fileExists (mozillaPath, "components/libwidget_gtk.so")) { //$NON-NLS-1$
        browser.dispose ();
        SWT.error (SWT.ERROR_NO_HANDLES, null, " [Mozilla GTK2 required (GTK1.2 detected)]"); //$NON-NLS-1$              
      }

      try {
        Library.loadLibrary ("swt-mozilla"); //$NON-NLS-1$
      } catch (UnsatisfiedLinkError e) {
        try {
          /* 
           * The initial loadLibrary attempt may have failed as a result of the user's
           * system not having libstdc++.so.6 installed, so try to load the alternate
           * swt mozilla library that depends on libswtc++.so.5 instead.
           */
          Library.loadLibrary ("swt-mozilla-gcc3"); //$NON-NLS-1$
        } catch (UnsatisfiedLinkError ex) {
          browser.dispose ();
          /*
           * Print the error from the first failed attempt since at this point it's
           * known that the failure was not due to the libstdc++.so.6 dependency.
           */
          SWT.error (SWT.ERROR_NO_HANDLES, e, " [MOZILLA_FIVE_HOME='" + mozillaPath + "']"); //$NON-NLS-1$ //$NON-NLS-2$
        }
      }
    }

    if (!Initialized) {
      LocationProvider = new AppFileLocProvider (mozillaPath);
      LocationProvider.AddRef ();

      /* extract external.xpt to temp */
      String tempPath = System.getProperty ("java.io.tmpdir"); //$NON-NLS-1$
      File componentsDir = new File (tempPath, "eclipse/mozillaComponents"); //$NON-NLS-1$
      java.io.InputStream is = Library.class.getResourceAsStream ("/external.xpt"); //$NON-NLS-1$
      if (is != null) {
        if (!componentsDir.exists ()) {
          componentsDir.mkdirs ();
        }
        int read;
        byte [] buffer = new byte [4096];
        File file = new File (componentsDir, "external.xpt"); //$NON-NLS-1$
        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 (componentsDir.exists () && componentsDir.isDirectory ()) {
        LocationProvider.setComponentsPath (componentsDir.getAbsolutePath ());
      }

      int /*long*/[] retVal = new int /*long*/[1];
      nsEmbedString pathString = new nsEmbedString (mozillaPath);
      int rc = XPCOM.NS_NewLocalFile (pathString.getAddress (), 1, retVal);
      pathString.dispose ();
      if (rc != XPCOM.NS_OK) {
        browser.dispose ();
        error (rc);
      }
      if (retVal[0] == 0) {
        browser.dispose ();
        error (XPCOM.NS_ERROR_NULL_POINTER);
      }

      nsIFile localFile = new nsILocalFile (retVal[0]);
      if (IsXULRunner) {
        int size = XPCOM.nsDynamicFunctionLoad_sizeof ();
        /* alloc memory for two structs, the second is empty to signify the end of the list */
        int /*long*/ ptr = C.malloc (size * 2);
        C.memset (ptr, 0, size * 2);
        nsDynamicFunctionLoad functionLoad = new nsDynamicFunctionLoad ();
        byte[] bytes = MozillaDelegate.wcsToMbcs (null, "XRE_InitEmbedding", true); //$NON-NLS-1$
        functionLoad.functionName = C.malloc (bytes.length);
        C.memmove (functionLoad.functionName, bytes, bytes.length);
        functionLoad.function = C.malloc (C.PTR_SIZEOF);
        C.memmove (functionLoad.function, new int /*long*/[] {0} , C.PTR_SIZEOF);
        XPCOM.memmove (ptr, functionLoad, XPCOM.nsDynamicFunctionLoad_sizeof ());
        XPCOM.XPCOMGlueLoadXULFunctions (ptr);
        C.memmove (result, functionLoad.function, C.PTR_SIZEOF);
        int /*long*/ functionPtr = result[0];
        result[0] = 0;
        C.free (functionLoad.function);
        C.free (functionLoad.functionName);
        C.free (ptr);
        if (functionPtr == 0) {
                browser.dispose ();
                error (XPCOM.NS_ERROR_NULL_POINTER);
        }
        rc = XPCOM.Call (functionPtr, localFile.getAddress (), localFile.getAddress (), LocationProvider.getAddress (), 0, 0);
        if (rc == XPCOM.NS_OK) {
          System.setProperty (XULRUNNER_PATH, mozillaPath);
        }
      } else {
        rc = XPCOM.NS_InitXPCOM2 (0, localFile.getAddress(), LocationProvider.getAddress ());
      }
      localFile.Release ();
      if (rc != XPCOM.NS_OK) {
        browser.dispose ();
        SWT.error (SWT.ERROR_NO_HANDLES, null, " [MOZILLA_FIVE_HOME may not point at an embeddable GRE] [NS_InitEmbedding " + mozillaPath + " error " + rc + "]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
      }
      System.setProperty (GRE_INITIALIZED, "true"); //$NON-NLS-1$
    }

    /* If JavaXPCOM is detected then attempt to initialize it with the XULRunner being used */
    if (IsXULRunner) {
      try {
        Class clazz = Class.forName ("org.mozilla.xpcom.Mozilla"); //$NON-NLS-1$
        Method method = clazz.getMethod ("getInstance", new Class[0]); //$NON-NLS-1$
        Object mozilla = method.invoke (null, new Object[0]);
        method = clazz.getMethod ("getComponentManager", new Class[0]); //$NON-NLS-1$
        try {
          method.invoke (mozilla, new Object[0]);
        } catch (InvocationTargetException e) {
          /* indicates that JavaXPCOM has not been initialized yet */
          Class fileClass = Class.forName ("java.io.File"); //$NON-NLS-1$
          method = clazz.getMethod ("initialize", new Class[] {fileClass}); //$NON-NLS-1$
          Constructor constructor = fileClass.getDeclaredConstructor (new Class[] {String.class});
          Object argument = constructor.newInstance (new Object[] {mozillaPath});
          method.invoke (mozilla, new Object[] {argument});
        }
      } catch (ClassNotFoundException e) {
        /* JavaXPCOM is not on the classpath */
      } catch (NoSuchMethodException e) {
        /* the JavaXPCOM on the classpath does not implement initialize() */
      } catch (IllegalArgumentException e) {
      } catch (IllegalAccessException e) {
      } catch (InvocationTargetException e) {
      } catch (InstantiationException e) {
      }
    }

    int rc = XPCOM.NS_GetComponentManager (result);
    if (rc != XPCOM.NS_OK) {
      browser.dispose ();
      error (rc);
    }
    if (result[0] == 0) {
      browser.dispose ();
      error (XPCOM.NS_NOINTERFACE);
    }
    
    nsIComponentManager componentManager = new nsIComponentManager (result[0]);
    result[0] = 0;
    if (delegate.needsSpinup ()) {
      /* nsIAppShell is discontinued as of xulrunner 1.9, so do not fail if it is not found */
      rc = componentManager.CreateInstance (XPCOM.NS_APPSHELL_CID, 0, nsIAppShell.NS_IAPPSHELL_IID, result);
      if (rc != XPCOM.NS_ERROR_NO_INTERFACE) {
        if (rc != XPCOM.NS_OK) {
          browser.dispose ();
          error (rc);
        }
        if (result[0] == 0) {
          browser.dispose ();
          error (XPCOM.NS_NOINTERFACE);
        }
  
        AppShell = new nsIAppShell (result[0]);
        rc = AppShell.Create (0, null);
        if (rc != XPCOM.NS_OK) {
          browser.dispose ();
          error (rc);
        }
        rc = AppShell.Spinup ();
        if (rc != XPCOM.NS_OK) {
          browser.dispose ();
          error (rc);
        }
      }
      result[0] = 0;
    }

    WindowCreator = new WindowCreator2 ();
    WindowCreator.AddRef ();
    
    rc = XPCOM.NS_GetServiceManager (result);
    if (rc != XPCOM.NS_OK) {
      browser.dispose ();
      error (rc);
    }
    if (result[0] == 0) {
      browser.dispose ();
      error (XPCOM.NS_NOINTERFACE);
    }
    
    nsIServiceManager serviceManager = new nsIServiceManager (result[0]);
    result[0] = 0;    
    byte[] aContractID = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_WINDOWWATCHER_CONTRACTID, true);
    rc = serviceManager.GetServiceByContractID (aContractID, nsIWindowWatcher.NS_IWINDOWWATCHER_IID, result);
    if (rc != XPCOM.NS_OK) {
      browser.dispose ();
      error (rc);
    }
    if (result[0] == 0) {
      browser.dispose ();
      error (XPCOM.NS_NOINTERFACE);    
    }

    nsIWindowWatcher windowWatcher = new nsIWindowWatcher (result[0]);
    result[0] = 0;
    rc = windowWatcher.SetWindowCreator (WindowCreator.getAddress());
    if (rc != XPCOM.NS_OK) {
      browser.dispose ();
      error (rc);
    }
    windowWatcher.Release ();

    /* compute the profile directory and set it on the AppFileLocProvider */
    if (LocationProvider != null) {
      byte[] buffer = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_DIRECTORYSERVICE_CONTRACTID, true);
      rc = serviceManager.GetServiceByContractID (buffer, nsIDirectoryService.NS_IDIRECTORYSERVICE_IID, result);
      if (rc != XPCOM.NS_OK) {
        browser.dispose ();
        error (rc);
      }
      if (result[0] == 0) {
        browser.dispose ();
        error (XPCOM.NS_NOINTERFACE);
      }

      nsIDirectoryService directoryService = new nsIDirectoryService (result[0]);
      result[0] = 0;
      rc = directoryService.QueryInterface (nsIProperties.NS_IPROPERTIES_IID, result);
      if (rc != XPCOM.NS_OK) {
        browser.dispose ();
        error (rc);
      }
      if (result[0] == 0) {
        browser.dispose ();
        error (XPCOM.NS_NOINTERFACE);
      }
      directoryService.Release ();

      nsIProperties properties = new nsIProperties (result[0]);
      result[0] = 0;
      buffer = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_APP_APPLICATION_REGISTRY_DIR, true);
      rc = properties.Get (buffer, nsIFile.NS_IFILE_IID, result);
      if (rc != XPCOM.NS_OK) {
        browser.dispose ();
        error (rc);
      }
      if (result[0] == 0) {
        browser.dispose ();
        error (XPCOM.NS_NOINTERFACE);
      }
      properties.Release ();

      nsIFile profileDir = new nsIFile (result[0]);
      result[0] = 0;
      int /*long*/ path = XPCOM.nsEmbedCString_new ();
      rc = profileDir.GetNativePath (path);
      if (rc != XPCOM.NS_OK) {
        browser.dispose ();
        error (rc);
      }
      int length = XPCOM.nsEmbedCString_Length (path);
      int /*long*/ ptr = XPCOM.nsEmbedCString_get (path);
      buffer = new byte [length];
      XPCOM.memmove (buffer, ptr, length);
      String profilePath = new String (MozillaDelegate.mbcsToWcs (null, buffer)) + PROFILE_DIR;
      LocationProvider.setProfilePath (profilePath);
      LocationProvider.isXULRunner = IsXULRunner;
      XPCOM.nsEmbedCString_delete (path);
      profileDir.Release ();

      /* notify observers of a new profile directory being used */
      buffer = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_OBSERVER_CONTRACTID, true);
      rc = serviceManager.GetServiceByContractID (buffer, nsIObserverService.NS_IOBSERVERSERVICE_IID, result);
      if (rc != XPCOM.NS_OK) {
        browser.dispose ();
        error (rc);
      }
      if (result[0] == 0) {
        browser.dispose ();
        error (XPCOM.NS_NOINTERFACE);
      }

      nsIObserverService observerService = new nsIObserverService (result[0]);
      result[0] = 0;
      buffer = MozillaDelegate.wcsToMbcs (null, PROFILE_DO_CHANGE, true);
      length = STARTUP.length ();
      char[] chars = new char [length + 1];
      STARTUP.getChars (0, length, chars, 0);
      rc = observerService.NotifyObservers (0, buffer, chars);
      if (rc != XPCOM.NS_OK) {
        browser.dispose ();
        error (rc);
      }
      buffer = MozillaDelegate.wcsToMbcs (null, PROFILE_AFTER_CHANGE, true);
      rc = observerService.NotifyObservers (0, buffer, chars);
      if (rc != XPCOM.NS_OK) {
        browser.dispose ();
        error (rc);
      }
      observerService.Release ();

          if (IsXULRunner) {
        int size = XPCOM.nsDynamicFunctionLoad_sizeof ();
        /* alloc memory for two structs, the second is empty to signify the end of the list */
        ptr = C.malloc (size * 2);
        C.memset (ptr, 0, size * 2);
        nsDynamicFunctionLoad functionLoad = new nsDynamicFunctionLoad ();
        byte[] bytes = MozillaDelegate.wcsToMbcs (null, "XRE_NotifyProfile", true); //$NON-NLS-1$
        functionLoad.functionName = C.malloc (bytes.length);
        C.memmove (functionLoad.functionName, bytes, bytes.length);
        functionLoad.function = C.malloc (C.PTR_SIZEOF);
        C.memmove (functionLoad.function, new int /*long*/[] {0} , C.PTR_SIZEOF);
        XPCOM.memmove (ptr, functionLoad, XPCOM.nsDynamicFunctionLoad_sizeof ());
        XPCOM.XPCOMGlueLoadXULFunctions (ptr);
        C.memmove (result, functionLoad.function, C.PTR_SIZEOF);
        int /*long*/ functionPtr = result[0];
        result[0] = 0;
        C.free (functionLoad.function);
        C.free (functionLoad.functionName);
        C.free (ptr);
        /* functionPtr == 0 for xulrunner < 1.9 */
        if (functionPtr != 0) {
          rc = XPCOM.Call (functionPtr);
                if (rc != XPCOM.NS_OK) {
                  browser.dispose ();
                  error (rc);
                }
        }
          }
    }

    /*
     * As a result of using a common profile the user cannot change their locale
     * and charset.  The fix for this is to set mozilla's locale and charset
     * preference values according to the user's current locale and charset.
     */
    aContractID = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_PREFSERVICE_CONTRACTID, true);
    rc = serviceManager.GetServiceByContractID (aContractID, nsIPrefService.NS_IPREFSERVICE_IID, result);
    if (rc != XPCOM.NS_OK) {
      browser.dispose ();
      error (rc);
    }
    if (result[0] == 0) {
      browser.dispose ();
      error (XPCOM.NS_NOINTERFACE);
    }

    nsIPrefService prefService = new nsIPrefService (result[0]);
    result[0] = 0;
    byte[] buffer = new byte[1];
    rc = prefService.GetBranch (buffer, result);  /* empty buffer denotes root preference level */
    prefService.Release ();
    if (rc != XPCOM.NS_OK) {
      browser.dispose ();
      error (rc);
    }
    if (result[0] == 0) {
      browser.dispose ();
      error (XPCOM.NS_NOINTERFACE);
    }

    nsIPrefBranch prefBranch = new nsIPrefBranch (result[0]);
    result[0] = 0;

    /* get Mozilla's current locale preference value */
    String prefLocales = null;
    nsIPrefLocalizedString localizedString = null;
    buffer = MozillaDelegate.wcsToMbcs (null, PREFERENCE_LANGUAGES, true);
    rc = prefBranch.GetComplexValue (buffer, nsIPrefLocalizedString.NS_IPREFLOCALIZEDSTRING_IID, result);
    /* 
     * Feature of Debian.  For some reason attempting to query for the current locale
     * preference fails on Debian.  The workaround for this is to assume a value of
     * "en-us,en" since this is typically the default value when mozilla is used without
     * a profile.
     */
    if (rc != XPCOM.NS_OK) {
      prefLocales = "en-us,en" + TOKENIZER_LOCALE;  //$NON-NLS-1$
    } else {
      if (result[0] == 0) {
        browser.dispose ();
        error (XPCOM.NS_NOINTERFACE);
      }
      localizedString = new nsIPrefLocalizedString (result[0]);
      result[0] = 0;
      rc = localizedString.ToString (result);
      if (rc != XPCOM.NS_OK) {
        browser.dispose ();
        error (rc);
      }
      if (result[0] == 0) {
        browser.dispose ();
        error (XPCOM.NS_NOINTERFACE);
      }
      int length = XPCOM.strlen_PRUnichar (result[0]);
      char[] dest = new char[length];
      XPCOM.memmove (dest, result[0], length * 2);
      prefLocales = new String (dest) + TOKENIZER_LOCALE;
    }
    result[0] = 0;

    /*
     * construct the new locale preference value by prepending the
     * user's current locale and language to the original value 
     */
    Locale locale = Locale.getDefault ();
    String language = locale.getLanguage ();
    String country = locale.getCountry ();
    StringBuffer stringBuffer = new StringBuffer (language);
    stringBuffer.append (SEPARATOR_LOCALE);
    stringBuffer.append (country.toLowerCase ());
    stringBuffer.append (TOKENIZER_LOCALE);
    stringBuffer.append (language);
    stringBuffer.append (TOKENIZER_LOCALE);
    String newLocales = stringBuffer.toString ();

    int start, end = -1;
    do {
      start = end + 1;
      end = prefLocales.indexOf (TOKENIZER_LOCALE, start);
      String token;
      if (end == -1) {
        token = prefLocales.substring (start);
      } else {
        token = prefLocales.substring (start, end);
      }
      if (token.length () > 0) {
        token = (token + TOKENIZER_LOCALE).trim ();
        /* ensure that duplicate locale values are not added */
        if (newLocales.indexOf (token) == -1) {
          stringBuffer.append (token);
        }
      }
    } while (end != -1);
    newLocales = stringBuffer.toString ();
    if (!newLocales.equals (prefLocales)) {
      /* write the new locale value */
      newLocales = newLocales.substring (0, newLocales.length () - TOKENIZER_LOCALE.length ()); /* remove trailing tokenizer */
      int length = newLocales.length ();
      char[] charBuffer = new char[length + 1];
      newLocales.getChars (0, length, charBuffer, 0);
      if (localizedString == null) {
        byte[] contractID = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_PREFLOCALIZEDSTRING_CONTRACTID, true);
        rc = componentManager.CreateInstanceByContractID (contractID, 0, nsIPrefLocalizedString.NS_IPREFLOCALIZEDSTRING_IID, result);
        if (rc != XPCOM.NS_OK) {
          browser.dispose ();
          error (rc);
        }
        if (result[0] == 0) {
          browser.dispose ();
          error (XPCOM.NS_NOINTERFACE);
        }
        localizedString = new nsIPrefLocalizedString (result[0]);
        result[0] = 0;
      }
      localizedString.SetDataWithLength (length, charBuffer);
      rc = prefBranch.SetComplexValue (buffer, nsIPrefLocalizedString.NS_IPREFLOCALIZEDSTRING_IID, localizedString.getAddress());
    }
    if (localizedString != null) {
      localizedString.Release ();
      localizedString = null;
    }

    /* get Mozilla's current charset preference value */
    String prefCharset = null;
    buffer = MozillaDelegate.wcsToMbcs (null, PREFERENCE_CHARSET, true);
    rc = prefBranch.GetComplexValue (buffer, nsIPrefLocalizedString.NS_IPREFLOCALIZEDSTRING_IID, result);
    /* 
     * Feature of Debian.  For some reason attempting to query for the current charset
     * preference fails on Debian.  The workaround for this is to assume a value of
     * "ISO-8859-1" since this is typically the default value when mozilla is used
     * without a profile.
     */
    if (rc != XPCOM.NS_OK) {
      prefCharset = "ISO-8859-1";  //$NON_NLS-1$
    } else {
      if (result[0] == 0) {
        browser.dispose ();
        error (XPCOM.NS_NOINTERFACE);
      }
      localizedString = new nsIPrefLocalizedString (result[0]);
      result[0] = 0;
      rc = localizedString.ToString (result);
      if (rc != XPCOM.NS_OK) {
        browser.dispose ();
        error (rc);
      }
      if (result[0] == 0) {
        browser.dispose ();
        error (XPCOM.NS_NOINTERFACE);
      }
      int length = XPCOM.strlen_PRUnichar (result[0]);
      char[] dest = new char[length];
      XPCOM.memmove (dest, result[0], length * 2);
      prefCharset = new String (dest);
    }
    result[0] = 0;

    String newCharset = System.getProperty ("file.encoding");  // $NON-NLS-1$
    if (!newCharset.equals (prefCharset)) {
      /* write the new charset value */
      int length = newCharset.length ();
      char[] charBuffer = new char[length + 1];
      newCharset.getChars (0, length, charBuffer, 0);
      if (localizedString == null) {
        byte[] contractID = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_PREFLOCALIZEDSTRING_CONTRACTID, true);
        rc = componentManager.CreateInstanceByContractID (contractID, 0, nsIPrefLocalizedString.NS_IPREFLOCALIZEDSTRING_IID, result);
        if (rc != XPCOM.NS_OK) {
          browser.dispose ();
          error (rc);
        }
        if (result[0] == 0) {
          browser.dispose ();
          error (XPCOM.NS_NOINTERFACE);
        }
        localizedString = new nsIPrefLocalizedString (result[0]);
        result[0] = 0;
      }
      localizedString.SetDataWithLength (length, charBuffer);
      rc = prefBranch.SetComplexValue (buffer, nsIPrefLocalizedString.NS_IPREFLOCALIZEDSTRING_IID, localizedString.getAddress ());
    }
    if (localizedString != null) localizedString.Release ();

    /*
    * Check for proxy values set as documented java properties and update mozilla's
    * preferences with these values if needed.
    */
    String proxyHost = System.getProperty (PROPERTY_PROXYHOST);
    String proxyPortString = System.getProperty (PROPERTY_PROXYPORT);

    int port = -1;
    if (proxyPortString != null) {
      try {
        int value = Integer.valueOf (proxyPortString).intValue ();
        if (0 <= value && value <= MAX_PORT) port = value;
      } catch (NumberFormatException e) {
        /* do nothing, java property has non-integer value */
      }
    }

    if (proxyHost != null) {
      byte[] contractID = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_PREFLOCALIZEDSTRING_CONTRACTID, true);
      rc = componentManager.CreateInstanceByContractID (contractID, 0, nsIPrefLocalizedString.NS_IPREFLOCALIZEDSTRING_IID, result);
      if (rc != XPCOM.NS_OK) error (rc);
      if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

      localizedString = new nsIPrefLocalizedString (result[0]);
      result[0] = 0;
      int length = proxyHost.length ();
      char[] charBuffer = new char[length + 1];
      proxyHost.getChars (0, length, charBuffer, 0);
      rc = localizedString.SetDataWithLength (length, charBuffer);
      if (rc != XPCOM.NS_OK) error (rc);
      buffer = MozillaDelegate.wcsToMbcs (null, PREFERENCE_PROXYHOST_FTP, true);
      rc = prefBranch.SetComplexValue (buffer, nsIPrefLocalizedString.NS_IPREFLOCALIZEDSTRING_IID, localizedString.getAddress ());
      if (rc != XPCOM.NS_OK) error (rc);
      buffer = MozillaDelegate.wcsToMbcs (null, PREFERENCE_PROXYHOST_HTTP, true);
      rc = prefBranch.SetComplexValue (buffer, nsIPrefLocalizedString.NS_IPREFLOCALIZEDSTRING_IID, localizedString.getAddress ());
      if (rc != XPCOM.NS_OK) error (rc);
      buffer = MozillaDelegate.wcsToMbcs (null, PREFERENCE_PROXYHOST_SSL, true);
      rc = prefBranch.SetComplexValue (buffer, nsIPrefLocalizedString.NS_IPREFLOCALIZEDSTRING_IID, localizedString.getAddress ());
      if (rc != XPCOM.NS_OK) error (rc);
      localizedString.Release ();
    }

    if (port != -1) {
      buffer = MozillaDelegate.wcsToMbcs (null, PREFERENCE_PROXYPORT_FTP, true);
      rc = prefBranch.SetIntPref (buffer, port);
      if (rc != XPCOM.NS_OK) error (rc);
      buffer = MozillaDelegate.wcsToMbcs (null, PREFERENCE_PROXYPORT_HTTP, true);
      rc = prefBranch.SetIntPref (buffer, port);
      if (rc != XPCOM.NS_OK) error (rc);
      buffer = MozillaDelegate.wcsToMbcs (null, PREFERENCE_PROXYPORT_SSL, true);
      rc = prefBranch.SetIntPref (buffer, port);
      if (rc != XPCOM.NS_OK) error (rc);
    }

    if (proxyHost != null || port != -1) {
      buffer = MozillaDelegate.wcsToMbcs (null, PREFERENCE_PROXYTYPE, true);
      rc = prefBranch.SetIntPref (buffer, 1);
      if (rc != XPCOM.NS_OK) error (rc);
    }

    /*
    * Ensure that windows that are shown during page loads are not blocked.  Firefox may
    * try to block these by default since such windows are often unwelcome, but this
    * assumption should not be made in the Browser's context.  Since the Browser client
    * is responsible for creating the new Browser and Shell in an OpenWindowListener,
    * they should decide whether the new window is unwelcome or not and act accordingly. 
    */
    buffer = MozillaDelegate.wcsToMbcs (null, PREFERENCE_DISABLEOPENDURINGLOAD, true);
    rc = prefBranch.SetBoolPref (buffer, 0);
    if (rc != XPCOM.NS_OK) {
      browser.dispose ();
      error (rc);
    }

    /* Ensure that the status text can be set through means like javascript */ 
    buffer = MozillaDelegate.wcsToMbcs (null, PREFERENCE_DISABLEWINDOWSTATUSCHANGE, true);
    rc = prefBranch.SetBoolPref (buffer, 0);
    if (rc != XPCOM.NS_OK) {
      browser.dispose ();
      error (rc);
    }

    prefBranch.Release ();

    PromptService2Factory factory = new PromptService2Factory ();
    factory.AddRef ();

    rc = componentManager.QueryInterface (nsIComponentRegistrar.NS_ICOMPONENTREGISTRAR_IID, result);
    if (rc != XPCOM.NS_OK) {
      browser.dispose ();
      error (rc);
    }
    if (result[0] == 0) {
      browser.dispose ();
      error (XPCOM.NS_NOINTERFACE);
    }
    
    nsIComponentRegistrar componentRegistrar = new nsIComponentRegistrar (result[0]);
    result[0] = 0;
    componentRegistrar.AutoRegister (0);   /* detect the External component */ 

    aContractID = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_PROMPTSERVICE_CONTRACTID, true); 
    byte[] aClassName = MozillaDelegate.wcsToMbcs (null, "Prompt Service", true); //$NON-NLS-1$
    rc = componentRegistrar.RegisterFactory (XPCOM.NS_PROMPTSERVICE_CID, aClassName, aContractID, factory.getAddress ());
    if (rc != XPCOM.NS_OK) {
      browser.dispose ();
      error (rc);
    }
    factory.Release ();

    ExternalFactory externalFactory = new ExternalFactory ();
    externalFactory.AddRef ();
    aContractID = MozillaDelegate.wcsToMbcs (null, XPCOM.EXTERNAL_CONTRACTID, true); 
    aClassName = MozillaDelegate.wcsToMbcs (null, "External", true); //$NON-NLS-1$
    rc = componentRegistrar.RegisterFactory (XPCOM.EXTERNAL_CID, aClassName, aContractID, externalFactory.getAddress ());
    if (rc != XPCOM.NS_OK) {
      browser.dispose ();
      error (rc);
    }
    externalFactory.Release ();

    rc = serviceManager.GetService (XPCOM.NS_CATEGORYMANAGER_CID, nsICategoryManager.NS_ICATEGORYMANAGER_IID, result);
    if (rc != XPCOM.NS_OK) error (rc);
    if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
    serviceManager.Release ();

    nsICategoryManager categoryManager = new nsICategoryManager (result[0]);
    result[0] = 0;
    byte[] category = MozillaDelegate.wcsToMbcs (null, "JavaScript global property", true); //$NON-NLS-1$
    byte[] entry = MozillaDelegate.wcsToMbcs (null, "external", true); //$NON-NLS-1$
    rc = categoryManager.AddCategoryEntry(category, entry, aContractID, 1, 1, result);
    result[0] = 0;
    categoryManager.Release ();

    /*
    * This Download factory will be used if the GRE version is < 1.8.
    * If the GRE version is 1.8.x then the Download factory that is registered later for
    *   contract "Transfer" will be used.
    * If the GRE version is >= 1.9 then no Download factory is registered because this
    *   functionality is provided by the GRE.
    */
    DownloadFactory downloadFactory = new DownloadFactory ();
    downloadFactory.AddRef ();
    aContractID = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_DOWNLOAD_CONTRACTID, true);
    aClassName = MozillaDelegate.wcsToMbcs (null, "Download", true); //$NON-NLS-1$
    rc = componentRegistrar.RegisterFactory (XPCOM.NS_DOWNLOAD_CID, aClassName, aContractID, downloadFactory.getAddress ());
    if (rc != XPCOM.NS_OK) {
      browser.dispose ();
      error (rc);
    }
    downloadFactory.Release ();

    FilePickerFactory pickerFactory = IsXULRunner ? new FilePickerFactory_1_8 () : new FilePickerFactory ();
    pickerFactory.AddRef ();
    aContractID = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_FILEPICKER_CONTRACTID, true);
    aClassName = MozillaDelegate.wcsToMbcs (null, "FilePicker", true); //$NON-NLS-1$
    rc = componentRegistrar.RegisterFactory (XPCOM.NS_FILEPICKER_CID, aClassName, aContractID, pickerFactory.getAddress ());
    if (rc != XPCOM.NS_OK) {
      browser.dispose ();
      error (rc);
    }
    pickerFactory.Release ();

    componentRegistrar.Release ();
    componentManager.Release ();

    Initialized = true;
  }

  if (display.getData (DISPOSE_LISTENER_HOOKED) == null) {
    display.setData (DISPOSE_LISTENER_HOOKED, DISPOSE_LISTENER_HOOKED);
    display.addListener (SWT.Dispose, new Listener () {
      public void handleEvent (Event event) {
        if (BrowserCount > 0) return; /* another display is still active */

        int /*long*/[] result = new int /*long*/[1];
        int rc = XPCOM.NS_GetServiceManager (result);
        if (rc != XPCOM.NS_OK) error (rc);
        if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

        nsIServiceManager serviceManager = new nsIServiceManager (result[0]);
        result[0] = 0;    
        byte[] buffer = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_OBSERVER_CONTRACTID, true);
        rc = serviceManager.GetServiceByContractID (buffer, nsIObserverService.NS_IOBSERVERSERVICE_IID, result);
        if (rc != XPCOM.NS_OK) error (rc);
        if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

        nsIObserverService observerService = new nsIObserverService (result[0]);
        result[0] = 0;
        buffer = MozillaDelegate.wcsToMbcs (null, PROFILE_BEFORE_CHANGE, true);
        int length = SHUTDOWN_PERSIST.length ();
        char[] chars = new char [length + 1];
        SHUTDOWN_PERSIST.getChars (0, length, chars, 0);
        rc = observerService.NotifyObservers (0, buffer, chars);
        if (rc != XPCOM.NS_OK) error (rc);
        observerService.Release ();

        if (LocationProvider != null) {
          String prefsLocation = LocationProvider.profilePath + AppFileLocProvider.PREFERENCES_FILE;
          nsEmbedString pathString = new nsEmbedString (prefsLocation);
          rc = XPCOM.NS_NewLocalFile (pathString.getAddress (), 1, result);
          if (rc != XPCOM.NS_OK) Mozilla.error (rc);
          if (result[0] == 0) Mozilla.error (XPCOM.NS_ERROR_NULL_POINTER);
          pathString.dispose ();

          nsILocalFile localFile = new nsILocalFile (result [0]);
          result[0] = 0;
            rc = localFile.QueryInterface (nsIFile.NS_IFILE_IID, result); 
          if (rc != XPCOM.NS_OK) Mozilla.error (rc);
          if (result[0] == 0) Mozilla.error (XPCOM.NS_ERROR_NO_INTERFACE);
          localFile.Release ();

          nsIFile prefFile = new nsIFile (result[0]);
          result[0] = 0;

          buffer = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_PREFSERVICE_CONTRACTID, true);
          rc = serviceManager.GetServiceByContractID (buffer, nsIPrefService.NS_IPREFSERVICE_IID, result);
          if (rc != XPCOM.NS_OK) error (rc);
          if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

          nsIPrefService prefService = new nsIPrefService (result[0]);
          result[0] = 0;
          rc = prefService.SavePrefFile(prefFile.getAddress ());
          prefService.Release ();
          prefFile.Release ();
        }
        serviceManager.Release ();

        if (XPCOMWasGlued) {
          /*
          * XULRunner 1.9 can crash on Windows if XPCOMGlueShutdown is invoked here,
          * presumably because one or more of its unloaded symbols are referenced when
          * this callback returns.  The workaround is to delay invoking XPCOMGlueShutdown
          * so that its symbols are still available once this callback returns.
          */
          display.asyncExec (new Runnable () {
            public void run () {
              XPCOM.XPCOMGlueShutdown ();
            }
          });

          // the following is intentionally commented, because calling XRE_TermEmbedding
          // causes subsequent browser instantiations within the process to fail

//          int size = XPCOM.nsDynamicFunctionLoad_sizeof ();
//          /* alloc memory for two structs, the second is empty to signify the end of the list */
//          int /*long*/ ptr = C.malloc (size * 2);
//          C.memset (ptr, 0, size * 2);
//          nsDynamicFunctionLoad functionLoad = new nsDynamicFunctionLoad ();
//          byte[] bytes = MozillaDelegate.wcsToMbcs (null, "XRE_TermEmbedding", true); //$NON-NLS-1$
//          functionLoad.functionName = C.malloc (bytes.length);
//          C.memmove (functionLoad.functionName, bytes, bytes.length);
//          functionLoad.function = C.malloc (C.PTR_SIZEOF);
//          C.memmove (functionLoad.function, new int /*long*/[] {0} , C.PTR_SIZEOF);
//          XPCOM.memmove (ptr, functionLoad, XPCOM.nsDynamicFunctionLoad_sizeof ());
//          XPCOM.XPCOMGlueLoadXULFunctions (ptr);
//          C.memmove (result, functionLoad.function, C.PTR_SIZEOF);
//          int /*long*/ functionPtr = result[0];
//          result[0] = 0;
//          C.free (functionLoad.function);
//          C.free (functionLoad.functionName);
//          C.free (ptr);
//          XPCOM.Call (functionPtr);

          XPCOMWasGlued = false;
        }
        if (XPCOMInitWasGlued) {
          XPCOMInit.XPCOMGlueShutdown ();
          XPCOMInitWasGlued = false;
        }
        Initialized = false;
      }
    });
  }

  BrowserCount++;
  int rc = XPCOM.NS_GetComponentManager (result);
  if (rc != XPCOM.NS_OK) {
    browser.dispose ();
    error (rc);
  }
  if (result[0] == 0) {
    browser.dispose ();
    error (XPCOM.NS_NOINTERFACE);
  }
  
  nsIComponentManager componentManager = new nsIComponentManager (result[0]);
  result[0] = 0;
  nsID NS_IWEBBROWSER_CID = new nsID ("F1EAC761-87E9-11d3-AF80-00A024FFC08C"); //$NON-NLS-1$
  rc = componentManager.CreateInstance (NS_IWEBBROWSER_CID, 0, nsIWebBrowser.NS_IWEBBROWSER_IID, result);
  if (rc != XPCOM.NS_OK) {
    browser.dispose ();
    error (rc);
  }
  if (result[0] == 0) {
    browser.dispose ();
    error (XPCOM.NS_NOINTERFACE);  
  }
  
  webBrowser = new nsIWebBrowser (result[0]);
  result[0] = 0;

  createCOMInterfaces ();
  AddRef ();

  rc = webBrowser.SetContainerWindow (webBrowserChrome.getAddress());
  if (rc != XPCOM.NS_OK) {
    browser.dispose ();
    error (rc);
  }
      
  rc = webBrowser.QueryInterface (nsIBaseWindow.NS_IBASEWINDOW_IID, result);
  if (rc != XPCOM.NS_OK) {
    browser.dispose ();
    error (rc);
  }
  if (result[0] == 0) {
    browser.dispose ();
    error (XPCOM.NS_ERROR_NO_INTERFACE);
  }
  
  nsIBaseWindow baseWindow = new nsIBaseWindow (result[0]);
  result[0] = 0;
  Rectangle rect = browser.getClientArea ();
  if (rect.isEmpty ()) {
    rect.width = 1;
    rect.height = 1;
  }

  embedHandle = delegate.getHandle ();

  rc = baseWindow.InitWindow (embedHandle, 0, 0, 0, rect.width, rect.height);
  if (rc != XPCOM.NS_OK) {
    browser.dispose ();
    error (XPCOM.NS_ERROR_FAILURE);
  }
  rc = delegate.createBaseWindow (baseWindow);
  if (rc != XPCOM.NS_OK) {
    browser.dispose ();
    error (XPCOM.NS_ERROR_FAILURE);
  }
  rc = baseWindow.SetVisibility (1);
  if (rc != XPCOM.NS_OK) {
    browser.dispose ();
    error (XPCOM.NS_ERROR_FAILURE);
  }
  baseWindow.Release ();

  if (!PerformedVersionCheck) {
    PerformedVersionCheck = true;

    rc = componentManager.QueryInterface (nsIComponentRegistrar.NS_ICOMPONENTREGISTRAR_IID, result);
    if (rc != XPCOM.NS_OK) {
      browser.dispose ();
      error (rc);
    }
    if (result[0] == 0) {
      browser.dispose ();
      error (XPCOM.NS_NOINTERFACE);
    }
    nsIComponentRegistrar componentRegistrar = new nsIComponentRegistrar (result[0]);
    result[0] = 0;

    HelperAppLauncherDialogFactory dialogFactory = new HelperAppLauncherDialogFactory ();
    dialogFactory.AddRef ();
    byte[] aContractID = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_HELPERAPPLAUNCHERDIALOG_CONTRACTID, true);
    byte[] aClassName = MozillaDelegate.wcsToMbcs (null, "Helper App Launcher Dialog", true); //$NON-NLS-1$
    rc = componentRegistrar.RegisterFactory (XPCOM.NS_HELPERAPPLAUNCHERDIALOG_CID, aClassName, aContractID, dialogFactory.getAddress ());
    if (rc != XPCOM.NS_OK) {
      browser.dispose ();
      error (rc);
    }
    dialogFactory.Release ();

    /*
    * Check for the availability of the pre-1.8 implementation of nsIDocShell
    * to determine if the GRE's version is < 1.8.
    */
    rc = webBrowser.QueryInterface (nsIInterfaceRequestor.NS_IINTERFACEREQUESTOR_IID, result);
    if (rc != XPCOM.NS_OK) {
      browser.dispose ();
      error (XPCOM.NS_ERROR_FAILURE);
    }
    if (result[0] == 0) {
      browser.dispose ();
      error (XPCOM.NS_ERROR_NO_INTERFACE);
    }
    nsIInterfaceRequestor interfaceRequestor = new nsIInterfaceRequestor (result[0]);
    result[0] = 0;

    rc = interfaceRequestor.GetInterface (nsIDocShell.NS_IDOCSHELL_IID, result);
    if (rc == XPCOM.NS_OK && result[0] != 0) {
      IsPre_1_8 = true;
      new nsISupports (result[0]).Release ();
    }
    result[0] = 0;
    IsPre_1_9 = true;

    /*
    * A Download factory for contract "Transfer" must be registered iff the GRE's version is 1.8.x.
    *   Check for the availability of the 1.8 implementation of nsIDocShell to determine if the
    *   GRE's version is 1.8.x.
    * If the GRE version is < 1.8 then the previously-registered Download factory for contract
    *   "Download" will be used.
    * If the GRE version is >= 1.9 then no Download factory is registered because this
    *   functionality is provided by the GRE.
    */
    if (!IsPre_1_8) {
      rc = interfaceRequestor.GetInterface (nsIDocShell_1_8.NS_IDOCSHELL_IID, result);
      if (rc == XPCOM.NS_OK && result[0] != 0) { /* 1.8 */
        new nsISupports (result[0]).Release ();
        result[0] = 0;

        DownloadFactory_1_8 downloadFactory_1_8 = new DownloadFactory_1_8 ();
        downloadFactory_1_8.AddRef ();
        aContractID = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_TRANSFER_CONTRACTID, true);
        aClassName = MozillaDelegate.wcsToMbcs (null, "Transfer", true); //$NON-NLS-1$
        rc = componentRegistrar.RegisterFactory (XPCOM.NS_DOWNLOAD_CID, aClassName, aContractID, downloadFactory_1_8.getAddress ());
        if (rc != XPCOM.NS_OK) {
          browser.dispose ();
          error (rc);
        }
        downloadFactory_1_8.Release ();
      } else { /* >= 1.9 */
        IsPre_1_9 = false;
      }
    }
    result[0] = 0;
    interfaceRequestor.Release ();
    componentRegistrar.Release ();
  }
  componentManager.Release ();

  /*
   * Bug in XULRunner 1.9.  On win32, Mozilla does not clear its background before content has
   * been set into it.  As a result, embedders appear broken if they do not immediately display
   * a URL or text.  The Mozilla bug for this is https://bugzilla.mozilla.org/show_bug.cgi?id=453523.
   * 
   * The workaround is to subclass the Mozilla window and clear it whenever WM_ERASEBKGND is received.
   * This subclass should be removed once content has been set into the browser.
   */
  if (!IsPre_1_9) {
    delegate.addWindowSubclass ();
  }

  rc = webBrowser.AddWebBrowserListener (weakReference.getAddress (), nsIWebProgressListener.NS_IWEBPROGRESSLISTENER_IID);
  if (rc != XPCOM.NS_OK) {
    browser.dispose ();
    error (rc);
  }

  rc = webBrowser.SetParentURIContentListener (uriContentListener.getAddress ());
  if (rc != XPCOM.NS_OK) {
    browser.dispose ();
    error (rc);
  }

  delegate.init ();

  listener = new Listener () {
    public void handleEvent (Event event) {
      switch (event.type) {
        case SWT.Dispose: {
          /* make this handler run after other dispose listeners */
          if (ignoreDispose) {
            ignoreDispose = false;
            break;
          }
          ignoreDispose = true;
          browser.notifyListeners (event.type, event);
          event.type = SWT.NONE;
          onDispose (event.display);
          break;
        }
        case SWT.Resize: onResize (); break;
        case SWT.FocusIn: Activate (); break;
        case SWT.Activate: Activate (); break;
        case SWT.Deactivate: {
          Display display = event.display;
          if (Mozilla.this.browser == display.getFocusControl ()) Deactivate ();
          break;
        }
        case SWT.Show: {
          /*
          * Feature in GTK Mozilla.  Mozilla does not show up when
          * its container (a GTK fixed handle) is made visible
          * after having been hidden.  The workaround is to reset
          * its size after the container has been made visible. 
          */
          Display display = event.display;
          display.asyncExec(new Runnable () {
            public void run() {
              if (browser.isDisposed ()) return;
              onResize ();
            }
          });
          break;
        }
      }
    }
  };  
  int[] folderEvents = new int[] {
    SWT.Dispose,
    SWT.Resize,  
    SWT.FocusIn,
    SWT.Activate,
    SWT.Deactivate,
    SWT.Show,
    SWT.KeyDown    // needed to make browser traversable
  };
  for (int i = 0; i < folderEvents.length; i++) {
    browser.addListener (folderEvents[i], listener);
  }
}

public boolean back () {
  htmlBytes = null;

  int /*long*/[] result = new int /*long*/[1];
  int rc = webBrowser.QueryInterface (nsIWebNavigation.NS_IWEBNAVIGATION_IID, result);
  if (rc != XPCOM.NS_OK) error (rc);
  if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);
  
  nsIWebNavigation webNavigation = new nsIWebNavigation (result[0]);       
  rc = webNavigation.GoBack ();  
  webNavigation.Release ();
  return rc == XPCOM.NS_OK;
}

void createCOMInterfaces () {
  // Create each of the interfaces that this object implements
  supports = new XPCOMObject (new int[] {2, 0, 0}) {
    public int /*long*/ method0 (int /*long*/[] args) {return QueryInterface (args[0], args[1]);}
    public int /*long*/ method1 (int /*long*/[] args) {return AddRef ();}
    public int /*long*/ method2 (int /*long*/[] args) {return Release ();}
  };
  
  weakReference = new XPCOMObject (new int[] {2, 0, 0, 2}) {
    public int /*long*/ method0 (int /*long*/[] args) {return QueryInterface (args[0], args[1]);}
    public int /*long*/ method1 (int /*long*/[] args) {return AddRef ();}
    public int /*long*/ method2 (int /*long*/[] args) {return Release ();}
    public int /*long*/ method3 (int /*long*/[] args) {return QueryReferent (args[0], args[1]);}
  };

  webProgressListener = new XPCOMObject (new int[] {2, 0, 0, 4, 6, 3, 4, 3}) {
    public int /*long*/ method0 (int /*long*/[] args) {return QueryInterface (args[0], args[1]);}
    public int /*long*/ method1 (int /*long*/[] args) {return AddRef ();}
    public int /*long*/ method2 (int /*long*/[] args) {return Release ();}
    public int /*long*/ method3 (int /*long*/[] args) {return OnStateChange (args[0], args[1], (int)/*64*/args[2], (int)/*64*/args[3]);}
    public int /*long*/ method4 (int /*long*/[] args) {return OnProgressChange (args[0], args[1], (int)/*64*/args[2], (int)/*64*/args[3], (int)/*64*/args[4], (int)/*64*/args[5]);}
    public int /*long*/ method5 (int /*long*/[] args) {return OnLocationChange (args[0], args[1], args[2]);}
    public int /*long*/ method6 (int /*long*/[] args) {return OnStatusChange (args[0], args[1], (int)/*64*/args[2], args[3]);}
    public int /*long*/ method7 (int /*long*/[] args) {return OnSecurityChange (args[0], args[1], (int)/*64*/args[2]);}
  };
  
  webBrowserChrome = new XPCOMObject (new int[] {2, 0, 0, 2, 1, 1, 1, 1, 0, 2, 0, 1, 1}) {
    public int /*long*/ method0 (int /*long*/[] args) {return QueryInterface (args[0], args[1]);}
    public int /*long*/ method1 (int /*long*/[] args) {return AddRef ();}
    public int /*long*/ method2 (int /*long*/[] args) {return Release ();}
    public int /*long*/ method3 (int /*long*/[] args) {return SetStatus ((int)/*64*/args[0], args[1]);}
    public int /*long*/ method4 (int /*long*/[] args) {return GetWebBrowser (args[0]);}
    public int /*long*/ method5 (int /*long*/[] args) {return SetWebBrowser (args[0]);}
    public int /*long*/ method6 (int /*long*/[] args) {return GetChromeFlags (args[0]);}
    public int /*long*/ method7 (int /*long*/[] args) {return SetChromeFlags ((int)/*64*/args[0]);}
    public int /*long*/ method8 (int /*long*/[] args) {return DestroyBrowserWindow ();}
    public int /*long*/ method9 (int /*long*/[] args) {return SizeBrowserTo ((int)/*64*/args[0], (int)/*64*/args[1]);}
    public int /*long*/ method10 (int /*long*/[] args) {return ShowAsModal ();}
    public int /*long*/ method11 (int /*long*/[] args) {return IsWindowModal (args[0]);}
    public int /*long*/ method12 (int /*long*/[] args) {return ExitModalEventLoop ((int)/*64*/args[0]);}
  };
  
  webBrowserChromeFocus = new XPCOMObject (new int[] {2, 0, 0, 0, 0}) {
    public int /*long*/ method0 (int /*long*/[] args) {return QueryInterface (args[0], args[1]);}
    public int /*long*/ method1 (int /*long*/[] args) {return AddRef ();}
    public int /*long*/ method2 (int /*long*/[] args) {return Release ();}
    public int /*long*/ method3 (int /*long*/[] args) {return FocusNextElement ();}
    public int /*long*/ method4 (int /*long*/[] args) {return FocusPrevElement ();}
  };
    
  embeddingSiteWindow = new XPCOMObject (new int[] {2, 0, 0, 5, 5, 0, 1, 1, 1, 1, 1}) {
    public int /*long*/ method0 (int /*long*/[] args) {return QueryInterface (args[0], args[1]);}
    public int /*long*/ method1 (int /*long*/[] args) {return AddRef ();}
    public int /*long*/ method2 (int /*long*/[] args) {return Release ();}
    public int /*long*/ method3 (int /*long*/[] args) {return SetDimensions ((int)/*64*/args[0], (int)/*64*/args[1], (int)/*64*/args[2], (int)/*64*/args[3], (int)/*64*/args[4]);}
    public int /*long*/ method4 (int /*long*/[] args) {return GetDimensions ((int)/*64*/args[0], args[1], args[2], args[3], args[4]);}
    public int /*long*/ method5 (int /*long*/[] args) {return SetFocus ();}
    public int /*long*/ method6 (int /*long*/[] args) {return GetVisibility (args[0]);}
    public int /*long*/ method7 (int /*long*/[] args) {return SetVisibility ((int)/*64*/args[0]);}
    public int /*long*/ method8 (int /*long*/[] args) {return GetTitle (args[0]);}
    public int /*long*/ method9 (int /*long*/[] args) {return SetTitle (args[0]);}
    public int /*long*/ method10 (int /*long*/[] args) {return GetSiteWindow (args[0]);}
  };
  
  interfaceRequestor = new XPCOMObject (new int[] {2, 0, 0, 2} ){
    public int /*long*/ method0 (int /*long*/[] args) {return QueryInterface (args[0], args[1]);}
    public int /*long*/ method1 (int /*long*/[] args) {return AddRef ();}
    public int /*long*/ method2 (int /*long*/[] args) {return Release ();}
    public int /*long*/ method3 (int /*long*/[] args) {return GetInterface (args[0], args[1]);}
  };
    
  supportsWeakReference = new XPCOMObject (new int[] {2, 0, 0, 1}) {
    public int /*long*/ method0 (int /*long*/[] args) {return QueryInterface (args[0], args[1]);}
    public int /*long*/ method1 (int /*long*/[] args) {return AddRef ();}
    public int /*long*/ method2 (int /*long*/[] args) {return Release ();}
    public int /*long*/ method3 (int /*long*/[] args) {return GetWeakReference (args[0]);}
  };
  
  contextMenuListener = new XPCOMObject (new int[] {2, 0, 0, 3}) {
    public int /*long*/ method0 (int /*long*/[] args) {return QueryInterface (args[0], args[1]);}
    public int /*long*/ method1 (int /*long*/[] args) {return AddRef ();}
    public int /*long*/ method2 (int /*long*/[] args) {return Release ();}
    public int /*long*/ method3 (int /*long*/[] args) {return OnShowContextMenu ((int)/*64*/args[0], args[1], args[2]);}
  };
  
  uriContentListener = new XPCOMObject (new int[] {2, 0, 0, 2, 5, 3, 4, 1, 1, 1, 1}) {
    public int /*long*/ method0 (int /*long*/[] args) {return QueryInterface (args[0], args[1]);}
    public int /*long*/ method1 (int /*long*/[] args) {return AddRef ();}
    public int /*long*/ method2 (int /*long*/[] args) {return Release ();}
    public int /*long*/ method3 (int /*long*/[] args) {return OnStartURIOpen (args[0], args[1]);}
    public int /*long*/ method4 (int /*long*/[] args) {return DoContent (args[0], (int)/*64*/args[1], args[2], args[3], args[4]);}
    public int /*long*/ method5 (int /*long*/[] args) {return IsPreferred (args[0], args[1], args[2]);}
    public int /*long*/ method6 (int /*long*/[] args) {return CanHandleContent (args[0], (int)/*64*/args[1], args[2], args[3]);}
    public int /*long*/ method7 (int /*long*/[] args) {return GetLoadCookie (args[0]);}
    public int /*long*/ method8 (int /*long*/[] args) {return SetLoadCookie (args[0]);}
    public int /*long*/ method9 (int /*long*/[] args) {return GetParentContentListener (args[0]);}
    public int /*long*/ method10 (int /*long*/[] args) {return SetParentContentListener (args[0]);}    
  };
  
  tooltipListener = new XPCOMObject (new int[] {2, 0, 0, 3, 0}) {
    public int /*long*/ method0 (int /*long*/[] args) {return QueryInterface (args[0], args[1]);}
    public int /*long*/ method1 (int /*long*/[] args) {return AddRef ();}
    public int /*long*/ method2 (int /*long*/[] args) {return Release ();}
    public int /*long*/ method3 (int /*long*/[] args) {return OnShowTooltip ((int)/*64*/args[0], (int)/*64*/args[1], args[2]);}
    public int /*long*/ method4 (int /*long*/[] args) {return OnHideTooltip ();}    
  };

  domEventListener = new XPCOMObject (new int[] {2, 0, 0, 1}) {
    public int /*long*/ method0 (int /*long*/[] args) {return QueryInterface (args[0], args[1]);}
    public int /*long*/ method1 (int /*long*/[] args) {return AddRef ();}
    public int /*long*/ method2 (int /*long*/[] args) {return Release ();}
    public int /*long*/ method3 (int /*long*/[] args) {return HandleEvent (args[0]);}
  };

  badCertListener = new XPCOMObject (new int[] {2, 0, 0, 4}) {
    public int /*long*/ method0 (int /*long*/[] args) {return QueryInterface (args[0], args[1]);}
    public int /*long*/ method1 (int /*long*/[] args) {return AddRef ();}
    public int /*long*/ method2 (int /*long*/[] args) {return Release ();}
    public int /*long*/ method3 (int /*long*/[] args) {return NotifyCertProblem (args[0], args[1], args[2], args[3]);}
  };
}

void deregisterFunction (BrowserFunction function) {
  super.deregisterFunction (function);
  AllFunctions.remove (new Integer (function.index));
}

void disposeCOMInterfaces () {
  if (supports != null) {
    supports.dispose ();
    supports = null;
  }  
  if (weakReference != null) {
    weakReference.dispose ();
    weakReference = null;  
  }
  if (webProgressListener != null) {
    webProgressListener.dispose ();
    webProgressListener = null;
  }
  if (webBrowserChrome != null) {
    webBrowserChrome.dispose ();
    webBrowserChrome = null;
  }
  if (webBrowserChromeFocus != null) {
    webBrowserChromeFocus.dispose ();
    webBrowserChromeFocus = null;
  }
  if (embeddingSiteWindow != null) {
    embeddingSiteWindow.dispose ();
    embeddingSiteWindow = null;
  }
  if (interfaceRequestor != null) {
    interfaceRequestor.dispose ();
    interfaceRequestor = null;
  }    
  if (supportsWeakReference != null) {
    supportsWeakReference.dispose ();
    supportsWeakReference = null;
  }  
  if (contextMenuListener != null) {
    contextMenuListener.dispose ();
    contextMenuListener = null;
  }
  if (uriContentListener != null) {
    uriContentListener.dispose ();
    uriContentListener = null;
  }
  if (tooltipListener != null) {
    tooltipListener.dispose ();
    tooltipListener = null;
  }
  if (domEventListener != null) {
    domEventListener.dispose ();
    domEventListener = null;
  }
  if (badCertListener != null) {
    badCertListener.dispose ();
    badCertListener = null;
  }
}

public boolean execute (String script) {
  /*
  * This could be the first content that is set into the browser, so
  * ensure that the custom subclass that works around Mozilla bug
  * https://bugzilla.mozilla.org/show_bug.cgi?id=453523 is removed.
  */
  delegate.removeWindowSubclass ();

  /*
  * As of mozilla 1.9 executing javascript via the javascript: protocol no
  * longer happens synchronously.  As a result, the result of executing JS
  * is not returned to the java side when expected by the client.  The
  * workaround is to invoke the javascript handler directly via C++, which is
  * exposed as of mozilla 1.9.
  */
  int /*long*/[] result = new int /*long*/[1];
  if (!IsPre_1_9) {
    int rc = XPCOM.NS_GetServiceManager (result);
    if (rc != XPCOM.NS_OK) error (rc);
    if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

    nsIServiceManager serviceManager = new nsIServiceManager (result[0]);
    result[0] = 0;
    nsIPrincipal principal = null;
    byte[] aContractID = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_SCRIPTSECURITYMANAGER_CONTRACTID, true);
    rc = serviceManager.GetServiceByContractID (aContractID, nsIScriptSecurityManager_1_9_1.NS_ISCRIPTSECURITYMANAGER_IID, result);
    if (rc == XPCOM.NS_OK && result[0] != 0) {
      nsIScriptSecurityManager_1_9_1 securityManager = new nsIScriptSecurityManager_1_9_1 (result[0]);
      result[0] = 0;
      rc = securityManager.GetSystemPrincipal (result);
      if (rc != XPCOM.NS_OK) error (rc);
      if (result[0] == 0) error (XPCOM.NS_ERROR_NULL_POINTER);
      principal = new nsIPrincipal (result[0]);
      result[0] = 0;
      securityManager.Release ();
    } else {
      rc = serviceManager.GetServiceByContractID (aContractID, nsIScriptSecurityManager_1_9.NS_ISCRIPTSECURITYMANAGER_IID, result);
      if (rc == XPCOM.NS_OK && result[0] != 0) {
        nsIScriptSecurityManager_1_9 securityManager = new nsIScriptSecurityManager_1_9 (result[0]);
        result[0] = 0;
        rc = securityManager.GetSystemPrincipal (result);
        if (rc != XPCOM.NS_OK) error (rc);
        if (result[0] == 0) error (XPCOM.NS_ERROR_NULL_POINTER);
        principal = new nsIPrincipal (result[0]);
        result[0] = 0;
        securityManager.Release ();
      }
    }
    serviceManager.Release ();

    if (principal != null) {
      rc = webBrowser.QueryInterface (nsIInterfaceRequestor.NS_IINTERFACEREQUESTOR_IID, result);
      if (rc != XPCOM.NS_OK) error (rc);
      if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);

      nsIInterfaceRequestor interfaceRequestor = new nsIInterfaceRequestor (result[0]);
      result[0] = 0;
      nsID scriptGlobalObjectNSID = new nsID ("6afecd40-0b9a-4cfd-8c42-0f645cd91829"); /* nsIScriptGlobalObject */ //$NON-NLS-1$
      rc = interfaceRequestor.GetInterface (scriptGlobalObjectNSID, result);
      interfaceRequestor.Release ();

      if (rc == XPCOM.NS_OK && result[0] != 0) {
        int /*long*/ scriptGlobalObject = result[0];
        result[0] = 0;
        rc = (int/*64*/)XPCOM.nsIScriptGlobalObject_EnsureScriptEnvironment (scriptGlobalObject, 2); /* nsIProgrammingLanguage.JAVASCRIPT */
        if (rc != XPCOM.NS_OK) error (rc);
        int /*long*/ scriptContext = XPCOM.nsIScriptGlobalObject_GetScriptContext (scriptGlobalObject, 2); /* nsIProgrammingLanguage.JAVASCRIPT */
        int /*long*/ globalJSObject = XPCOM.nsIScriptGlobalObject_GetScriptGlobal (scriptGlobalObject, 2); /* nsIProgrammingLanguage.JAVASCRIPT */
        new nsISupports (scriptGlobalObject).Release ();

        if (scriptContext != 0 && globalJSObject != 0) {
          /* ensure that the received nsIScriptContext implements the expected interface */
          nsID scriptContextNSID = new nsID ("e7b9871d-3adc-4bf7-850d-7fb9554886bf"); /* nsIScriptContext */ //$NON-NLS-1$          
          rc = new nsISupports (scriptContext).QueryInterface (scriptContextNSID, result);
          if (rc == XPCOM.NS_OK && result[0] != 0) {
            new nsISupports (result[0]).Release ();
            result[0] = 0;

            int /*long*/ nativeContext = XPCOM.nsIScriptContext_GetNativeContext (scriptContext);
            if (nativeContext != 0) {
              int length = script.length ();
              char[] scriptChars = new char[length];
              script.getChars(0, length, scriptChars, 0);
              byte[] urlbytes = MozillaDelegate.wcsToMbcs (null, getUrl (), true);
              rc = principal.GetJSPrincipals (nativeContext, result);
              if (rc == XPCOM.NS_OK && result[0] != 0) {
                int /*long*/ principals = result[0];
                result[0] = 0;
                principal.Release ();
                String mozillaPath = LocationProvider.mozillaPath + delegate.getJSLibraryName () + '\0';
                byte[] pathBytes = null;
                try {
                  pathBytes = mozillaPath.getBytes ("UTF-8"); //$NON-NLS-1$
                } catch (UnsupportedEncodingException e) {
                  pathBytes = mozillaPath.getBytes ();
                }
                rc = XPCOM.JS_EvaluateUCScriptForPrincipals (pathBytes, nativeContext, globalJSObject, principals, scriptChars, length, urlbytes, 0, result);
                return rc != 0;
              }
            }
          }
        }
      }
      principal.Release ();
    }
  }

  /* fall back to the pre-1.9 approach */

  String url = PREFIX_JAVASCRIPT + script + ";void(0);";  //$NON-NLS-1$
  int rc = webBrowser.QueryInterface (nsIWebNavigation.NS_IWEBNAVIGATION_IID, result);
  if (rc != XPCOM.NS_OK) error (rc);
  if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);

  nsIWebNavigation webNavigation = new nsIWebNavigation (result[0]);
    char[] arg = url.toCharArray (); 
    char[] c = new char[arg.length+1];
    System.arraycopy (arg, 0, c, 0, arg.length);
  rc = webNavigation.LoadURI (c, nsIWebNavigation.LOAD_FLAGS_NONE, 0, 0, 0);
  webNavigation.Release ();
  return rc == XPCOM.NS_OK;
}

static Browser findBrowser (int /*long*/ handle) {
  return MozillaDelegate.findBrowser (handle);
}

static Browser findBrowser (nsIDOMWindow aDOMWindow) {
  int /*long*/[] result = new int /*long*/[1];
  int rc = XPCOM.NS_GetServiceManager (result);
  if (rc != XPCOM.NS_OK) Mozilla.error (rc);
  if (result[0] == 0) Mozilla.error (XPCOM.NS_NOINTERFACE);

  nsIServiceManager serviceManager = new nsIServiceManager (result[0]);
  result[0] = 0;
  byte[] aContractID = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_WINDOWWATCHER_CONTRACTID, true);
  rc = serviceManager.GetServiceByContractID (aContractID, nsIWindowWatcher.NS_IWINDOWWATCHER_IID, result);
  if (rc != XPCOM.NS_OK) Mozilla.error(rc);
  if (result[0] == 0) Mozilla.error (XPCOM.NS_NOINTERFACE);    
  serviceManager.Release ();

  nsIWindowWatcher windowWatcher = new nsIWindowWatcher (result[0]);
  result[0] = 0;
  /* the chrome will only be answered for the top-level nsIDOMWindow */
  rc = aDOMWindow.GetTop (result);
  if (rc != XPCOM.NS_OK) Mozilla.error (rc);
  if (result[0] == 0) Mozilla.error (XPCOM.NS_NOINTERFACE);
  int /*long*/ topDOMWindow = result[0];
  result[0] = 0;
  rc = windowWatcher.GetChromeForWindow (topDOMWindow, result);
  if (rc != XPCOM.NS_OK) Mozilla.error (rc);
  new nsISupports (topDOMWindow).Release ();
  windowWatcher.Release ();
  if (result[0] == 0) return null;  /* the parent chrome is disconnected */

  nsIWebBrowserChrome webBrowserChrome = new nsIWebBrowserChrome (result[0]);
  result[0] = 0;
  rc = webBrowserChrome.QueryInterface (nsIEmbeddingSiteWindow.NS_IEMBEDDINGSITEWINDOW_IID, result);
  if (rc != XPCOM.NS_OK) Mozilla.error (rc);
  if (result[0] == 0) Mozilla.error (XPCOM.NS_NOINTERFACE);    
  webBrowserChrome.Release ();

  nsIEmbeddingSiteWindow embeddingSiteWindow = new nsIEmbeddingSiteWindow (result[0]);
  result[0] = 0;
  rc = embeddingSiteWindow.GetSiteWindow (result);
  if (rc != XPCOM.NS_OK) Mozilla.error (rc);
  if (result[0] == 0) Mozilla.error (XPCOM.NS_NOINTERFACE);    
  embeddingSiteWindow.Release ();

  return findBrowser (result[0]); 
}

public boolean forward () {
  htmlBytes = null;

  int /*long*/[] result = new int /*long*/[1];
  int rc = webBrowser.QueryInterface (nsIWebNavigation.NS_IWEBNAVIGATION_IID, result);
  if (rc != XPCOM.NS_OK) error (rc);
  if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);
  
  nsIWebNavigation webNavigation = new nsIWebNavigation (result[0]);
  rc = webNavigation.GoForward ();
  webNavigation.Release ();

  return rc == XPCOM.NS_OK;
}

public String getBrowserType () {
  return "mozilla"; //$NON-NLS-1$
}

int getNextFunctionIndex () {
  return NextJSFunctionIndex++;
}

public String getText () {
  int /*long*/[] result = new int /*long*/[1];
  int rc = webBrowser.GetContentDOMWindow (result);
  if (rc != XPCOM.NS_OK) error (rc);
  if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

  nsIDOMWindow window = new nsIDOMWindow (result[0]);
  result[0] = 0;
  rc = window.GetDocument (result);
  if (rc != XPCOM.NS_OK) error (rc);
  if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
  window.Release ();

  int /*long*/ document = result[0];
  result[0] = 0;
  rc = XPCOM.NS_GetComponentManager (result);
  if (rc != XPCOM.NS_OK) error (rc);
  if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

  nsIComponentManager componentManager = new nsIComponentManager (result[0]);
  result[0] = 0;
  byte[] contractID = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_DOMSERIALIZER_CONTRACTID, true);
  char[] chars = null;

  rc = componentManager.CreateInstanceByContractID (contractID, 0, nsIDOMSerializer_1_7.NS_IDOMSERIALIZER_IID, result);
  if (rc == XPCOM.NS_OK) {  /* mozilla >= 1.7 */
    if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

    nsIDOMSerializer_1_7 serializer = new nsIDOMSerializer_1_7 (result[0]);
    result[0] = 0;
    int /*long*/ string = XPCOM.nsEmbedString_new ();
    rc = serializer.SerializeToString (document, string);
    serializer.Release ();

    int length = XPCOM.nsEmbedString_Length (string);
    int /*long*/ buffer = XPCOM.nsEmbedString_get (string);
    chars = new char[length];
    XPCOM.memmove (chars, buffer, length * 2);
    XPCOM.nsEmbedString_delete (string);
  } else {  /* mozilla < 1.7 */
    rc = componentManager.CreateInstanceByContractID (contractID, 0, nsIDOMSerializer.NS_IDOMSERIALIZER_IID, result);
    if (rc != XPCOM.NS_OK) error (rc);
    if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

    nsIDOMSerializer serializer = new nsIDOMSerializer (result[0]);
    result[0] = 0;
    rc = serializer.SerializeToString (document, result);
    serializer.Release ();

    int length = XPCOM.strlen_PRUnichar (result[0]);
    chars = new char[length];
    XPCOM.memmove (chars, result[0], length * 2);
  }

  componentManager.Release ();
  new nsISupports (document).Release ();
  return new String (chars);
}

public String getUrl () {
  int /*long*/[] result = new int /*long*/[1];
  int rc = webBrowser.QueryInterface (nsIWebNavigation.NS_IWEBNAVIGATION_IID, result);
  if (rc != XPCOM.NS_OK) error (rc);
  if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);

  nsIWebNavigation webNavigation = new nsIWebNavigation (result[0]);
  int /*long*/[] aCurrentURI = new int /*long*/[1];
  rc = webNavigation.GetCurrentURI (aCurrentURI);
  if (rc != XPCOM.NS_OK) error (rc);
  webNavigation.Release ();

  byte[] dest = null;
  if (aCurrentURI[0] != 0) {
    nsIURI uri = new nsIURI (aCurrentURI[0]);
    int /*long*/ aSpec = XPCOM.nsEmbedCString_new ();
    rc = uri.GetSpec (aSpec);
    if (rc != XPCOM.NS_OK) error (rc);
    int length = XPCOM.nsEmbedCString_Length (aSpec);
    int /*long*/ buffer = XPCOM.nsEmbedCString_get (aSpec);
    dest = new byte[length];
    XPCOM.memmove (dest, buffer, length);
    XPCOM.nsEmbedCString_delete (aSpec);
    uri.Release ();
  }
  if (dest == null) return ""; //$NON-NLS-1$

  String location = new String (dest);
  /*
   * 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 (location.equals (URI_FROMMEMORY)) location = ABOUT_BLANK;
  return location;
}

public Object getWebBrowser () {
  if ((browser.getStyle () & SWT.MOZILLA) == 0) return null;
  if (webBrowserObject != null) return webBrowserObject;

  try {
    Class clazz = Class.forName ("org.mozilla.xpcom.Mozilla"); //$NON-NLS-1$
    Method method = clazz.getMethod ("getInstance", new Class[0]); //$NON-NLS-1$
    Object mozilla = method.invoke (null, new Object[0]);
    method = clazz.getMethod ("wrapXPCOMObject", new Class[] {Long.TYPE, String.class}); //$NON-NLS-1$
    webBrowserObject = method.invoke (mozilla, new Object[] {new Long (webBrowser.getAddress ()), nsIWebBrowser.NS_IWEBBROWSER_IID_STR});
    /*
     * The following AddRef() is needed to offset the automatic Release() that
     * will be performed by JavaXPCOM when webBrowserObject is finalized.
     */
    webBrowser.AddRef ();
    return webBrowserObject;
  } catch (ClassNotFoundException e) {
  } catch (NoSuchMethodException e) {
  } catch (IllegalArgumentException e) {
  } catch (IllegalAccessException e) {
  } catch (InvocationTargetException e) {
  }
  return null;
}

public boolean isBackEnabled () {
  int /*long*/[] result = new int /*long*/[1];
  int rc = webBrowser.QueryInterface (nsIWebNavigation.NS_IWEBNAVIGATION_IID, result);
  if (rc != XPCOM.NS_OK) error (rc);
  if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);
  
  nsIWebNavigation webNavigation = new nsIWebNavigation (result[0]);
  int[] aCanGoBack = new int[1]; /* PRBool */
  rc = webNavigation.GetCanGoBack (aCanGoBack);  
  webNavigation.Release ();
  return aCanGoBack[0] != 0;
}

public boolean isForwardEnabled () {
  int /*long*/[] result = new int /*long*/[1];
  int rc = webBrowser.QueryInterface (nsIWebNavigation.NS_IWEBNAVIGATION_IID, result);
  if (rc != XPCOM.NS_OK) error (rc);
  if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);
  
  nsIWebNavigation webNavigation = new nsIWebNavigation (result[0]);
  int[] aCanGoForward = new int[1]; /* PRBool */
  rc = webNavigation.GetCanGoForward (aCanGoForward);
  webNavigation.Release ();
  return aCanGoForward[0] != 0;
}

static String error (int code) {
  throw new SWTError ("XPCOM error " + code); //$NON-NLS-1$
}

void onDispose (Display display) {
  int rc = webBrowser.RemoveWebBrowserListener (weakReference.getAddress (), nsIWebProgressListener.NS_IWEBPROGRESSLISTENER_IID);
  if (rc != XPCOM.NS_OK) error (rc);

  rc = webBrowser.SetParentURIContentListener (0);
  if (rc != XPCOM.NS_OK) error (rc);
  
  rc = webBrowser.SetContainerWindow (0);
  if (rc != XPCOM.NS_OK) error (rc);

  unhookDOMListeners ();
  if (listener != null) {
    int[] folderEvents = new int[] {
      SWT.Dispose,
      SWT.Resize,  
      SWT.FocusIn,
      SWT.Activate,
      SWT.Deactivate,
      SWT.Show,
      SWT.KeyDown,
    };
    for (int i = 0; i < folderEvents.length; i++) {
      browser.removeListener (folderEvents[i], listener);
    }
    listener = null;
  }

  int /*long*/[] result = new int /*long*/[1];
  rc = webBrowser.QueryInterface (nsIBaseWindow.NS_IBASEWINDOW_IID, result);
  if (rc != XPCOM.NS_OK) error (rc);
  if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);

  nsIBaseWindow baseWindow = new nsIBaseWindow (result[0]);
  rc = baseWindow.Destroy ();
  if (rc != XPCOM.NS_OK) error (rc);
  baseWindow.Release ();

  Release ();
  webBrowser.Release ();
  webBrowser = null;
  webBrowserObject = null;
  lastNavigateURL = null;
  htmlBytes = null;

  if (tip != null && !tip.isDisposed ()) tip.dispose ();
  tip = null;
  location = size = null;

  Enumeration elements = unhookedDOMWindows.elements ();
  while (elements.hasMoreElements ()) {
    LONG ptrObject = (LONG)elements.nextElement ();
    new nsISupports (ptrObject.value).Release ();
  }
  unhookedDOMWindows = null;

  elements = functions.elements ();
  while (elements.hasMoreElements ()) {
    BrowserFunction function = ((BrowserFunction)elements.nextElement ());
    AllFunctions.remove (new Integer (function.index));
    function.dispose (false);
  }
  functions = null;

  delegate.onDispose (embedHandle);
  delegate = null;

  embedHandle = 0;
  BrowserCount--;
}

void Activate () {
  int /*long*/[] result = new int /*long*/[1];
  int rc = webBrowser.QueryInterface (nsIWebBrowserFocus.NS_IWEBBROWSERFOCUS_IID, result);
  if (rc != XPCOM.NS_OK) error (rc);
  if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);
  
  nsIWebBrowserFocus webBrowserFocus = new nsIWebBrowserFocus (result[0]);
  rc = webBrowserFocus.Activate ();
  if (rc != XPCOM.NS_OK) error (rc);
  webBrowserFocus.Release ();
}

void Deactivate () {
  int /*long*/[] result = new int /*long*/[1];
  int rc = webBrowser.QueryInterface (nsIWebBrowserFocus.NS_IWEBBROWSERFOCUS_IID, result);
  if (rc != XPCOM.NS_OK) error (rc);
  if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);
  
  nsIWebBrowserFocus webBrowserFocus = new nsIWebBrowserFocus (result[0]);
  rc = webBrowserFocus.Deactivate ();
  if (rc != XPCOM.NS_OK) error (rc);
  webBrowserFocus.Release ();
}

void onResize () {
  Rectangle rect = browser.getClientArea ();
  int width = Math.max (1, rect.width);
  int height = Math.max (1, rect.height);

  int /*long*/[] result = new int /*long*/[1];
  int rc = webBrowser.QueryInterface (nsIBaseWindow.NS_IBASEWINDOW_IID, result);
  if (rc != XPCOM.NS_OK) error (rc);
  if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);

  delegate.setSize (embedHandle, width, height);
  nsIBaseWindow baseWindow = new nsIBaseWindow (result[0]);
  rc = baseWindow.SetPositionAndSize (0, 0, width, height, 1);
  if (rc != XPCOM.NS_OK) error (rc);
  baseWindow.Release ();
}

public void refresh () {
  htmlBytes = null;

  int /*long*/[] result = new int /*long*/[1];
  int rc = webBrowser.QueryInterface (nsIWebNavigation.NS_IWEBNAVIGATION_IID, result);
  if (rc != XPCOM.NS_OK) error(rc);
  if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);
  
  nsIWebNavigation webNavigation = new nsIWebNavigation (result[0]);       
  rc = webNavigation.Reload (nsIWebNavigation.LOAD_FLAGS_NONE);
  webNavigation.Release ();
  if (rc == XPCOM.NS_OK) return;
  /*
  * Feature in Mozilla.  Reload returns an error code NS_ERROR_INVALID_POINTER
  * when it is called immediately after a request to load a new document using
  * LoadURI.  The workaround is to ignore this error code.
  *
  * Feature in Mozilla.  Attempting to reload a file that no longer exists
  * returns an error code of NS_ERROR_FILE_NOT_FOUND.  This is equivalent to
  * attempting to load a non-existent local url, which is not a Browser error,
  * so this error code should be ignored. 
  */
  if (rc != XPCOM.NS_ERROR_INVALID_POINTER && rc != XPCOM.NS_ERROR_FILE_NOT_FOUND) error (rc);
}

void registerFunction (BrowserFunction function) {
  super.registerFunction (function);
  AllFunctions.put (new Integer (function.index), function);
}

public boolean setText (String html) {
  /*
  *  Feature in Mozilla.  The focus memory of Mozilla must be 
  *  properly managed through the nsIWebBrowserFocus interface.
  *  In particular, nsIWebBrowserFocus.deactivate must be called
  *  when the focus moves from the browser (or one of its children
  *  managed by Mozilla to another widget.  We currently do not
  *  get notified when a widget takes focus away from the Browser.
  *  As a result, deactivate is not properly called. This causes
  *  Mozilla to retake focus the next time a document is loaded.
  *  This breaks the case where the HTML loaded in the Browser 
  *  varies while the user enters characters in a text widget. The text
  *  widget loses focus every time new content is loaded.
  *  The current workaround is to call deactivate everytime if 
  *  the browser currently does not have focus. A better workaround
  *  would be to have a way to call deactivate when the Browser
  *  or one of its children loses focus.
  */
  if (browser != browser.getDisplay ().getFocusControl ()) Deactivate ();
  
  /* convert the String containing HTML to an array of bytes with UTF-8 data */
  byte[] data = null;
  try {
    data = html.getBytes ("UTF-8"); //$NON-NLS-1$
  } catch (UnsupportedEncodingException e) {
    return false;
  }

  /*
   * This could be the first content that is set into the browser, so
   * ensure that the custom subclass that works around Mozilla bug
   * https://bugzilla.mozilla.org/show_bug.cgi?id=453523 is removed.
   */
  delegate.removeWindowSubclass ();

  int /*long*/[] result = new int /*long*/[1];
  int rc = webBrowser.QueryInterface (nsIWebBrowserStream.NS_IWEBBROWSERSTREAM_IID, result);
  if (rc == XPCOM.NS_OK && result[0] != 0) {
    /*
    * Setting mozilla's content through nsIWebBrowserStream does not cause a page
    * load to occur, so the events that usually accompany a page change are not
    * fired.  To make this behave as expected, navigate to about:blank first and
    * then set the html content once the page has loaded.
    */
    new nsISupports (result[0]).Release ();
    result[0] = 0;

    /*
    * If htmlBytes is not null then the about:blank page is already being loaded,
    * so no Navigate is required.  Just set the html that is to be shown.
    */
    boolean blankLoading = htmlBytes != null;
    htmlBytes = data;
    if (blankLoading) return true;

    /* navigate to about:blank */
    rc = webBrowser.QueryInterface (nsIWebNavigation.NS_IWEBNAVIGATION_IID, result);
    if (rc != XPCOM.NS_OK) error (rc);
    if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);
    nsIWebNavigation webNavigation = new nsIWebNavigation (result[0]);
    result[0] = 0;
      char[] uri = new char[ABOUT_BLANK.length () + 1];
      ABOUT_BLANK.getChars (0, ABOUT_BLANK.length (), uri, 0);
    rc = webNavigation.LoadURI (uri, nsIWebNavigation.LOAD_FLAGS_NONE, 0, 0, 0);
    if (rc != XPCOM.NS_OK) error (rc);
    webNavigation.Release ();
  } else {
    byte[] contentCharsetBuffer = MozillaDelegate.wcsToMbcs (null, "UTF-8", true);  //$NON-NLS-1$
    int /*long*/ aContentCharset = XPCOM.nsEmbedCString_new (contentCharsetBuffer, contentCharsetBuffer.length);
    byte[] contentTypeBuffer = MozillaDelegate.wcsToMbcs (null, "text/html", true); // $NON-NLS-1$
    int /*long*/ aContentType = XPCOM.nsEmbedCString_new (contentTypeBuffer, contentTypeBuffer.length);

    rc = XPCOM.NS_GetServiceManager (result);
    if (rc != XPCOM.NS_OK) error (rc);
    if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

    nsIServiceManager serviceManager = new nsIServiceManager (result[0]);
    result[0] = 0;
    rc = serviceManager.GetService (XPCOM.NS_IOSERVICE_CID, nsIIOService.NS_IIOSERVICE_IID, result);
    if (rc != XPCOM.NS_OK) error (rc);
    if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
    serviceManager.Release ();

    nsIIOService ioService = new nsIIOService (result[0]);
    result[0] = 0;
    /*
    * Note.  Mozilla ignores LINK tags used to load CSS stylesheets
    * when the URI protocol for the nsInputStreamChannel
    * is about:blank.  The fix is to specify the file protocol.
    */
    byte[] aString = MozillaDelegate.wcsToMbcs (null, URI_FROMMEMORY, false);
    int /*long*/ aSpec = XPCOM.nsEmbedCString_new (aString, aString.length);
    rc = ioService.NewURI (aSpec, null, 0, result);
    if (rc != XPCOM.NS_OK) error (rc);
    if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
    XPCOM.nsEmbedCString_delete (aSpec);
    ioService.Release ();

    nsIURI uri = new nsIURI (result[0]);
    result[0] = 0;

    rc = webBrowser.QueryInterface (nsIInterfaceRequestor.NS_IINTERFACEREQUESTOR_IID, result);
    if (rc != XPCOM.NS_OK) error (rc);
    if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);
    nsIInterfaceRequestor interfaceRequestor = new nsIInterfaceRequestor (result[0]);
    result[0] = 0;

    /*
    * Feature in Mozilla. LoadStream invokes the nsIInputStream argument
    * through a different thread.  The callback mechanism must attach 
    * a non java thread to the JVM otherwise the nsIInputStream Read and
    * Close methods never get called.
    */
    InputStream inputStream = new InputStream (data);
    inputStream.AddRef ();

    rc = interfaceRequestor.GetInterface (nsIDocShell.NS_IDOCSHELL_IID, result);
    if (rc != XPCOM.NS_OK) error (rc);
    if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);
    nsIDocShell docShell = new nsIDocShell (result[0]);
    result[0] = 0;
    rc = docShell.LoadStream (inputStream.getAddress (), uri.getAddress (), aContentType,  aContentCharset, 0);
    docShell.Release ();

    inputStream.Release ();
    interfaceRequestor.Release ();
    uri.Release ();
    XPCOM.nsEmbedCString_delete (aContentType);
    XPCOM.nsEmbedCString_delete (aContentCharset);
  }
  return true;
}

public boolean setUrl (String url) {
  htmlBytes = null;

  int /*long*/[] result = new int /*long*/[1];
  int rc = webBrowser.QueryInterface (nsIWebNavigation.NS_IWEBNAVIGATION_IID, result);
  if (rc != XPCOM.NS_OK) error (rc);
  if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);

  /*
   * This could be the first content that is set into the browser, so
   * ensure that the custom subclass that works around Mozilla bug
   * https://bugzilla.mozilla.org/show_bug.cgi?id=453523 is removed.
   */
  delegate.removeWindowSubclass ();

  nsIWebNavigation webNavigation = new nsIWebNavigation (result[0]);
    char[] uri = new char[url.length () + 1];
    url.getChars (0, url.length (), uri, 0);
  rc = webNavigation.LoadURI (uri, nsIWebNavigation.LOAD_FLAGS_NONE, 0, 0, 0);
  webNavigation.Release ();
  return rc == XPCOM.NS_OK;
}

public void stop () {
  htmlBytes = null;

  int /*long*/[] result = new int /*long*/[1];
  int rc = webBrowser.QueryInterface (nsIWebNavigation.NS_IWEBNAVIGATION_IID, result);
  if (rc != XPCOM.NS_OK) error (rc);
  if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);
  
  nsIWebNavigation webNavigation = new nsIWebNavigation (result[0]);     
  rc = webNavigation.Stop (nsIWebNavigation.STOP_ALL);
  if (rc != XPCOM.NS_OK) error (rc);
  webNavigation.Release ();
}

void hookDOMListeners (nsIDOMEventTarget target, boolean isTop) {
  nsEmbedString string = new nsEmbedString (XPCOM.DOMEVENT_FOCUS);
  target.AddEventListener (string.getAddress (), domEventListener.getAddress (), 0);
  string.dispose ();
  string = new nsEmbedString (XPCOM.DOMEVENT_UNLOAD);
  target.AddEventListener (string.getAddress (), domEventListener.getAddress (), 0);
  string.dispose ();
  string = new nsEmbedString (XPCOM.DOMEVENT_MOUSEDOWN);
  target.AddEventListener (string.getAddress (), domEventListener.getAddress (), 0);
  string.dispose ();
  string = new nsEmbedString (XPCOM.DOMEVENT_MOUSEUP);
  target.AddEventListener (string.getAddress (), domEventListener.getAddress (), 0);
  string.dispose ();
  string = new nsEmbedString (XPCOM.DOMEVENT_MOUSEMOVE);
  target.AddEventListener (string.getAddress (), domEventListener.getAddress (), 0);
  string.dispose ();
  string = new nsEmbedString (XPCOM.DOMEVENT_MOUSEWHEEL);
  target.AddEventListener (string.getAddress (), domEventListener.getAddress (), 0);
  string.dispose ();
  string = new nsEmbedString (XPCOM.DOMEVENT_MOUSEDRAG);
  target.AddEventListener (string.getAddress (), domEventListener.getAddress (), 0);
  string.dispose ();

  /*
  * Only hook mouseover and mouseout if the target is a top-level frame, so that mouse moves
  * between frames will not generate events.
  */
  if (isTop && delegate.hookEnterExit ()) {
    string = new nsEmbedString (XPCOM.DOMEVENT_MOUSEOVER);
    target.AddEventListener (string.getAddress (), domEventListener.getAddress (), 0);
    string.dispose ();
    string = new nsEmbedString (XPCOM.DOMEVENT_MOUSEOUT);
    target.AddEventListener (string.getAddress (), domEventListener.getAddress (), 0);
    string.dispose ();
  }

  string = new nsEmbedString (XPCOM.DOMEVENT_KEYDOWN);
  target.AddEventListener (string.getAddress (), domEventListener.getAddress (), 0);
  string.dispose ();
  string = new nsEmbedString (XPCOM.DOMEVENT_KEYPRESS);
  target.AddEventListener (string.getAddress (), domEventListener.getAddress (), 0);
  string.dispose ();
  string = new nsEmbedString (XPCOM.DOMEVENT_KEYUP);
  target.AddEventListener (string.getAddress (), domEventListener.getAddress (), 0);
  string.dispose ();
}

void unhookDOMListeners () {
  int /*long*/[] result = new int /*long*/[1];
  int rc = webBrowser.GetContentDOMWindow (result);
  if (rc != XPCOM.NS_OK) error (rc);
  if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);

  nsIDOMWindow window = new nsIDOMWindow (result[0]);
  result[0] = 0;
  rc = window.QueryInterface (nsIDOMEventTarget.NS_IDOMEVENTTARGET_IID, result);
  if (rc != XPCOM.NS_OK) error (rc);
  if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);

  nsIDOMEventTarget target = new nsIDOMEventTarget (result[0]);
  result[0] = 0;
  unhookDOMListeners (target);
  target.Release ();

  /* Listeners must be unhooked in pages contained in frames */
  rc = window.GetFrames (result);
  if (rc != XPCOM.NS_OK) error (rc);
  if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);
  nsIDOMWindowCollection frames = new nsIDOMWindowCollection (result[0]);
  result[0] = 0;
  int[] frameCount = new int[1];
  rc = frames.GetLength (frameCount); /* PRUint32 */
  if (rc != XPCOM.NS_OK) error (rc);
  int count = frameCount[0];

  if (count > 0) {
    for (int i = 0; i < count; i++) {
      rc = frames.Item (i, result);
      if (rc != XPCOM.NS_OK) error (rc);
      if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);

      nsIDOMWindow frame = new nsIDOMWindow (result[0]);
      result[0] = 0;
      rc = frame.QueryInterface (nsIDOMEventTarget.NS_IDOMEVENTTARGET_IID, result);
      if (rc != XPCOM.NS_OK) error (rc);
      if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);

      target = new nsIDOMEventTarget (result[0]);
      result[0] = 0;
      unhookDOMListeners (target);
      target.Release ();
      frame.Release ();
    }
  }
  frames.Release ();
  window.Release ();
}

void unhookDOMListeners (nsIDOMEventTarget target) {
  nsEmbedString string = new nsEmbedString (XPCOM.DOMEVENT_FOCUS);
  target.RemoveEventListener (string.getAddress (), domEventListener.getAddress (), 0);
  string.dispose ();
  string = new nsEmbedString (XPCOM.DOMEVENT_UNLOAD);
  target.RemoveEventListener (string.getAddress (), domEventListener.getAddress (), 0);
  string.dispose ();
  string = new nsEmbedString (XPCOM.DOMEVENT_MOUSEDOWN);
  target.RemoveEventListener (string.getAddress (), domEventListener.getAddress (), 0);
  string.dispose ();
  string = new nsEmbedString (XPCOM.DOMEVENT_MOUSEUP);
  target.RemoveEventListener (string.getAddress (), domEventListener.getAddress (), 0);
  string.dispose ();
  string = new nsEmbedString (XPCOM.DOMEVENT_MOUSEMOVE);
  target.RemoveEventListener (string.getAddress (), domEventListener.getAddress (), 0);
  string.dispose ();
  string = new nsEmbedString (XPCOM.DOMEVENT_MOUSEWHEEL);
  target.RemoveEventListener (string.getAddress (), domEventListener.getAddress (), 0);
  string.dispose ();
  string = new nsEmbedString (XPCOM.DOMEVENT_MOUSEDRAG);
  target.RemoveEventListener (string.getAddress (), domEventListener.getAddress (), 0);
  string.dispose ();
  string = new nsEmbedString (XPCOM.DOMEVENT_MOUSEOVER);
  target.RemoveEventListener (string.getAddress (), domEventListener.getAddress (), 0);
  string.dispose ();
  string = new nsEmbedString (XPCOM.DOMEVENT_MOUSEOUT);
  target.RemoveEventListener (string.getAddress (), domEventListener.getAddress (), 0);
  string.dispose ();
  string = new nsEmbedString (XPCOM.DOMEVENT_KEYDOWN);
  target.RemoveEventListener (string.getAddress (), domEventListener.getAddress (), 0);
  string.dispose ();
  string = new nsEmbedString (XPCOM.DOMEVENT_KEYPRESS);
  target.RemoveEventListener (string.getAddress (), domEventListener.getAddress (), 0);
  string.dispose ();
  string = new nsEmbedString (XPCOM.DOMEVENT_KEYUP);
  target.RemoveEventListener (string.getAddress (), domEventListener.getAddress (), 0);
  string.dispose ();
}

/* nsISupports */

int QueryInterface (int /*long*/ riid, int /*long*/ ppvObject) {
  if (riid == 0 || ppvObject == 0) return XPCOM.NS_ERROR_NO_INTERFACE;

  nsID guid = new nsID ();
  XPCOM.memmove (guid, riid, nsID.sizeof);

  if (guid.Equals (nsISupports.NS_ISUPPORTS_IID)) {
    XPCOM.memmove (ppvObject, new int /*long*/[] {supports.getAddress ()}, C.PTR_SIZEOF);
    AddRef ();
    return XPCOM.NS_OK;
  }
  if (guid.Equals (nsIWeakReference.NS_IWEAKREFERENCE_IID)) {
    XPCOM.memmove (ppvObject, new int /*long*/[] {weakReference.getAddress ()}, C.PTR_SIZEOF);
    AddRef ();
    return XPCOM.NS_OK;
  }
  if (guid.Equals (nsIWebProgressListener.NS_IWEBPROGRESSLISTENER_IID)) {
    XPCOM.memmove (ppvObject, new int /*long*/[] {webProgressListener.getAddress ()}, C.PTR_SIZEOF);
    AddRef ();
    return XPCOM.NS_OK;
  }
  if (guid.Equals (nsIWebBrowserChrome.NS_IWEBBROWSERCHROME_IID)) {
    XPCOM.memmove (ppvObject, new int /*long*/[] {webBrowserChrome.getAddress ()}, C.PTR_SIZEOF);
    AddRef ();
    return XPCOM.NS_OK;
  }
  if (guid.Equals (nsIWebBrowserChromeFocus.NS_IWEBBROWSERCHROMEFOCUS_IID)) {
    XPCOM.memmove (ppvObject, new int /*long*/[] {webBrowserChromeFocus.getAddress ()}, C.PTR_SIZEOF);
    AddRef ();
    return XPCOM.NS_OK;
  }
  if (guid.Equals (nsIEmbeddingSiteWindow.NS_IEMBEDDINGSITEWINDOW_IID)) {
    XPCOM.memmove (ppvObject, new int /*long*/[] {embeddingSiteWindow.getAddress ()}, C.PTR_SIZEOF);
    AddRef ();
    return XPCOM.NS_OK;
  }
  if (guid.Equals (nsIInterfaceRequestor.NS_IINTERFACEREQUESTOR_IID)) {
    XPCOM.memmove (ppvObject, new int /*long*/[] {interfaceRequestor.getAddress ()}, C.PTR_SIZEOF);
    AddRef ();
    return XPCOM.NS_OK;
  }
  if (guid.Equals (nsISupportsWeakReference.NS_ISUPPORTSWEAKREFERENCE_IID)) {
    XPCOM.memmove (ppvObject, new int /*long*/[] {supportsWeakReference.getAddress ()}, C.PTR_SIZEOF);
    AddRef ();
    return XPCOM.NS_OK;
  }
  if (guid.Equals (nsIContextMenuListener.NS_ICONTEXTMENULISTENER_IID)) {
    XPCOM.memmove (ppvObject, new int /*long*/[] {contextMenuListener.getAddress ()}, C.PTR_SIZEOF);
    AddRef ();
    return XPCOM.NS_OK;
  }
  if (guid.Equals (nsIURIContentListener.NS_IURICONTENTLISTENER_IID)) {
    XPCOM.memmove (ppvObject, new int /*long*/[] {uriContentListener.getAddress ()}, C.PTR_SIZEOF);
    AddRef ();
    return XPCOM.NS_OK;
  }
  if (guid.Equals (nsITooltipListener.NS_ITOOLTIPLISTENER_IID)) {
    XPCOM.memmove (ppvObject, new int /*long*/[] {tooltipListener.getAddress ()}, C.PTR_SIZEOF);
    AddRef ();
    return XPCOM.NS_OK;
  }
  if (guid.Equals (nsIBadCertListener2.NS_IBADCERTLISTENER2_IID)) {
    XPCOM.memmove (ppvObject, new int /*long*/[] {badCertListener.getAddress ()}, C.PTR_SIZEOF);
    AddRef ();
    return XPCOM.NS_OK;
  }
  XPCOM.memmove (ppvObject, new int /*long*/[] {0}, C.PTR_SIZEOF);
  return XPCOM.NS_ERROR_NO_INTERFACE;
}

int AddRef () {
  refCount++;
  return refCount;
}

int Release () {
  refCount--;
  if (refCount == 0) disposeCOMInterfaces ();
  return refCount;
}

/* nsIWeakReference */  
  
int QueryReferent (int /*long*/ riid, int /*long*/ ppvObject) {
  return QueryInterface (riid, ppvObject);
}

/* nsIInterfaceRequestor */

int GetInterface (int /*long*/ riid, int /*long*/ ppvObject) {
  if (riid == 0 || ppvObject == 0) return XPCOM.NS_ERROR_NO_INTERFACE;
  nsID guid = new nsID ();
  XPCOM.memmove (guid, riid, nsID.sizeof);
  if (guid.Equals (nsIDOMWindow.NS_IDOMWINDOW_IID)) {
    int /*long*/[] aContentDOMWindow = new int /*long*/[1];
    int rc = webBrowser.GetContentDOMWindow (aContentDOMWindow);
    if (rc != XPCOM.NS_OK) error (rc);
    if (aContentDOMWindow[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);
    XPCOM.memmove (ppvObject, aContentDOMWindow, C.PTR_SIZEOF);
    return rc;
  }
  return QueryInterface (riid, ppvObject);
}

int GetWeakReference (int /*long*/ ppvObject) {
  XPCOM.memmove (ppvObject, new int /*long*/[] {weakReference.getAddress ()}, C.PTR_SIZEOF);
  AddRef ();
  return XPCOM.NS_OK;
}

/* nsIWebProgressListener */

int OnStateChange (int /*long*/ aWebProgress, int /*long*/ aRequest, int aStateFlags, int aStatus) {
  if ((aStateFlags & nsIWebProgressListener.STATE_IS_DOCUMENT) == 0) return XPCOM.NS_OK;
  if ((aStateFlags & nsIWebProgressListener.STATE_START) != 0) {
    int /*long*/[] result = new int /*long*/[1];

    /*
    * When navigating to a site that is known to have a bad certificate, request notification
    * callbacks on the channel so that our nsIBadCertListener2 will be invoked.
    */
    if (isRetrievingBadCert) {
      isRetrievingBadCert = false;
      nsIRequest request = new nsIRequest (aRequest);
      int rc = request.QueryInterface (nsIChannel.NS_ICHANNEL_IID, result);
      if (rc != XPCOM.NS_OK) error (rc);
      if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

      nsIChannel channel = new nsIChannel (result[0]);
      result[0] = 0;
      rc = channel.SetNotificationCallbacks (interfaceRequestor.getAddress ());
      if (rc != XPCOM.NS_OK) error (rc);
      channel.Release ();
      return XPCOM.NS_OK;
    }

    if (request == 0) request = aRequest;
    /*
     * Add the page's nsIDOMWindow to the collection of windows that will
     * have DOM listeners added to them later on in the page loading
     * process.  These listeners cannot be added yet because the
     * nsIDOMWindow is not ready to take them at this stage.
     */
    nsIWebProgress progress = new nsIWebProgress (aWebProgress);
    int rc = progress.GetDOMWindow (result);
    if (rc != XPCOM.NS_OK) error (rc);
    if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
    unhookedDOMWindows.addElement (new LONG (result[0]));
  } else if ((aStateFlags & nsIWebProgressListener.STATE_REDIRECTING) != 0) {
    if (request == aRequest) request = 0;
  } else if ((aStateFlags & nsIWebProgressListener.STATE_STOP) != 0) {
    /*
    * If this page's nsIDOMWindow handle is still in unhookedDOMWindows then
    * add its DOM listeners now.  It's possible for this to happen since
    * there is no guarantee that a STATE_TRANSFERRING state change will be
    * received for every window in a page, which is when these listeners
    * are typically added.
    */
    int /*long*/[] result = new int /*long*/[1];
    nsIWebProgress progress = new nsIWebProgress (aWebProgress);
    int rc = progress.GetDOMWindow (result);
    if (rc != XPCOM.NS_OK) error (rc);
    if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
    nsIDOMWindow domWindow = new nsIDOMWindow (result[0]);

    LONG ptrObject = new LONG (result[0]);
    result[0] = 0;
    int index = unhookedDOMWindows.indexOf (ptrObject);
    if (index != -1) {
      rc = webBrowser.GetContentDOMWindow (result);
      if (rc != XPCOM.NS_OK) error (rc);
      if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);
      boolean isTop = result[0] == domWindow.getAddress ();
      new nsISupports (result[0]).Release ();
      result[0] = 0;

      rc = domWindow.QueryInterface (nsIDOMEventTarget.NS_IDOMEVENTTARGET_IID, result);
      if (rc != XPCOM.NS_OK) error (rc);
      if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);

      nsIDOMEventTarget target = new nsIDOMEventTarget (result[0]);
      result[0] = 0;
      hookDOMListeners (target, isTop);
      target.Release ();

      /*
      * Remove and unreference the nsIDOMWindow from the collection of windows
      * that are waiting to have DOM listeners hooked on them. 
      */
      unhookedDOMWindows.remove (ptrObject);
      new nsISupports (ptrObject.value).Release ();
    }

    boolean deferCompleted = false;
    /*
     * If htmlBytes 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 (htmlBytes != null) {
      nsIRequest req = new nsIRequest (aRequest);
      int /*long*/ name = XPCOM.nsEmbedCString_new ();
      rc = req.GetName (name);
      if (rc != XPCOM.NS_OK) error (rc);
      int length = XPCOM.nsEmbedCString_Length (name);
      int /*long*/ buffer = XPCOM.nsEmbedCString_get (name);
      byte[] dest = new byte[length];
      XPCOM.memmove (dest, buffer, length);
      String url = new String (dest);
      XPCOM.nsEmbedCString_delete (name);

      if (url.startsWith (ABOUT_BLANK)) {
        /*
         * Setting mozilla's content with nsIWebBrowserStream invalidates the 
         * DOM listeners that were hooked on it (about:blank), so remove them and
         * add new ones after the content has been set.
         */
        unhookDOMListeners ();

        rc = XPCOM.NS_GetServiceManager (result);
        if (rc != XPCOM.NS_OK) error (rc);
        if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

        nsIServiceManager serviceManager = new nsIServiceManager (result[0]);
        result[0] = 0;
        rc = serviceManager.GetService (XPCOM.NS_IOSERVICE_CID, nsIIOService.NS_IIOSERVICE_IID, result);
        if (rc != XPCOM.NS_OK) error (rc);
        if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
        serviceManager.Release ();

        nsIIOService ioService = new nsIIOService (result[0]);
        result[0] = 0;
        /*
        * Note.  Mozilla ignores LINK tags used to load CSS stylesheets
        * when the URI protocol for the nsInputStreamChannel
        * is about:blank.  The fix is to specify the file protocol.
        */
        byte[] aString = MozillaDelegate.wcsToMbcs (null, URI_FROMMEMORY, false);
        int /*long*/ aSpec = XPCOM.nsEmbedCString_new (aString, aString.length);
        rc = ioService.NewURI (aSpec, null, 0, result);
        if (rc != XPCOM.NS_OK) error (rc);
        if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
        XPCOM.nsEmbedCString_delete (aSpec);
        ioService.Release ();

        nsIURI uri = new nsIURI (result[0]);
        result[0] = 0;

        rc = webBrowser.QueryInterface (nsIWebBrowserStream.NS_IWEBBROWSERSTREAM_IID, result);
        if (rc != XPCOM.NS_OK) error (rc);
        if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

        nsIWebBrowserStream stream = new nsIWebBrowserStream (result[0]);
        result[0] = 0;

        byte[] contentTypeBuffer = MozillaDelegate.wcsToMbcs (null, "text/html", true); // $NON-NLS-1$
        int /*long*/ aContentType = XPCOM.nsEmbedCString_new (contentTypeBuffer, contentTypeBuffer.length);

        rc = stream.OpenStream (uri.getAddress (), aContentType);
        if (rc != XPCOM.NS_OK) error (rc);
        int /*long*/ ptr = C.malloc (htmlBytes.length);
        XPCOM.memmove (ptr, htmlBytes, htmlBytes.length);
        int pageSize = 8192;
        int pageCount = htmlBytes.length / pageSize + 1;
        int /*long*/ current = ptr;
        for (int i = 0; i < pageCount; i++) {
          length = i == pageCount - 1 ? htmlBytes.length % pageSize : pageSize;
          if (length > 0) {
            rc = stream.AppendToStream (current, length);
            if (rc != XPCOM.NS_OK) error (rc);
          }
          current += pageSize;
        }
        rc = stream.CloseStream ();
        if (rc != XPCOM.NS_OK) error (rc);
        C.free (ptr);
        XPCOM.nsEmbedCString_delete (aContentType);
        stream.Release ();
        uri.Release ();
        htmlBytes = null;
        /*
        * Browser content that is set via nsIWebBrowserStream is not parsed immediately.
        * Since clients depend on the Completed event to know when the browser's content
        * is available, delay the sending of this event so that the stream content will
        * be parsed first.
        */
        deferCompleted = true;

        rc = webBrowser.GetContentDOMWindow (result);
        if (rc != XPCOM.NS_OK) error (rc);
        if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);
        boolean isTop = result[0] == domWindow.getAddress ();
        new nsISupports (result[0]).Release ();
        result[0] = 0;

        rc = domWindow.QueryInterface (nsIDOMEventTarget.NS_IDOMEVENTTARGET_IID, result);
        if (rc != XPCOM.NS_OK) error (rc);
        if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);
        nsIDOMEventTarget target = new nsIDOMEventTarget (result[0]);
        result[0] = 0;
        hookDOMListeners (target, isTop);
        target.Release ();
      }
    }
    domWindow.Release ();

    /*
    * Feature in Mozilla.  When a request is redirected (STATE_REDIRECTING),
    * it never reaches the state STATE_STOP and it is replaced with a new request.
    * The new request is received when it is in the state STATE_STOP.
    * To handle this case,  the variable request is set to 0 when the corresponding
    * request is redirected. The following request received with the state STATE_STOP
    * - the new request resulting from the redirection - is used to send
    * the ProgressListener.completed event.
    */
    if (request == aRequest || request == 0) {
      request = 0;
      StatusTextEvent event = new StatusTextEvent (browser);
      event.display = browser.getDisplay ();
      event.widget = browser;
      event.text = ""; //$NON-NLS-1$
      for (int i = 0; i < statusTextListeners.length; i++) {
        statusTextListeners[i].changed (event);
      }

      /* re-install registered functions */
      Enumeration elements = functions.elements ();
      while (elements.hasMoreElements ()) {
        BrowserFunction function = (BrowserFunction)elements.nextElement ();
        execute (function.functionString);
      }

      final Display display = browser.getDisplay ();
      final ProgressEvent event2 = new ProgressEvent (browser);
      event2.display = display;
      event2.widget = browser;
      Runnable runnable = new Runnable () {
        public void run () {
          if (browser.isDisposed ()) return;
          for (int i = 0; i < progressListeners.length; i++) {
            progressListeners[i].completed (event2);
          }
        }
      };
      if (deferCompleted) {
        display.asyncExec (runnable);
      } else {
        display.syncExec (runnable);
      }
    }
  } else if ((aStateFlags & nsIWebProgressListener.STATE_TRANSFERRING) != 0) {
    /*
    * Hook DOM listeners to the page's nsIDOMWindow here because this is
    * the earliest opportunity to do so.    
    */
    int /*long*/[] result = new int /*long*/[1];
    nsIWebProgress progress = new nsIWebProgress (aWebProgress);
    int rc = progress.GetDOMWindow (result);
    if (rc != XPCOM.NS_OK) error (rc);
    if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
    nsIDOMWindow domWindow = new nsIDOMWindow (result[0]);

    LONG ptrObject = new LONG (result[0]);
    result[0] = 0;
    int index = unhookedDOMWindows.indexOf (ptrObject);
    if (index != -1) {
      rc = webBrowser.GetContentDOMWindow (result);
      if (rc != XPCOM.NS_OK) error (rc);
      if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);
      boolean isTop = result[0] == domWindow.getAddress ();
      new nsISupports (result[0]).Release ();
      result[0] = 0;

      rc = domWindow.QueryInterface (nsIDOMEventTarget.NS_IDOMEVENTTARGET_IID, result);
      if (rc != XPCOM.NS_OK) error (rc);
      if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);

      nsIDOMEventTarget target = new nsIDOMEventTarget (result[0]);
      result[0] = 0;
      hookDOMListeners (target, isTop);
      target.Release ();

      /*
      * Remove and unreference the nsIDOMWindow from the collection of windows
      * that are waiting to have DOM listeners hooked on them. 
      */
      unhookedDOMWindows.remove (ptrObject);
      new nsISupports (ptrObject.value).Release ();
    }
    domWindow.Release ();
  }
  return XPCOM.NS_OK;
}

int OnProgressChange (int /*long*/ aWebProgress, int /*long*/ aRequest, int aCurSelfProgress, int aMaxSelfProgress, int aCurTotalProgress, int aMaxTotalProgress) {
  if (progressListeners.length == 0) return XPCOM.NS_OK;
  ProgressEvent event = new ProgressEvent (browser);
  event.display = browser.getDisplay ();
  event.widget = browser;
  event.current = aCurTotalProgress;
  event.total = aMaxTotalProgress;
  for (int i = 0; i < progressListeners.length; i++) {
    progressListeners[i].changed (event);
  }
  return XPCOM.NS_OK;
}

int OnLocationChange (int /*long*/ aWebProgress, int /*long*/ aRequest, int /*long*/ aLocation) {
  /*
  * Feature in Mozilla.  When a page is loaded via setText before a previous
  * setText page load has completed, the expected OnStateChange STATE_STOP for the
  * original setText never arrives because it gets replaced by the OnStateChange
  * STATE_STOP for the new request.  This results in the request field never being
  * cleared because the original request's OnStateChange STATE_STOP is still expected
  * (but never arrives).  To handle this case, the request field is updated to the new
  * overriding request since its OnStateChange STATE_STOP will be received next.
  */
  if (request != 0 && request != aRequest) request = aRequest;

  if (locationListeners.length == 0) return XPCOM.NS_OK;

  nsIWebProgress webProgress = new nsIWebProgress (aWebProgress);
  int /*long*/[] aDOMWindow = new int /*long*/[1];
  int rc = webProgress.GetDOMWindow (aDOMWindow);
  if (rc != XPCOM.NS_OK) error (rc);
  if (aDOMWindow[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);
  
  nsIDOMWindow domWindow = new nsIDOMWindow (aDOMWindow[0]);
  int /*long*/[] aTop = new int /*long*/[1];
  rc = domWindow.GetTop (aTop);
  if (rc != XPCOM.NS_OK) error (rc);
  if (aTop[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);
  domWindow.Release ();
  
  nsIDOMWindow topWindow = new nsIDOMWindow (aTop[0]);
  topWindow.Release ();
  
  nsIURI location = new nsIURI (aLocation);
  int /*long*/ aSpec = XPCOM.nsEmbedCString_new ();
  location.GetSpec (aSpec);
  int length = XPCOM.nsEmbedCString_Length (aSpec);
  int /*long*/ buffer = XPCOM.nsEmbedCString_get (aSpec);
  byte[] dest = new byte[length];
  XPCOM.memmove (dest, buffer, length);
  XPCOM.nsEmbedCString_delete (aSpec);
  String url = new String (dest);

  /*
   * As of Mozilla 1.8, the first time that a page is displayed, regardless of
   * whether it's via Browser.setURL() or Browser.setText(), the GRE navigates
   * to about:blank and fires the corresponding navigation events.  Do not send
   * this event on to the user since it is not expected.
   */
  if (!IsPre_1_8 && aRequest == 0 && url.startsWith (ABOUT_BLANK)) return XPCOM.NS_OK;

  LocationEvent event = new LocationEvent (browser);
  event.display = browser.getDisplay ();
  event.widget = browser;
  event.location = url;
  /*
   * 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 (event.location.equals (URI_FROMMEMORY)) event.location = ABOUT_BLANK;
  event.top = aTop[0] == aDOMWindow[0];
  for (int i = 0; i < locationListeners.length; i++) {
    locationListeners[i].changed (event);
  }
  return XPCOM.NS_OK;
}

int OnStatusChange (int /*long*/ aWebProgress, int /*long*/ aRequest, int aStatus, int /*long*/ aMessage) {
  if (statusTextListeners.length == 0) return XPCOM.NS_OK;
  StatusTextEvent event = new StatusTextEvent (browser);
  event.display = browser.getDisplay ();
  event.widget = browser;
  int length = XPCOM.strlen_PRUnichar (aMessage);
  char[] dest = new char[length];
  XPCOM.memmove (dest, aMessage, length * 2);
  event.text = new String (dest);
  for (int i = 0; i < statusTextListeners.length; i++) {
    statusTextListeners[i].changed (event);
  }
  return XPCOM.NS_OK;
}    

int OnSecurityChange (int /*long*/ aWebProgress, int /*long*/ aRequest, int state) {
  return XPCOM.NS_OK;
}

/* nsIWebBrowserChrome */

int SetStatus (int statusType, int /*long*/ status) {
  if (statusTextListeners.length == 0) return XPCOM.NS_OK;
  StatusTextEvent event = new StatusTextEvent (browser);
  event.display = browser.getDisplay ();
  event.widget = browser;
  int length = XPCOM.strlen_PRUnichar (status);
  char[] dest = new char[length];
  XPCOM.memmove (dest, status, length * 2);
  String string = new String (dest);
  event.text = string;
  for (int i = 0; i < statusTextListeners.length; i++) {
    statusTextListeners[i].changed (event);
  }
  return XPCOM.NS_OK;
}

int GetWebBrowser (int /*long*/ aWebBrowser) {
  int /*long*/[] ret = new int /*long*/[1];  
  if (webBrowser != null) {
    webBrowser.AddRef ();
    ret[0] = webBrowser.getAddress ();  
  }
  XPCOM.memmove (aWebBrowser, ret, C.PTR_SIZEOF);
  return XPCOM.NS_OK;
}

int SetWebBrowser (int /*long*/ aWebBrowser) {
  if (webBrowser != null) webBrowser.Release ();
  webBrowser = aWebBrowser != 0 ? new nsIWebBrowser (aWebBrowser) : null;          
  return XPCOM.NS_OK;
}
   
int GetChromeFlags (int /*long*/ aChromeFlags) {
  int[] ret = new int[1];
  ret[0] = chromeFlags;
  XPCOM.memmove (aChromeFlags, ret, 4); /* PRUint32 */
  return XPCOM.NS_OK;
}

int SetChromeFlags (int aChromeFlags) {
  chromeFlags = aChromeFlags;
  return XPCOM.NS_OK;
}

int DestroyBrowserWindow () {
  WindowEvent newEvent = new WindowEvent (browser);
  newEvent.display = browser.getDisplay ();
  newEvent.widget = browser;
  for (int i = 0; i < closeWindowListeners.length; i++) {
    closeWindowListeners[i].close (newEvent);
  }
  /*
  * Note on Mozilla.  The DestroyBrowserWindow notification cannot be cancelled.
  * The browser widget cannot be used after this notification has been received.
  * The application is advised to close the window hosting the browser widget.
  * The browser widget must be disposed in all cases.
  */
  browser.dispose ();
  return XPCOM.NS_OK;
}
     
int SizeBrowserTo (int aCX, int aCY) {
  size = new Point (aCX, aCY);
  boolean isChrome = (chromeFlags & nsIWebBrowserChrome.CHROME_OPENAS_CHROME) != 0;
  if (isChrome) {
    Shell shell = browser.getShell ();
    shell.setSize (shell.computeSize (size.x, size.y));
  }
  return XPCOM.NS_OK;
}

int ShowAsModal () {
  int /*long*/[] result = new int /*long*/[1];
  int rc = XPCOM.NS_GetServiceManager (result);
  if (rc != XPCOM.NS_OK) error (rc);
  if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

  nsIServiceManager serviceManager = new nsIServiceManager (result[0]);
  result[0] = 0;
  byte[] aContractID = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_CONTEXTSTACK_CONTRACTID, true);
  rc = serviceManager.GetServiceByContractID (aContractID, nsIJSContextStack.NS_IJSCONTEXTSTACK_IID, result);
  if (rc != XPCOM.NS_OK) error (rc);
  if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
  serviceManager.Release ();

  nsIJSContextStack stack = new nsIJSContextStack (result[0]);
  result[0] = 0;
  rc = stack.Push (0);
  if (rc != XPCOM.NS_OK) error (rc);

  Shell shell = browser.getShell ();
  Display display = browser.getDisplay ();
  while (!shell.isDisposed ()) {
    if (!display.readAndDispatch ()) display.sleep ();
  }

  rc = stack.Pop (result);
  if (rc != XPCOM.NS_OK) error (rc);
  stack.Release ();
  return XPCOM.NS_OK;
}

int IsWindowModal (int /*long*/ retval) {
  int result = (chromeFlags & nsIWebBrowserChrome.CHROME_MODAL) != 0 ? 1 : 0;
  XPCOM.memmove (retval, new int[] {result}, 4); /* PRBool */
  return XPCOM.NS_OK;
}
   
int ExitModalEventLoop (int aStatus) {
  return XPCOM.NS_OK;
}

/* nsIEmbeddingSiteWindow */ 

int SetDimensions (int flags, int x, int y, int cx, int cy) {
  if ((flags & nsIEmbeddingSiteWindow.DIM_FLAGS_POSITION) != 0) {
    location = new Point (x, y);
    browser.getShell ().setLocation (x, y);
  }
  if ((flags & nsIEmbeddingSiteWindow.DIM_FLAGS_SIZE_INNER) != 0) {
    browser.setSize (cx, cy);
  }
  if ((flags & nsIEmbeddingSiteWindow.DIM_FLAGS_SIZE_OUTER) != 0) {
    browser.getShell ().setSize (cx, cy);
  }
  return XPCOM.NS_OK;
}

int GetDimensions (int flags, int /*long*/ x, int /*long*/ y, int /*long*/ cx, int /*long*/ cy) {
  if ((flags & nsIEmbeddingSiteWindow.DIM_FLAGS_POSITION) != 0) {
    Point location = browser.getShell ().getLocation ();
    if (x != 0) C.memmove (x, new int[] {location.x}, 4); /* PRInt32 */
    if (y != 0) C.memmove (y, new int[] {location.y}, 4); /* PRInt32 */
  }
  if ((flags & nsIEmbeddingSiteWindow.DIM_FLAGS_SIZE_INNER) != 0) {
    Point size = browser.getSize ();
    if (cx != 0) C.memmove (cx, new int[] {size.x}, 4); /* PRInt32 */
    if (cy != 0) C.memmove (cy, new int[] {size.y}, 4); /* PRInt32 */
  }
  if ((flags & nsIEmbeddingSiteWindow.DIM_FLAGS_SIZE_OUTER) != 0) {
    Point size = browser.getShell().getSize ();
    if (cx != 0) C.memmove (cx, new int[] {size.x}, 4); /* PRInt32 */
    if (cy != 0) C.memmove (cy, new int[] {size.y}, 4); /* PRInt32 */
  }
  return XPCOM.NS_OK;
}

int SetFocus () {
  int /*long*/[] result = new int /*long*/[1];
  int rc = webBrowser.QueryInterface (nsIBaseWindow.NS_IBASEWINDOW_IID, result);
  if (rc != XPCOM.NS_OK) error (rc);
  if (result[0] == 0) error (XPCOM.NS_ERROR_NO_INTERFACE);
  
  nsIBaseWindow baseWindow = new nsIBaseWindow (result[0]);
  rc = baseWindow.SetFocus ();
  if (rc != XPCOM.NS_OK) error (rc);
  baseWindow.Release ();

  /*
  * Note. Mozilla notifies here that one of the children took
  * focus. This could or should be used to fire an SWT.FOCUS_IN
  * event on Browser focus listeners.
  */
  return XPCOM.NS_OK;       
}  

int GetVisibility (int /*long*/ aVisibility) {
  boolean visible = browser.isVisible () && !browser.getShell ().getMinimized ();
  XPCOM.memmove (aVisibility, new int[] {visible ? 1 : 0}, 4); /* PRBool */
  return XPCOM.NS_OK;
}

int SetVisibility (int aVisibility) {
  if (isChild) {
    WindowEvent event = new WindowEvent (browser);
    event.display = browser.getDisplay ();
    event.widget = browser;
    if (aVisibility != 0) {
      /*
      * Bug in Mozilla.  When the JavaScript window.open is executed, Mozilla
      * fires multiple SetVisibility 1 notifications.  The workaround is
      * to ignore subsequent notifications. 
      */
      if (!visible) {
        visible = true;
        event.location = location;
        event.size = size;
        event.addressBar = (chromeFlags & nsIWebBrowserChrome.CHROME_LOCATIONBAR) != 0;
        event.menuBar = (chromeFlags & nsIWebBrowserChrome.CHROME_MENUBAR) != 0;
        event.statusBar = (chromeFlags & nsIWebBrowserChrome.CHROME_STATUSBAR) != 0;
        event.toolBar = (chromeFlags & nsIWebBrowserChrome.CHROME_TOOLBAR) != 0;
        for (int i = 0; i < visibilityWindowListeners.length; i++) {
          visibilityWindowListeners[i].show (event);
        }
        location = null;
        size = null;
      }
    } else {
      visible = false;
      for (int i = 0; i < visibilityWindowListeners.length; i++) {
        visibilityWindowListeners[i].hide (event);
      }
    }
  } else {
    visible = aVisibility != 0;
  }
  return XPCOM.NS_OK;       
}

int GetTitle (int /*long*/ aTitle) {
  return XPCOM.NS_OK;       
}
 
int SetTitle (int /*long*/ aTitle) {
  if (titleListeners.length == 0) return XPCOM.NS_OK;
  TitleEvent event = new TitleEvent (browser);
  event.display = browser.getDisplay ();
  event.widget = browser;
  /*
  * To be consistent with other platforms the title event should
  * contain the page's url if the page does not contain a <title>
  * tag. 
  */
  int length = XPCOM.strlen_PRUnichar (aTitle);
  if (length > 0) {
    char[] dest = new char[length];
    XPCOM.memmove (dest, aTitle, length * 2);
    event.title = new String (dest);
  } else {
    event.title = getUrl ();
  }
  for (int i = 0; i < titleListeners.length; i++) {
    titleListeners[i].changed (event);
  }
  return XPCOM.NS_OK;       
}

int GetSiteWindow (int /*long*/ aSiteWindow) {
  /*
  * Note.  The handle is expected to be an HWND on Windows and
  * a GtkWidget* on GTK.  This callback is invoked on Windows
  * when the javascript window.print is invoked and the print
  * dialog comes up. If no handle is returned, the print dialog
  * does not come up on this platform.  
  */
  XPCOM.memmove (aSiteWindow, new int /*long*/[] {embedHandle}, C.PTR_SIZEOF);
  return XPCOM.NS_OK;       
}  
 
/* nsIWebBrowserChromeFocus */

int FocusNextElement () {
  /*
  * Bug in Mozilla embedding API.  Mozilla takes back the focus after sending
  * this event.  This prevents tabbing out of Mozilla. This behaviour can be reproduced
  * with the Mozilla application TestGtkEmbed.  The workaround is to
  * send the traversal notification after this callback returns.
  */
  browser.getDisplay ().asyncExec (new Runnable () {
    public void run () {
      if (browser.isDisposed ()) return;
      browser.traverse (SWT.TRAVERSE_TAB_NEXT);
    }
  });
  return XPCOM.NS_OK;  
}

int FocusPrevElement () {
  /*
  * Bug in Mozilla embedding API.  Mozilla takes back the focus after sending
  * this event.  This prevents tabbing out of Mozilla. This behaviour can be reproduced
  * with the Mozilla application TestGtkEmbed.  The workaround is to
  * send the traversal notification after this callback returns.
  */
  browser.getDisplay ().asyncExec (new Runnable () {
    public void run () {
      if (browser.isDisposed ()) return;
      browser.traverse (SWT.TRAVERSE_TAB_PREVIOUS);
    }
  });
  return XPCOM.NS_OK;       
}

/* nsIContextMenuListener */

int OnShowContextMenu (int aContextFlags, int /*long*/ aEvent, int /*long*/ aNode) {
  nsIDOMEvent domEvent = new nsIDOMEvent (aEvent);
  int /*long*/[] result = new int /*long*/[1];
  int rc = domEvent.QueryInterface (nsIDOMMouseEvent.NS_IDOMMOUSEEVENT_IID, result);
  if (rc != XPCOM.NS_OK) error (rc);
  if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

  nsIDOMMouseEvent domMouseEvent = new nsIDOMMouseEvent (result[0]);
  int[] aScreenX = new int[1], aScreenY = new int[1];
  rc = domMouseEvent.GetScreenX (aScreenX);
  if (rc != XPCOM.NS_OK) error (rc);
  rc = domMouseEvent.GetScreenY (aScreenY);
  if (rc != XPCOM.NS_OK) error (rc);
  domMouseEvent.Release ();
  
  Event event = new Event ();
  event.x = aScreenX[0];
  event.y = aScreenY[0];
  browser.notifyListeners (SWT.MenuDetect, event);
  if (!event.doit || browser.isDisposed ()) return XPCOM.NS_OK;
  Menu menu = browser.getMenu ();
  if (menu != null && !menu.isDisposed ()) {
    if (aScreenX[0] != event.x || aScreenY[0] != event.y) {
      menu.setLocation (event.x, event.y);
    }
    menu.setVisible (true);
  }
  return XPCOM.NS_OK;       
}

/* nsIURIContentListener */

int OnStartURIOpen (int /*long*/ aURI, int /*long*/ retval) {
  if (isRetrievingBadCert) return XPCOM.NS_OK;
  authCount = 0;

  nsIURI location = new nsIURI (aURI);
  int /*long*/ aSpec = XPCOM.nsEmbedCString_new ();
  location.GetSpec (aSpec);
  int length = XPCOM.nsEmbedCString_Length (aSpec);
  int /*long*/ buffer = XPCOM.nsEmbedCString_get (aSpec);
  buffer = XPCOM.nsEmbedCString_get (aSpec);
  byte[] dest = new byte[length];
  XPCOM.memmove (dest, buffer, length);
  XPCOM.nsEmbedCString_delete (aSpec);
  String value = new String (dest);

  /*
  * Navigating to "...aboutCertError.xhtml", or to "javascript:showSecuritySection()" when
  * the page "netError.xhtml" is showing, indicates that the last attempted page view had
  * an invalid certificate.  When this happens, veto the current navigate and re-navigate
  * to the page with the bad certificate so that NotifyCertProblem will be invoked.
  */
  if (value.indexOf ("aboutCertError.xhtml") != -1 || (isViewingErrorPage && value.indexOf ("javascript:showSecuritySection") != -1)) { //$NON-NLS-1$ //$NON-NLS-2$
    XPCOM.memmove (retval, new int[] {1}, 4); /* PRBool */
    isRetrievingBadCert = true;
    setUrl (lastNavigateURL);
    return XPCOM.NS_OK;
  }
  isViewingErrorPage = value.indexOf ("netError.xhtml") != -1; //$NON-NLS-1$

  boolean doit = true;
  if (request == 0) {
    /* 
     * listeners should not be notified of internal transitions like "javascript:..."
     * because this is an implementation side-effect, not a true navigate
     */
    if (!value.startsWith (PREFIX_JAVASCRIPT)) {
      if (locationListeners.length > 0) {
        LocationEvent event = new LocationEvent (browser);
        event.display = browser.getDisplay();
        event.widget = browser;
        event.location = value;
        /*
         * 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 (event.location.equals (URI_FROMMEMORY)) event.location = ABOUT_BLANK;
        event.doit = doit;
        for (int i = 0; i < locationListeners.length; i++) {
          locationListeners[i].changing (event);
        }
        doit = event.doit && !browser.isDisposed();
      }

      if (doit) {
        if (jsEnabledChanged) {
          jsEnabledChanged = false;
  
          int /*long*/[] result = new int /*long*/[1];
          int rc = webBrowser.QueryInterface (nsIWebBrowserSetup.NS_IWEBBROWSERSETUP_IID, result);
          if (rc != XPCOM.NS_OK) error (rc);
          if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
  
          nsIWebBrowserSetup setup = new nsIWebBrowserSetup (result[0]);
          result[0] = 0;
          rc = setup.SetProperty (nsIWebBrowserSetup.SETUP_ALLOW_JAVASCRIPT, jsEnabled ? 1 : 0);
          if (rc != XPCOM.NS_OK) error (rc);
          setup.Release ();
        }
        if (!isViewingErrorPage) lastNavigateURL = value;
      }
    }
  }
  XPCOM.memmove (retval, new int[] {doit ? 0 : 1}, 4); /* PRBool */
  return XPCOM.NS_OK;
}

int DoContent (int /*long*/ aContentType, int aIsContentPreferred, int /*long*/ aRequest, int /*long*/ aContentHandler, int /*long*/ retval) {
  return XPCOM.NS_ERROR_NOT_IMPLEMENTED;
}

int IsPreferred (int /*long*/ aContentType, int /*long*/ aDesiredContentType, int /*long*/ retval) {
  boolean preferred = false;
  int size = XPCOM.strlen (aContentType);
  if (size > 0) {
    byte[] typeBytes = new byte[size + 1];
    XPCOM.memmove (typeBytes, aContentType, size);
    String contentType = new String (typeBytes, 0, size);

    /* do not attempt to handle known problematic content types */
    if (!contentType.equals (XPCOM.CONTENT_MAYBETEXT) && !contentType.equals (XPCOM.CONTENT_MULTIPART)) {
      /* determine whether browser can handle the content type */
      int /*long*/[] result = new int /*long*/[1];
      int rc = XPCOM.NS_GetServiceManager (result);
      if (rc != XPCOM.NS_OK) error (rc);
      if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
      nsIServiceManager serviceManager = new nsIServiceManager (result[0]);
      result[0] = 0;

      /* First try to use the nsIWebNavigationInfo if it's available (>= mozilla 1.8) */
      byte[] aContractID = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_WEBNAVIGATIONINFO_CONTRACTID, true);
      rc = serviceManager.GetServiceByContractID (aContractID, nsIWebNavigationInfo.NS_IWEBNAVIGATIONINFO_IID, result);
      if (rc == XPCOM.NS_OK) {
        byte[] bytes = MozillaDelegate.wcsToMbcs (null, contentType, true);
        int /*long*/ typePtr = XPCOM.nsEmbedCString_new (bytes, bytes.length);
        nsIWebNavigationInfo info = new nsIWebNavigationInfo (result[0]);
        result[0] = 0;
        int[] isSupportedResult = new int[1]; /* PRUint32 */
        rc = info.IsTypeSupported (typePtr, 0, isSupportedResult);
        if (rc != XPCOM.NS_OK) error (rc);
        info.Release ();
        XPCOM.nsEmbedCString_delete (typePtr);
        preferred = isSupportedResult[0] != 0;
      } else {
        /* nsIWebNavigationInfo is not available, so do the type lookup */
        result[0] = 0;
        rc = serviceManager.GetService (XPCOM.NS_CATEGORYMANAGER_CID, nsICategoryManager.NS_ICATEGORYMANAGER_IID, result);
        if (rc != XPCOM.NS_OK) error (rc);
        if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

        nsICategoryManager categoryManager = new nsICategoryManager (result[0]);
        result[0] = 0;
        byte[] categoryBytes = MozillaDelegate.wcsToMbcs (null, "Gecko-Content-Viewers", true);  //$NON-NLS-1$
        rc = categoryManager.GetCategoryEntry (categoryBytes, typeBytes, result);
        categoryManager.Release ();
        /* if no viewer for the content type is registered then rc == XPCOM.NS_ERROR_NOT_AVAILABLE */
        preferred = rc == XPCOM.NS_OK;
      }
      serviceManager.Release ();
    }
  }

  XPCOM.memmove(retval, new int[] {preferred ? 1 : 0}, 4); /* PRBool */
  if (preferred) {
    XPCOM.memmove (aDesiredContentType, new int /*long*/[] {0}, C.PTR_SIZEOF);
  }
  return XPCOM.NS_OK;
}

int CanHandleContent (int /*long*/ aContentType, int aIsContentPreferred, int /*long*/ aDesiredContentType, int /*long*/ retval) {
  return XPCOM.NS_ERROR_NOT_IMPLEMENTED;
}

int GetLoadCookie (int /*long*/ aLoadCookie) {
  return XPCOM.NS_ERROR_NOT_IMPLEMENTED;
}

int SetLoadCookie (int /*long*/ aLoadCookie) {
  return XPCOM.NS_ERROR_NOT_IMPLEMENTED;
}

int GetParentContentListener (int /*long*/ aParentContentListener) {
  return XPCOM.NS_ERROR_NOT_IMPLEMENTED;
}
  
int SetParentContentListener (int /*long*/ aParentContentListener) {
  return XPCOM.NS_ERROR_NOT_IMPLEMENTED;
}

/* nsITooltipListener */

int OnShowTooltip (int aXCoords, int aYCoords, int /*long*/ aTipText) {
  int length = XPCOM.strlen_PRUnichar (aTipText);
  char[] dest = new char[length];
  XPCOM.memmove (dest, aTipText, length * 2);
  String text = new String (dest);
  if (tip != null && !tip.isDisposed ()) tip.dispose ();
  Display display = browser.getDisplay ();
  Shell parent = browser.getShell ();
  tip = new Shell (parent, SWT.ON_TOP);
  tip.setLayout (new FillLayout());
  Label label = new Label (tip, SWT.CENTER);
  label.setForeground (display.getSystemColor (SWT.COLOR_INFO_FOREGROUND));
  label.setBackground (display.getSystemColor (SWT.COLOR_INFO_BACKGROUND));
  label.setText (text);
  /*
  * Bug in Mozilla embedded API.  Tooltip coordinates are wrong for 
  * elements inside an inline frame (IFrame tag).  The workaround is 
  * to position the tooltip based on the mouse cursor location.
  */
  Point point = display.getCursorLocation ();
  /* Assuming cursor is 21x21 because this is the size of
   * the arrow cursor on Windows
   */ 
  point.y += 21;
  tip.setLocation (point);
  tip.pack ();
  tip.setVisible (true);
  return XPCOM.NS_OK;
}

int OnHideTooltip () {
  if (tip != null && !tip.isDisposed ()) tip.dispose ();
  tip = null;
  return XPCOM.NS_OK;
}

/* nsIDOMEventListener */

int HandleEvent (int /*long*/ event) {
  nsIDOMEvent domEvent = new nsIDOMEvent (event);

  int /*long*/ type = XPCOM.nsEmbedString_new ();
  int rc = domEvent.GetType (type);
  if (rc != XPCOM.NS_OK) error (rc);
  int length = XPCOM.nsEmbedString_Length (type);
  int /*long*/ buffer = XPCOM.nsEmbedString_get (type);
  char[] chars = new char[length];
  XPCOM.memmove (chars, buffer, length * 2);
  String typeString = new String (chars);
  XPCOM.nsEmbedString_delete (type);

  if (XPCOM.DOMEVENT_UNLOAD.equals (typeString)) {
    int /*long*/[] result = new int /*long*/[1];
    rc = domEvent.GetCurrentTarget (result);
    if (rc != XPCOM.NS_OK) error (rc);
    if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

    nsIDOMEventTarget target = new nsIDOMEventTarget (result[0]);
    unhookDOMListeners (target);
    target.Release ();
    return XPCOM.NS_OK;
  }

  if (XPCOM.DOMEVENT_FOCUS.equals (typeString)) {
    delegate.handleFocus ();
    return XPCOM.NS_OK;
  }

  if (XPCOM.DOMEVENT_KEYDOWN.equals (typeString)) {
    int /*long*/[] result = new int /*long*/[1];
    rc = domEvent.QueryInterface (nsIDOMKeyEvent.NS_IDOMKEYEVENT_IID, result);
    if (rc != XPCOM.NS_OK) error (rc);
    if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
    nsIDOMKeyEvent domKeyEvent = new nsIDOMKeyEvent (result[0]);
    result[0] = 0;

    int[] aKeyCode = new int[1]; /* PRUint32 */
    rc = domKeyEvent.GetKeyCode (aKeyCode);
    if (rc != XPCOM.NS_OK) error (rc);
    int keyCode = translateKey (aKeyCode[0]);

    /*
    * if keyCode == lastKeyCode then either a repeating key like Shift
    * is being held or a key for which key events are not sent has been
    * pressed.  In both of these cases a KeyDown should not be sent.
    */
    if (keyCode != lastKeyCode) {
      lastKeyCode = keyCode;
      switch (keyCode) {
        case SWT.SHIFT:
        case SWT.CONTROL:
        case SWT.ALT:
        case SWT.CAPS_LOCK:
        case SWT.NUM_LOCK:
        case SWT.SCROLL_LOCK:
        case SWT.COMMAND: {
          /* keypress events will not be received for these keys, so send KeyDowns for them now */
          int[] aAltKey = new int[1], aCtrlKey = new int[1], aShiftKey = new int[1], aMetaKey = new int[1]; /* PRBool */
          rc = domKeyEvent.GetAltKey (aAltKey);
          if (rc != XPCOM.NS_OK) error (rc);
          rc = domKeyEvent.GetCtrlKey (aCtrlKey);
          if (rc != XPCOM.NS_OK) error (rc);
          rc = domKeyEvent.GetShiftKey (aShiftKey);
          if (rc != XPCOM.NS_OK) error (rc);
          rc = domKeyEvent.GetMetaKey (aMetaKey);
          if (rc != XPCOM.NS_OK) error (rc);

          Event keyEvent = new Event ();
          keyEvent.widget = browser;
          keyEvent.type = SWT.KeyDown;
          keyEvent.keyCode = keyCode;
          keyEvent.stateMask = (aAltKey[0] != 0 ? SWT.ALT : 0) | (aCtrlKey[0] != 0 ? SWT.CTRL : 0) | (aShiftKey[0] != 0 ? SWT.SHIFT : 0) | (aMetaKey[0] != 0 ? SWT.COMMAND : 0);
          keyEvent.stateMask &= ~keyCode;    /* remove current keydown if it's a state key */
          browser.notifyListeners (keyEvent.type, keyEvent);
          if (!keyEvent.doit || browser.isDisposed ()) {
            domEvent.PreventDefault ();
          }
          break;
        }
        default: {
          /* 
          * If the keydown has Meta (but not Meta+Ctrl) as a modifier then send a KeyDown event for it here
          * because a corresponding keypress event will not be received for it from the DOM.  If the keydown
          * does not have Meta as a modifier, or has Meta+Ctrl as a modifier, then then do nothing here
          * because its KeyDown event will be sent from the keypress listener.
          */
          int[] aMetaKey = new int[1]; /* PRBool */
          rc = domKeyEvent.GetMetaKey (aMetaKey);
          if (rc != XPCOM.NS_OK) error (rc);
          if (aMetaKey[0] != 0) {
            int[] aCtrlKey = new int[1]; /* PRBool */
            rc = domKeyEvent.GetCtrlKey (aCtrlKey);
            if (rc != XPCOM.NS_OK) error (rc);
            if (aCtrlKey[0] == 0) {
              int[] aAltKey = new int[1], aShiftKey = new int[1]; /* PRBool */
              rc = domKeyEvent.GetAltKey (aAltKey);
              if (rc != XPCOM.NS_OK) error (rc);
              rc = domKeyEvent.GetShiftKey (aShiftKey);
              if (rc != XPCOM.NS_OK) error (rc);

              Event keyEvent = new Event ();
              keyEvent.widget = browser;
              keyEvent.type = SWT.KeyDown;
              keyEvent.keyCode = lastKeyCode;
              keyEvent.stateMask = (aAltKey[0] != 0 ? SWT.ALT : 0) | (aCtrlKey[0] != 0? SWT.CTRL : 0) | (aShiftKey[0] != 0? SWT.SHIFT : 0) | (aMetaKey[0] != 0? SWT.COMMAND : 0);
              browser.notifyListeners (keyEvent.type, keyEvent);
              if (!keyEvent.doit || browser.isDisposed ()) {
                domEvent.PreventDefault ();
              }
            }
          }
        }
      }
    }

    domKeyEvent.Release ();
    return XPCOM.NS_OK;
  }

  if (XPCOM.DOMEVENT_KEYPRESS.equals (typeString)) {
    /*
    * if keydown could not determine a keycode for this key then it's a
    * key for which key events are not sent (eg.- the Windows key)
    */
    if (lastKeyCode == 0) return XPCOM.NS_OK;

    /*
    * On linux only, unexpected keypress events are received for some
    * modifier keys.  The workaround is to ignore these events since
    * KeyDown events are sent for these keys in the keydown listener.  
    */
    switch (lastKeyCode) {
      case SWT.CAPS_LOCK:
      case SWT.NUM_LOCK:
      case SWT.SCROLL_LOCK: return XPCOM.NS_OK;
    }

    int /*long*/[] result = new int /*long*/[1];
    rc = domEvent.QueryInterface (nsIDOMKeyEvent.NS_IDOMKEYEVENT_IID, result);
    if (rc != XPCOM.NS_OK) error (rc);
    if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
    nsIDOMKeyEvent domKeyEvent = new nsIDOMKeyEvent (result[0]);
    result[0] = 0;

    int[] aAltKey = new int[1], aCtrlKey = new int[1], aShiftKey = new int[1], aMetaKey = new int[1]; /* PRBool */
    rc = domKeyEvent.GetAltKey (aAltKey);
    if (rc != XPCOM.NS_OK) error (rc);
    rc = domKeyEvent.GetCtrlKey (aCtrlKey);
    if (rc != XPCOM.NS_OK) error (rc);
    rc = domKeyEvent.GetShiftKey (aShiftKey);
    if (rc != XPCOM.NS_OK) error (rc);
    rc = domKeyEvent.GetMetaKey (aMetaKey);
    if (rc != XPCOM.NS_OK) error (rc);
    domKeyEvent.Release ();

    int[] aCharCode = new int[1]; /* PRUint32 */
    rc = domKeyEvent.GetCharCode (aCharCode);
    if (rc != XPCOM.NS_OK) error (rc);
    lastCharCode = aCharCode[0];
    if (lastCharCode == 0) {
      switch (lastKeyCode) {
        case SWT.TAB: lastCharCode = SWT.TAB; break;
        case SWT.CR: lastCharCode = SWT.CR; break;
        case SWT.BS: lastCharCode = SWT.BS; break;
        case SWT.ESC: lastCharCode = SWT.ESC; break;
        case SWT.DEL: lastCharCode = SWT.DEL; break;
      }
    }
    if (aCtrlKey[0] != 0 && (0 <= lastCharCode && lastCharCode <= 0x7F)) {
      if ('a'  <= lastCharCode && lastCharCode <= 'z') lastCharCode -= 'a' - 'A';
      if (64 <= lastCharCode && lastCharCode <= 95) lastCharCode -= 64;
    }

    Event keyEvent = new Event ();
    keyEvent.widget = browser;
    keyEvent.type = SWT.KeyDown;
    keyEvent.keyCode = lastKeyCode;
    keyEvent.character = (char)lastCharCode;
    keyEvent.stateMask = (aAltKey[0] != 0 ? SWT.ALT : 0) | (aCtrlKey[0] != 0 ? SWT.CTRL : 0) | (aShiftKey[0] != 0 ? SWT.SHIFT : 0) | (aMetaKey[0] != 0 ? SWT.COMMAND : 0);
    browser.notifyListeners (keyEvent.type, keyEvent);
    if (!keyEvent.doit || browser.isDisposed ()) {
      domEvent.PreventDefault ();
    }
    return XPCOM.NS_OK;
  }

  if (XPCOM.DOMEVENT_KEYUP.equals (typeString)) {
    int /*long*/[] result = new int /*long*/[1];
    rc = domEvent.QueryInterface (nsIDOMKeyEvent.NS_IDOMKEYEVENT_IID, result);
    if (rc != XPCOM.NS_OK) error (rc);
    if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
    nsIDOMKeyEvent domKeyEvent = new nsIDOMKeyEvent (result[0]);
    result[0] = 0;

    int[] aKeyCode = new int[1]; /* PRUint32 */
    rc = domKeyEvent.GetKeyCode (aKeyCode);
    if (rc != XPCOM.NS_OK) error (rc);
    int keyCode = translateKey (aKeyCode[0]);
    if (keyCode == 0) {
      /* indicates a key for which key events are not sent */
      domKeyEvent.Release ();
      return XPCOM.NS_OK;
    }
    if (keyCode != lastKeyCode) {
      /* keyup does not correspond to the last keydown */
      lastKeyCode = keyCode;
      lastCharCode = 0;
    }

    int[] aAltKey = new int[1], aCtrlKey = new int[1], aShiftKey = new int[1], aMetaKey = new int[1]; /* PRBool */
    rc = domKeyEvent.GetAltKey (aAltKey);
    if (rc != XPCOM.NS_OK) error (rc);
    rc = domKeyEvent.GetCtrlKey (aCtrlKey);
    if (rc != XPCOM.NS_OK) error (rc);
    rc = domKeyEvent.GetShiftKey (aShiftKey);
    if (rc != XPCOM.NS_OK) error (rc);
    rc = domKeyEvent.GetMetaKey (aMetaKey);
    if (rc != XPCOM.NS_OK) error (rc);
    domKeyEvent.Release ();

    Event keyEvent = new Event ();
    keyEvent.widget = browser;
    keyEvent.type = SWT.KeyUp;
    keyEvent.keyCode = lastKeyCode;
    keyEvent.character = (char)lastCharCode;
    keyEvent.stateMask = (aAltKey[0] != 0 ? SWT.ALT : 0) | (aCtrlKey[0] != 0 ? SWT.CTRL : 0) | (aShiftKey[0] != 0 ? SWT.SHIFT : 0) | (aMetaKey[0] != 0 ? SWT.COMMAND : 0);
    switch (lastKeyCode) {
      case SWT.SHIFT:
      case SWT.CONTROL:
      case SWT.ALT:
      case SWT.COMMAND: {
        keyEvent.stateMask |= lastKeyCode;
      }
    }
    browser.notifyListeners (keyEvent.type, keyEvent);
    if (!keyEvent.doit || browser.isDisposed ()) {
      domEvent.PreventDefault ();
    }
    lastKeyCode = lastCharCode = 0;
    return XPCOM.NS_OK;
  }

  /* mouse event */

  int /*long*/[] result = new int /*long*/[1];
  rc = domEvent.QueryInterface (nsIDOMMouseEvent.NS_IDOMMOUSEEVENT_IID, result);
  if (rc != XPCOM.NS_OK) error (rc);
  if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
  nsIDOMMouseEvent domMouseEvent = new nsIDOMMouseEvent (result[0]);
  result[0] = 0;

  /*
   * MouseOver and MouseOut events are fired any time the mouse enters or exits
   * any element within the Browser.  To ensure that SWT events are only
   * fired for mouse movements into or out of the Browser, do not fire an
   * event if the element being exited (on MouseOver) or entered (on MouseExit)
   * is within the Browser.
   */
  if (XPCOM.DOMEVENT_MOUSEOVER.equals (typeString) || XPCOM.DOMEVENT_MOUSEOUT.equals (typeString)) {
    rc = domMouseEvent.GetRelatedTarget (result);
    if (rc != XPCOM.NS_OK) error (rc);
    if (result[0] != 0) {
      domMouseEvent.Release ();
      return XPCOM.NS_OK;
    }
  }

  int[] aClientX = new int[1], aClientY = new int[1], aDetail = new int[1]; /* PRInt32 */
  rc = domMouseEvent.GetClientX (aClientX);
  if (rc != XPCOM.NS_OK) error (rc);
  rc = domMouseEvent.GetClientY (aClientY);
  if (rc != XPCOM.NS_OK) error (rc);
  rc = domMouseEvent.GetDetail (aDetail);
  if (rc != XPCOM.NS_OK) error (rc);
  short[] aButton = new short[1]; /* PRUint16 */
  rc = domMouseEvent.GetButton (aButton);
  if (rc != XPCOM.NS_OK) error (rc);
  int[] aAltKey = new int[1], aCtrlKey = new int[1], aShiftKey = new int[1], aMetaKey = new int[1]; /* PRBool */
  rc = domMouseEvent.GetAltKey (aAltKey);
  if (rc != XPCOM.NS_OK) error (rc);
  rc = domMouseEvent.GetCtrlKey (aCtrlKey);
  if (rc != XPCOM.NS_OK) error (rc);
  rc = domMouseEvent.GetShiftKey (aShiftKey);
  if (rc != XPCOM.NS_OK) error (rc);
  rc = domMouseEvent.GetMetaKey (aMetaKey);
  if (rc != XPCOM.NS_OK) error (rc);
  domMouseEvent.Release ();

  Event mouseEvent = new Event ();
  mouseEvent.widget = browser;
  mouseEvent.x = aClientX[0]; mouseEvent.y = aClientY[0];
  mouseEvent.stateMask = (aAltKey[0] != 0 ? SWT.ALT : 0) | (aCtrlKey[0] != 0 ? SWT.CTRL : 0) | (aShiftKey[0] != 0 ? SWT.SHIFT : 0) | (aMetaKey[0] != 0 ? SWT.COMMAND : 0);

  if (XPCOM.DOMEVENT_MOUSEDOWN.equals (typeString)) {
    delegate.handleMouseDown ();
    mouseEvent.type = SWT.MouseDown;
    mouseEvent.button = aButton[0] + 1;
    mouseEvent.count = aDetail[0];
  } else if (XPCOM.DOMEVENT_MOUSEUP.equals (typeString)) {
    /*
     * Bug on OSX.  For some reason multiple mouseup events come from the DOM
     * when button 3 is released on OSX.  The first of these events has a count
     * detail and the others do not.  The workaround is to not fire received
     * button 3 mouseup events that do not have a count since mouse events
     * without a click count are not valid.
     */
    int button = aButton[0] + 1;
    int count = aDetail[0];
    if (count == 0 && button == 3) return XPCOM.NS_OK;
    mouseEvent.type = SWT.MouseUp;
    mouseEvent.button = button;
    mouseEvent.count = count;
  } else if (XPCOM.DOMEVENT_MOUSEMOVE.equals (typeString)) {
    mouseEvent.type = SWT.MouseMove;
  } else if (XPCOM.DOMEVENT_MOUSEWHEEL.equals (typeString)) {
    mouseEvent.type = SWT.MouseWheel;
    mouseEvent.count = -aDetail[0];
  } else if (XPCOM.DOMEVENT_MOUSEOVER.equals (typeString)) {
    mouseEvent.type = SWT.MouseEnter;
  } else if (XPCOM.DOMEVENT_MOUSEOUT.equals (typeString)) {
    mouseEvent.type = SWT.MouseExit;
  } else if (XPCOM.DOMEVENT_MOUSEDRAG.equals (typeString)) {
    mouseEvent.type = SWT.DragDetect;
    mouseEvent.button = aButton[0] + 1;
    switch (mouseEvent.button) {
      case 1: mouseEvent.stateMask |= SWT.BUTTON1; break;
      case 2: mouseEvent.stateMask |= SWT.BUTTON2; break;
      case 3: mouseEvent.stateMask |= SWT.BUTTON3; break;
      case 4: mouseEvent.stateMask |= SWT.BUTTON4; break;
      case 5: mouseEvent.stateMask |= SWT.BUTTON5; break;
    }
  }

  browser.notifyListeners (mouseEvent.type, mouseEvent);
  if (browser.isDisposed ()) return XPCOM.NS_OK;
  if (aDetail[0] == 2 && XPCOM.DOMEVENT_MOUSEDOWN.equals (typeString)) {
    mouseEvent = new Event ();
    mouseEvent.widget = browser;
    mouseEvent.x = aClientX[0]; mouseEvent.y = aClientY[0];
    mouseEvent.stateMask = (aAltKey[0] != 0 ? SWT.ALT : 0) | (aCtrlKey[0] != 0 ? SWT.CTRL : 0) | (aShiftKey[0] != 0 ? SWT.SHIFT : 0) | (aMetaKey[0] != 0 ? SWT.COMMAND : 0);
    mouseEvent.type = SWT.MouseDoubleClick;
    mouseEvent.button = aButton[0] + 1;
    mouseEvent.count = aDetail[0];
    browser.notifyListeners (mouseEvent.type, mouseEvent);  
  }
  return XPCOM.NS_OK;
}

/* nsIBadCertListener2 */

int NotifyCertProblem (int /*long*/ socketInfo, int /*long*/ status, int /*long*/ targetSite, int /*long*/ _suppressError) {
  /* determine the host name and port */
  int length = XPCOM.nsEmbedCString_Length (targetSite);
  int /*long*/ buffer = XPCOM.nsEmbedCString_get (targetSite);
  byte[] dest = new byte[length];
  XPCOM.memmove (dest, buffer, length);
  final String urlPort = new String (dest);
  int index = urlPort.indexOf (':');
  final String host = urlPort.substring (0,index);
  final int port = Integer.valueOf (urlPort.substring (index + 1)).intValue ();

  /* create text descriptions of the certificate problem(s) */

  int /*long*/[] result = new int /*long*/[1];
  nsISupports supports = new nsISupports (status);
  int rc = supports.QueryInterface (nsISSLStatus.NS_ISSLSTATUS_IID, result);
  if (rc != XPCOM.NS_OK) error (rc);
  if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);

  nsISSLStatus sslStatus = new nsISSLStatus (result[0]);
  result[0] = 0;
  rc = sslStatus.GetServerCert (result);
  if (rc != XPCOM.NS_OK) error (rc);
  if (result[0] == 0) error (XPCOM.NS_ERROR_NULL_POINTER);

  final nsIX509Cert cert = new nsIX509Cert (result[0]);
  result[0] = 0;
  String[] problems = new String[3];
  int problemCount = 0, flags = 0;
  int[] intResult = new int[1];

  rc = sslStatus.GetIsDomainMismatch (intResult);
  if (intResult[0] != 0) {
    int /*long*/ ptr = XPCOM.nsEmbedString_new ();
    rc = cert.GetCommonName (ptr);
    if (rc != XPCOM.NS_OK) SWT.error (rc);
    length = XPCOM.nsEmbedString_Length (ptr);
    buffer = XPCOM.nsEmbedString_get (ptr);
    char[] chars = new char[length];
    XPCOM.memmove (chars, buffer, length * 2);
    String name = new String (chars);
    problems[problemCount++] = Compatibility.getMessage ("SWT_InvalidCert_InvalidName", new String[] {name}); //$NON-NLS-1$
    flags |= nsICertOverrideService.ERROR_MISMATCH;
    XPCOM.nsEmbedString_delete (ptr);
  }
  intResult[0] = 0;

  rc = sslStatus.GetIsNotValidAtThisTime (intResult);
  if (intResult[0] != 0) {
    rc = cert.GetValidity (result);
    if (rc != XPCOM.NS_OK) SWT.error (rc);
    if (result[0] == 0) error (XPCOM.NS_ERROR_NULL_POINTER);

    nsIX509CertValidity validity = new nsIX509CertValidity(result[0]);
    result[0] = 0;

    int /*long*/ ptr = XPCOM.nsEmbedString_new ();
    rc = validity.GetNotBeforeGMT (ptr);
    if (rc != XPCOM.NS_OK) SWT.error (rc);
    length = XPCOM.nsEmbedString_Length (ptr);
    buffer = XPCOM.nsEmbedString_get (ptr);
    char[] chars = new char[length];
    XPCOM.memmove (chars, buffer, length * 2);
    String notBefore = new String (chars);
    XPCOM.nsEmbedString_delete (ptr);

    ptr = XPCOM.nsEmbedString_new ();
    rc = validity.GetNotAfterGMT (ptr);
    if (rc != XPCOM.NS_OK) SWT.error (rc);
    length = XPCOM.nsEmbedString_Length (ptr);
    buffer = XPCOM.nsEmbedString_get (ptr);
    chars = new char[length];
    XPCOM.memmove (chars, buffer, length * 2);
    String notAfter = new String (chars);
    XPCOM.nsEmbedString_delete (ptr);

    String range = notBefore + " - " + notAfter; //$NON-NLS-1$
    problems[problemCount++] = Compatibility.getMessage ("SWT_InvalidCert_NotValid", new String[] {range}); //$NON-NLS-1$
    flags |= nsICertOverrideService.ERROR_TIME;
    validity.Release ();
  }
  intResult[0] = 0;

  rc = sslStatus.GetIsUntrusted (intResult);
  if (intResult[0] != 0) {
    int /*long*/ ptr = XPCOM.nsEmbedString_new ();
    rc = cert.GetIssuerCommonName (ptr);
    if (rc != XPCOM.NS_OK) SWT.error (rc);
    length = XPCOM.nsEmbedString_Length (ptr);
    buffer = XPCOM.nsEmbedString_get (ptr);
    char[] chars = new char[length];
    XPCOM.memmove (chars, buffer, length * 2);
    String name = new String (chars);
    problems[problemCount++] = Compatibility.getMessage ("SWT_InvalidCert_NotTrusted", new String[] {name}); //$NON-NLS-1$
    flags |= nsICertOverrideService.ERROR_UNTRUSTED;
    XPCOM.nsEmbedString_delete (ptr);
  }
  intResult[0] = 0;
  sslStatus.Release ();

  /*
  * The invalid certificate dialog must be shown asynchronously because
  * NotifyCertProblem implementations cannot block.
  */
  final int finalFlags = flags;
  final String[] finalProblems = new String[problemCount];
  System.arraycopy (problems, 0, finalProblems, 0, problemCount);
  final String url = lastNavigateURL;
  browser.getDisplay().asyncExec(new Runnable() {
    public void run() {
      if (browser.isDisposed ()) return;
      if (!url.equals (lastNavigateURL)) return;  /* user has navigated elsewhere */

      String message = Compatibility.getMessage ("SWT_InvalidCert_Message", new String[] {urlPort}); //$NON-NLS-1$
      if (new PromptDialog (browser.getShell ()).invalidCert (browser, message, finalProblems, cert)) {
        int /*long*/[] result = new int /*long*/[1];
        int rc = XPCOM.NS_GetServiceManager (result);
        if (rc != XPCOM.NS_OK) error (rc);
        if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
    
        nsIServiceManager serviceManager = new nsIServiceManager (result[0]);
        result[0] = 0;
        byte[] aContractID = MozillaDelegate.wcsToMbcs (null, XPCOM.NS_CERTOVERRIDE_CONTRACTID, true);
        rc = serviceManager.GetServiceByContractID (aContractID, nsICertOverrideService.NS_ICERTOVERRIDESERVICE_IID, result);
        if (rc != XPCOM.NS_OK) error (rc);
        if (result[0] == 0) error (XPCOM.NS_NOINTERFACE);
        serviceManager.Release ();
    
        nsICertOverrideService overrideService = new nsICertOverrideService (result[0]);
        result[0] = 0;
        byte[] hostBytes = MozillaDelegate.wcsToMbcs (null, host, false);
        int /*long*/ hostString = XPCOM.nsEmbedCString_new (hostBytes, hostBytes.length);
        rc = overrideService.RememberValidityOverride (hostString, port, cert.getAddress (), finalFlags, 1);
        browser.setUrl (url);
        XPCOM.nsEmbedCString_delete (hostString);
        overrideService.Release ();
      }
      cert.Release ();
    }
  });

  C.memmove (_suppressError, new int[] {1}, 4); /* PRInt32 */
  return XPCOM.NS_OK;
}

}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.