/**
  * Construct a new web browser.
  *
  * @param options the options to configure the behavior of this component.
  */
 public JWebBrowser(NSOption... options) {
   Map<Object, Object> optionMap = NSOption.createOptionMap(options);
   INativeWebBrowser.WebBrowserRuntime runtime = INativeWebBrowser.WebBrowserRuntime.DEFAULT;
   if (optionMap.get(USE_XULRUNNER_RUNTIME_OPTION_KEY) != null) {
     runtime = INativeWebBrowser.WebBrowserRuntime.XULRUNNER;
   }
   if (optionMap.get(USE_WEBKIT_RUNTIME_OPTION_KEY) != null) {
     if (runtime != INativeWebBrowser.WebBrowserRuntime.DEFAULT) {
       throw new IllegalStateException("Only one web browser runtime can be specified!");
     }
     runtime = INativeWebBrowser.WebBrowserRuntime.WEBKIT;
   }
   nativeWebBrowser =
       NativeCoreObjectFactory.create(
           INativeWebBrowser.class,
           "chrriis.dj.nativeswing.swtimpl.components.core.NativeWebBrowser",
           new Class<?>[] {JWebBrowser.class, INativeWebBrowser.WebBrowserRuntime.class},
           new Object[] {this, runtime});
   initialize((NativeComponent) nativeWebBrowser);
   webBrowserDecorator =
       createWebBrowserDecorator(nativeWebBrowser.createEmbeddableComponent(optionMap));
   add(webBrowserDecorator, BorderLayout.CENTER);
 }
/**
 * A native web browser, using Internet Explorer or Mozilla on Windows, and Mozilla on other
 * platforms.<br>
 * Methods execute when this component is initialized. If the component is not initialized, methods
 * will be executed as soon as it gets initialized. If the initialization fails, the methods will
 * not have any effect. The results from methods have relevant values only when the component is
 * valid.
 *
 * @author Christopher Deckers
 */
public class JWebBrowser extends NSPanelComponent {

  /** The function to use when sending a command from some web content using Javascript. */
  public static final String COMMAND_FUNCTION = INativeWebBrowser.COMMAND_FUNCTION;

  /**
   * The prefix to use when sending a command from some web content, using a static link or by
   * setting window.location from Javascript.
   */
  public static final String COMMAND_LOCATION_PREFIX = INativeWebBrowser.COMMAND_LOCATION_PREFIX;

  /**
   * The prefix to use when sending a command from some web content, by setting window.status from
   * Javascript.
   */
  public static final String COMMAND_STATUS_PREFIX = INativeWebBrowser.COMMAND_STATUS_PREFIX;

  private static final String USE_XULRUNNER_RUNTIME_OPTION_KEY = "XULRunner Runtime";
  private static final NSOption XUL_RUNNER_RUNTIME_OPTION =
      new NSOption(USE_XULRUNNER_RUNTIME_OPTION_KEY);

  /**
   * Create an option to make the web browser use the Mozilla XULRunner runtime.
   *
   * @return the option to use the XULRunner runtime.
   */
  public static NSOption useXULRunnerRuntime() {
    return XUL_RUNNER_RUNTIME_OPTION;
  }

  private static final String USE_WEBKIT_RUNTIME_OPTION_KEY = "Webkit Runtime";
  private static final NSOption WEBKIT_RUNTIME_OPTION = new NSOption(USE_WEBKIT_RUNTIME_OPTION_KEY);

  /**
   * Create an option to make the web browser use the Webkit runtime.
   *
   * @return the option to use the Webkit runtime.
   */
  public static NSOption useWebkitRuntime() {
    return WEBKIT_RUNTIME_OPTION;
  }

  /**
   * A factory that creates the decorators for web browsers.
   *
   * @author Christopher Deckers
   */
  public static interface WebBrowserDecoratorFactory {
    /**
     * Create the decorator for a web browser, which adds the rendering component to its component
     * hierarchy and will itself be added to the web browser.
     *
     * @param webBrowser the webbrowser for which to create the decorator.
     * @param renderingComponent the component that renders the web browser's content.
     * @return the decorator.
     */
    public WebBrowserDecorator createWebBrowserDecorator(
        JWebBrowser webBrowser, Component renderingComponent);
  }

  private static WebBrowserDecoratorFactory webBrowserDecoratorFactory;

  /**
   * Set the decorator that will be used for future web browser instances.
   *
   * @param webBrowserDecoratorFactory the factory that creates the decorators, or null for default
   *     decorators.
   */
  public static void setWebBrowserDecoratorFactory(
      WebBrowserDecoratorFactory webBrowserDecoratorFactory) {
    JWebBrowser.webBrowserDecoratorFactory = webBrowserDecoratorFactory;
  }

  private WebBrowserDecorator webBrowserDecorator;

  WebBrowserDecorator getWebBrowserDecorator() {
    return webBrowserDecorator;
  }

  /**
   * Create a decorator for this web browser. This method can be overridden so that the web browser
   * uses a different decorator.
   *
   * @param renderingComponent the component to add to the decorator's component hierarchy.
   * @return the decorator that was created.
   */
  protected WebBrowserDecorator createWebBrowserDecorator(Component renderingComponent) {
    if (webBrowserDecoratorFactory != null) {
      WebBrowserDecorator webBrowserDecorator =
          webBrowserDecoratorFactory.createWebBrowserDecorator(this, renderingComponent);
      if (webBrowserDecorator != null) {
        return webBrowserDecorator;
      }
    }
    return new DefaultWebBrowserDecorator(this, renderingComponent);
  }

  private static INativeWebBrowserStatic webBrowserStatic =
      NativeCoreObjectFactory.create(
          INativeWebBrowserStatic.class,
          "chrriis.dj.nativeswing.swtimpl.components.core.NativeWebBrowserStatic",
          new Class<?>[0],
          new Object[0]);

  /** Clear all session cookies from all web browser instances. */
  public static void clearSessionCookies() {
    webBrowserStatic.clearSessionCookies();
  }

  /**
   * Get a cookie for a given URL and a given name.
   *
   * @return the cookie or null if it does not exist.
   */
  public static String getCookie(String url, String name) {
    return webBrowserStatic.getCookie(url, name);
  }

  /**
   * Set a cookie for all web browser instances.
   *
   * @param url the url.
   * @param value the value, in a cookie form like: <code>foo=bar</code> (basic session cookie)
   *     <code>foo=bar; path=/; domain=.eclipse.org</code> (session cookie) <code>
   *     foo=bar; expires=Thu, 01-Jan-2030 00:00:01 GMT</code> (persistent cookie) <code>
   *     foo=; expires=Thu, 01-Jan-1970 00:00:01 GMT</code> (deletes cookie <code>foo</code>)
   */
  public static void setCookie(String url, String value) {
    webBrowserStatic.setCookie(url, value);
  }

  private INativeWebBrowser nativeWebBrowser;

  /**
   * Copy the appearance, the visibility of the various bars, from one web browser to another.
   *
   * @param fromWebBrowser the web browser to copy the appearance from.
   * @param toWebBrowser the web browser to copy the appearance to.
   */
  public static void copyAppearance(JWebBrowser fromWebBrowser, JWebBrowser toWebBrowser) {
    toWebBrowser.setLocationBarVisible(fromWebBrowser.isLocationBarVisible());
    toWebBrowser.setButtonBarVisible(fromWebBrowser.isButtonBarVisible());
    toWebBrowser.setMenuBarVisible(fromWebBrowser.isMenuBarVisible());
    toWebBrowser.setStatusBarVisible(fromWebBrowser.isStatusBarVisible());
  }

  /**
   * Copy the content, whether a URL or its HTML content, from one web browser to another.
   *
   * @param fromWebBrowser the web browser to copy the content from.
   * @param toWebBrowser the web browser to copy the content to.
   */
  public static void copyContent(JWebBrowser fromWebBrowser, JWebBrowser toWebBrowser) {
    String location = fromWebBrowser.getResourceLocation();
    if ("about:blank".equals(location)) {
      toWebBrowser.setHTMLContent(fromWebBrowser.getHTMLContent());
    } else {
      toWebBrowser.navigate(location);
    }
  }

  /**
   * Construct a new web browser.
   *
   * @param options the options to configure the behavior of this component.
   */
  public JWebBrowser(NSOption... options) {
    Map<Object, Object> optionMap = NSOption.createOptionMap(options);
    INativeWebBrowser.WebBrowserRuntime runtime = INativeWebBrowser.WebBrowserRuntime.DEFAULT;
    if (optionMap.get(USE_XULRUNNER_RUNTIME_OPTION_KEY) != null) {
      runtime = INativeWebBrowser.WebBrowserRuntime.XULRUNNER;
    }
    if (optionMap.get(USE_WEBKIT_RUNTIME_OPTION_KEY) != null) {
      if (runtime != INativeWebBrowser.WebBrowserRuntime.DEFAULT) {
        throw new IllegalStateException("Only one web browser runtime can be specified!");
      }
      runtime = INativeWebBrowser.WebBrowserRuntime.WEBKIT;
    }
    nativeWebBrowser =
        NativeCoreObjectFactory.create(
            INativeWebBrowser.class,
            "chrriis.dj.nativeswing.swtimpl.components.core.NativeWebBrowser",
            new Class<?>[] {JWebBrowser.class, INativeWebBrowser.WebBrowserRuntime.class},
            new Object[] {this, runtime});
    initialize((NativeComponent) nativeWebBrowser);
    webBrowserDecorator =
        createWebBrowserDecorator(nativeWebBrowser.createEmbeddableComponent(optionMap));
    add(webBrowserDecorator, BorderLayout.CENTER);
  }

  /**
   * Set whether the status bar is visible.
   *
   * @param isStatusBarVisible true if the status bar should be visible, false otherwise.
   */
  public void setStatusBarVisible(boolean isStatusBarVisible) {
    webBrowserDecorator.setStatusBarVisible(isStatusBarVisible);
  }

  /**
   * Indicate whether the status bar is visible.
   *
   * @return true if the status bar is visible.
   */
  public boolean isStatusBarVisible() {
    return webBrowserDecorator.isStatusBarVisible();
  }

  /**
   * Set whether the menu bar is visible.
   *
   * @param isMenuBarVisible true if the menu bar should be visible, false otherwise.
   */
  public void setMenuBarVisible(boolean isMenuBarVisible) {
    webBrowserDecorator.setMenuBarVisible(isMenuBarVisible);
  }

  /**
   * Indicate whether the menu bar is visible.
   *
   * @return true if the menu bar is visible.
   */
  public boolean isMenuBarVisible() {
    return webBrowserDecorator.isMenuBarVisible();
  }

  /**
   * Set whether the button bar is visible.
   *
   * @param isButtonBarVisible true if the button bar should be visible, false otherwise.
   */
  public void setButtonBarVisible(boolean isButtonBarVisible) {
    webBrowserDecorator.setButtonBarVisible(isButtonBarVisible);
  }

  /**
   * Indicate whether the button bar is visible.
   *
   * @return true if the button bar is visible.
   */
  public boolean isButtonBarVisible() {
    return webBrowserDecorator.isButtonBarVisible();
  }

  /**
   * Set whether the location bar is visible.
   *
   * @param isLocationBarVisible true if the location bar should be visible, false otherwise.
   */
  public void setLocationBarVisible(boolean isLocationBarVisible) {
    webBrowserDecorator.setLocationBarVisible(isLocationBarVisible);
  }

  /**
   * Indicate whether the location bar is visible.
   *
   * @return true if the location bar is visible.
   */
  public boolean isLocationBarVisible() {
    return webBrowserDecorator.isLocationBarVisible();
  }

  /**
   * Get the title of the web page.
   *
   * @return the title of the page.
   */
  public String getPageTitle() {
    return nativeWebBrowser.getPageTitle();
  }

  /**
   * Get the status text.
   *
   * @return the status text.
   */
  public String getStatusText() {
    return nativeWebBrowser.getStatusText();
  }

  /**
   * Get the HTML content.
   *
   * @return the HTML content.
   */
  public String getHTMLContent() {
    return nativeWebBrowser.getHTMLContent();
  }

  /**
   * Set the HTML content.
   *
   * @param html the HTML content.
   */
  public boolean setHTMLContent(String html) {
    return nativeWebBrowser.setHTMLContent(html);
  }

  /**
   * Get the location of the resource currently displayed.
   *
   * @return the location.
   */
  public String getResourceLocation() {
    return nativeWebBrowser.getResourceLocation();
  }

  /**
   * Navigate to a resource, with its location specified as a URL or path.
   *
   * @param resourceLocation the URL or path.
   * @return true if the navigation was successful.
   */
  public boolean navigate(String resourceLocation) {
    return navigate(resourceLocation, null);
  }

  /**
   * Navigate to a resource, with its location specified as a URL or path.
   *
   * @param resourceLocation the URL or path.
   * @param parameters the parameters (headers and POST data) to send with the navigation request.
   * @return true if the navigation was successful.
   */
  public boolean navigate(String resourceLocation, WebBrowserNavigationParameters parameters) {
    return nativeWebBrowser.navigate(resourceLocation, parameters);
  }

  /**
   * Indicate if the web browser Back functionality is enabled.
   *
   * @return true if the web browser Back functionality is enabled.
   */
  public boolean isBackNavigationEnabled() {
    return nativeWebBrowser.isBackNavigationEnabled();
  }

  /** Invoke the web browser Back functionality. */
  public void navigateBack() {
    nativeWebBrowser.navigateBack();
  }

  /**
   * Indicate if the web browser Forward functionality is enabled.
   *
   * @return true if the web browser Forward functionality is enabled.
   */
  public boolean isForwardNavigationEnabled() {
    return nativeWebBrowser.isForwardNavigationEnabled();
  }

  /** Invoke the web browser Forward functionality. */
  public void navigateForward() {
    nativeWebBrowser.navigateForward();
  }

  /** Invoke the web browser Reload functionality. */
  public void reloadPage() {
    nativeWebBrowser.reloadPage();
  }

  /** Invoke the web browser Stop functionality, to stop all current loading operations. */
  public void stopLoading() {
    nativeWebBrowser.stopLoading();
  }

  /**
   * Indicate if Javascript will be allowed to run in pages subsequently viewed.
   *
   * @return true if Javascript is enabled.
   */
  public boolean isJavascriptEnabled() {
    return nativeWebBrowser.isJavascriptEnabled();
  }

  /**
   * Set whether javascript will be allowed to run in pages subsequently. Note that setting this
   * value does not affect the running of javascript in the current page.
   *
   * @param isJavascriptEnabled true to enable Javascript, false otherwise.
   */
  public void setJavascriptEnabled(boolean isJavascriptEnabled) {
    nativeWebBrowser.setJavascriptEnabled(isJavascriptEnabled);
  }

  //  /**
  //   * Execute some javascript, and wait for the indication of success.
  //   * @param javascript the javascript to execute.
  //   * @return true if the execution succeeded.
  //   */
  //  public boolean executeJavascriptAndWait(String javascript) {
  //    return nativeComponent.executeJavascriptAndWait(javascript);
  //  }

  /**
   * Execute some javascript.
   *
   * @param javascript the javascript to execute.
   */
  public void executeJavascript(String javascript) {
    nativeWebBrowser.executeJavascript(javascript);
  }

  /**
   * Execute some javascript, and wait for the result coming from the return statements.
   *
   * @param javascript the javascript to execute which must contain explicit return statements.
   * @return the value, potentially a String, Number, Boolean.
   */
  public Object executeJavascriptWithResult(String javascript) {
    if (!javascript.endsWith(";")) {
      javascript = javascript + ";";
    }
    //    return nativeWebBrowser.executeJavascriptWithResult(
    //        "try {" +
    //        "  return function() {" + javascript + "}();" +
    //        "} catch(exxxxx) {" +
    //        "  return null;" +
    //        "}");
    Object[] result =
        executeJavascriptWithCommandResult(
            "[[getScriptResult]]",
            "try {"
                + "  "
                + COMMAND_FUNCTION
                + "('[[getScriptResult]]', (function() {"
                + javascript
                + "})());"
                + "} catch(exxxxx) {"
                + "  "
                + COMMAND_FUNCTION
                + "('[[getScriptResult]]');"
                + "}");
    if (result == null) {
      return null;
    }
    return result.length == 0 ? null : result[0];
  }

  /**
   * Create the Javascript function call using the function name and Java objects as arguments. Note
   * that it does not contain a semi-colon at the end of the statement, to allow call chaining.
   *
   * @param functionName the name of the Javascript funtion.
   * @param args the Java objects (String, number, boolean, or array) which will get converted to
   *     Javascript arguments.
   * @return the function call, in the form "functionName(convArg1, convArg2, ...)".
   */
  public static String createJavascriptFunctionCall(String functionName, Object... args) {
    StringBuilder sb = new StringBuilder();
    sb.append(functionName).append('(');
    for (int i = 0; i < args.length; i++) {
      if (i > 0) {
        sb.append(", ");
      }
      sb.append(convertJavaObjectToJavascript(args[i]));
    }
    sb.append(")");
    return sb.toString();
  }

  /**
   * Convert a Java object to Javascript, to simplify the task of executing scripts. Conversion adds
   * quotes around Strings (with Java escaping and Javascript unescaping around), add brackets to
   * arrays, treats arrays of arrays, and can handle null values.
   *
   * @param o the object to convert, which can be a String, number, boolean, or array.
   */
  public static String convertJavaObjectToJavascript(Object o) {
    if (o == null) {
      return "null";
    }
    if (o instanceof Boolean || o instanceof Number) {
      return o.toString();
    }
    if (o.getClass().isArray()) {
      StringBuilder sb = new StringBuilder();
      sb.append('[');
      int length = Array.getLength(o);
      for (int i = 0; i < length; i++) {
        if (i > 0) {
          sb.append(", ");
        }
        sb.append(convertJavaObjectToJavascript(Array.get(o, i)));
      }
      sb.append(']');
      return sb.toString();
    }
    o = o.toString();
    String encodedArg = Utils.encodeURL((String) o);
    if (o.equals(encodedArg)) {
      return '\'' + (String) o + '\'';
    }
    return "decodeURIComponent('" + encodedArg + "')";
  }

  //  private static Object convertJavascriptObjectToJava(String type, String value) {
  //    if(type.length() == 0) {
  //      return null;
  //    }
  //    if("boolean".equals(type)) {
  //      return Boolean.parseBoolean(value);
  //    }
  //    if("number".equals(type)) {
  //      try {
  //        return Integer.parseInt(value);
  //      } catch(Exception e) {}
  //      try {
  //        return Float.parseFloat(value);
  //      } catch(Exception e) {}
  //      try {
  //        return Long.parseLong(value);
  //      } catch(Exception e) {}
  //      throw new IllegalStateException("Could not convert number: " + value);
  //    }
  //    return value;
  //  }

  private static class NCommandListener extends WebBrowserAdapter {
    private String command;
    private AtomicReference<Object[]> result;

    private NCommandListener(String command, AtomicReference<Object[]> result) {
      this.command = command;
      this.result = result;
    }

    @Override
    public void commandReceived(WebBrowserCommandEvent e) {
      if (command.equals(e.getCommand())) {
        result.set(e.getParameters());
        ((INativeWebBrowser) e.getWebBrowser().getNativeComponent()).removeWebBrowserListener(this);
      }
    }
  }

  private Object[] executeJavascriptWithCommandResult(final String command, String script) {
    if (!((NativeComponent) nativeWebBrowser).isNativePeerInitialized()) {
      return null;
    }
    final AtomicReference<Object[]> result = new AtomicReference<Object[]>();
    WebBrowserAdapter webBrowserListener = new NCommandListener(command, result);
    nativeWebBrowser.addWebBrowserListener(webBrowserListener);
    if (nativeWebBrowser.executeJavascriptAndWait(script)) {
      for (int i = 0; i < 20; i++) {
        EventDispatchUtils.sleepWithEventDispatch(
            new EventDispatchUtils.Condition() {
              public boolean getValue() {
                return result.get() != null;
              }
            },
            50);
      }
    }
    nativeWebBrowser.removeWebBrowserListener(webBrowserListener);
    return result.get();
  }

  /**
   * Get the loading progress, a value between 0 and 100, where 100 means it is fully loaded.
   *
   * @return a value between 0 and 100 indicating the current loading progress.
   */
  public int getLoadingProgress() {
    return nativeWebBrowser.getLoadingProgress();
  }

  private static class NativeWebBrowserListener implements WebBrowserListener {

    private Reference<WebBrowserListener> webBrowserListener;

    public NativeWebBrowserListener(WebBrowserListener webBrowserListener) {
      this.webBrowserListener = new WeakReference<WebBrowserListener>(webBrowserListener);
    }

    public void commandReceived(WebBrowserCommandEvent e) {
      WebBrowserListener webBrowserListener = this.webBrowserListener.get();
      if (webBrowserListener != null) {
        boolean isInternal = e.getCommand().startsWith("[Chrriis]");
        if (!isInternal || webBrowserListener.getClass().getName().startsWith("chrriis.")) {
          webBrowserListener.commandReceived(e);
        }
      }
    }

    public void loadingProgressChanged(WebBrowserEvent e) {
      WebBrowserListener webBrowserListener = this.webBrowserListener.get();
      if (webBrowserListener != null) {
        webBrowserListener.loadingProgressChanged(e);
      }
    }

    public void locationChangeCanceled(WebBrowserNavigationEvent e) {
      WebBrowserListener webBrowserListener = this.webBrowserListener.get();
      if (webBrowserListener != null) {
        webBrowserListener.locationChangeCanceled(e);
      }
    }

    public void locationChanged(WebBrowserNavigationEvent e) {
      WebBrowserListener webBrowserListener = this.webBrowserListener.get();
      if (webBrowserListener != null) {
        webBrowserListener.locationChanged(e);
      }
    }

    public void locationChanging(WebBrowserNavigationEvent e) {
      WebBrowserListener webBrowserListener = this.webBrowserListener.get();
      if (webBrowserListener != null) {
        webBrowserListener.locationChanging(e);
      }
    }

    public void statusChanged(WebBrowserEvent e) {
      WebBrowserListener webBrowserListener = this.webBrowserListener.get();
      if (webBrowserListener != null) {
        webBrowserListener.statusChanged(e);
      }
    }

    public void titleChanged(WebBrowserEvent e) {
      WebBrowserListener webBrowserListener = this.webBrowserListener.get();
      if (webBrowserListener != null) {
        webBrowserListener.titleChanged(e);
      }
    }

    public void windowClosing(WebBrowserEvent e) {
      WebBrowserListener webBrowserListener = this.webBrowserListener.get();
      if (webBrowserListener != null) {
        webBrowserListener.windowClosing(e);
      }
    }

    public void windowOpening(WebBrowserWindowOpeningEvent e) {
      WebBrowserListener webBrowserListener = this.webBrowserListener.get();
      if (webBrowserListener != null) {
        webBrowserListener.windowOpening(e);
      }
    }

    public void windowWillOpen(WebBrowserWindowWillOpenEvent e) {
      WebBrowserListener webBrowserListener = this.webBrowserListener.get();
      if (webBrowserListener != null) {
        webBrowserListener.windowWillOpen(e);
      }
    }
  }

  /**
   * Set the authentication handler.
   *
   * @param authenticationHandler The authentication handler, or null to remove the current one.
   */
  public void setAuthenticationHandler(WebBrowserAuthenticationHandler authenticationHandler) {
    nativeWebBrowser.setAuthenticationHandler(authenticationHandler);
  }

  /**
   * Get the authentication handler or null if none is set.
   *
   * @return the authentication handler.
   */
  public WebBrowserAuthenticationHandler getAuthenticationHandler() {
    return nativeWebBrowser.getAuthenticationHandler();
  }

  private Map<WebBrowserListener, NativeWebBrowserListener>
      webBrowserListenerToNativeWebBrowserListenerMap =
          new HashMap<WebBrowserListener, NativeWebBrowserListener>();

  /**
   * Add a web browser listener.
   *
   * @param listener The web browser listener to add.
   */
  public void addWebBrowserListener(WebBrowserListener listener) {
    listenerList.add(WebBrowserListener.class, listener);
    NativeWebBrowserListener nativeWebBrowserListener = new NativeWebBrowserListener(listener);
    webBrowserListenerToNativeWebBrowserListenerMap.put(listener, nativeWebBrowserListener);
    nativeWebBrowser.addWebBrowserListener(nativeWebBrowserListener);
  }

  /**
   * Remove a web browser listener.
   *
   * @param listener the web browser listener to remove.
   */
  public void removeWebBrowserListener(WebBrowserListener listener) {
    listenerList.remove(WebBrowserListener.class, listener);
    NativeWebBrowserListener nativeWebBrowserListener =
        webBrowserListenerToNativeWebBrowserListenerMap.remove(listener);
    if (nativeWebBrowserListener != null) {
      nativeWebBrowser.removeWebBrowserListener(nativeWebBrowserListener);
    }
  }

  /**
   * Get the web browser listeners.
   *
   * @return the web browser listeners.
   */
  public WebBrowserListener[] getWebBrowserListeners() {
    return listenerList.getListeners(WebBrowserListener.class);
  }

  /**
   * Show or hide all the bars at once.
   *
   * @param areBarsVisible true to show all bars, false to hide them all.
   */
  public void setBarsVisible(boolean areBarsVisible) {
    setMenuBarVisible(areBarsVisible);
    setButtonBarVisible(areBarsVisible);
    setLocationBarVisible(areBarsVisible);
    setStatusBarVisible(areBarsVisible);
  }

  /**
   * Get the web browser window if the web browser is contained in one.
   *
   * @return the web browser Window, or null.
   */
  public JWebBrowserWindow getWebBrowserWindow() {
    Window window = SwingUtilities.getWindowAncestor(this);
    if (window instanceof JWebBrowserWindow) {
      return (JWebBrowserWindow) window;
    }
    return null;
  }

  /**
   * Set whether this component is able to detect a popup menu gesture to show its default popup
   * menu.
   *
   * @param isDefaultPopupMenuRegistered true if the default popup menu is registered.
   */
  public void setDefaultPopupMenuRegistered(boolean isDefaultPopupMenuRegistered) {
    nativeWebBrowser.setDefaultPopupMenuRegistered(isDefaultPopupMenuRegistered);
  }

  /**
   * Register a function to the web browser which can be called from Javascript.
   *
   * @param function the function to add.
   */
  public void registerFunction(WebBrowserFunction function) {
    nativeWebBrowser.registerFunction(function);
  }

  /**
   * Unregister a function from the web browser, which cannot be called from Javascript anymore.
   *
   * @param function the function to remove.
   */
  public void unregisterFunction(WebBrowserFunction function) {
    nativeWebBrowser.unregisterFunction(function);
  }

  /**
   * Get the type of browser (ie, mozilla, etc.).
   *
   * @return the browser type.
   */
  public String getBrowserType() {
    return nativeWebBrowser.getBrowserType();
  }

  /**
   * Get the version of the browser. This is mainly for troubleshooting and may even return null if
   * it fails to detect it.
   *
   * @return the version or null if it could not be obtained.
   */
  public String getBrowserVersion() {
    return nativeWebBrowser.getBrowserVersion();
  }

  /**
   * Dispose the native peer but potentially allows confirmation dialog to the user.
   *
   * @param isConfirmationDialogAllowed true if the component is allowed to ask confirmation to the
   *     user, false otherwise.
   * @return true if the component was disposed, false otherwise.
   */
  public boolean disposeNativePeer(boolean isConfirmationDialogAllowed) {
    if (isConfirmationDialogAllowed) {
      return nativeWebBrowser.unloadAndDispose();
    }
    disposeNativePeer();
    return true;
  }

  /**
   * Attempt to print the content of the currently loaded page specifying whether to show the print
   * dialog. This method invokes the Javascript print facility, or potentially direct native calls
   * if the platforms allows it. Not all runtimes support printing without showing the dialog (only
   * IE supports it at the moment) in which case the method would return false.
   *
   * @return true if the method call worked, false otherwise.
   */
  public boolean print(boolean isShowingDialog) {
    return nativeWebBrowser.print(isShowingDialog);
  }
}