All Downloads are FREE. Search and download functionalities are using the official Maven repository.

gov.nasa.worldwind.util.webview.WindowsWebView Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2012 United States Government as represented by the Administrator of the
 * National Aeronautics and Space Administration.
 * All Rights Reserved.
 */

package gov.nasa.worldwind.util.webview;

import com.jogamp.opengl.util.texture.Texture;
import gov.nasa.worldwind.Configuration;
import gov.nasa.worldwind.avlist.AVList;
import gov.nasa.worldwind.exception.WWRuntimeException;
import gov.nasa.worldwind.render.*;
import gov.nasa.worldwind.util.*;

import java.awt.*;
import java.awt.event.*;
import java.net.URL;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;

/**
 * {@link WebView} implementation for Windows. This implementation uses the Window's native web browser control and the
 * MSHTML library to render a web page and create an OpenGL texture from the web browser window.
 * 

*

Limits on the number of WebViews that can be created

WindowsWebView creates a hidden * native window. Creating the native window can fail if the process runs out of Windows user object handles. Other GUI * elements in an application also consume these handles, so it is difficult to put a firm limit on how many WebViews * can be created. An application that creates only WebViews and no other windows can create about 1500 WebViews before * running out of handles. See MSDN for * more information on User Objects and operating system limits. * * @author pabercrombie * @version $Id: WindowsWebView.java 1171 2013-02-11 21:45:02Z dcollins $ */ public class WindowsWebView extends AbstractWebView { /** Lock to protect creation of the web view message loop thread. */ protected static final Object webViewUILock = new Object(); /** * Thread to run web view message loop. At most one message loop thread is running at any time, and it is shared by * all WebView instances. */ protected static Thread webViewUI; /** Identifier for the message loop in native code. */ protected static long webViewMessageLoop; /** The address of the native WindowsWebView object. Initialized during construction. */ protected long webViewWindowPtr; /** The address of the native NotificationAdapter object. Initialized during construction. */ protected long observerPtr; /** * Count of the number of active (non-disposed) WebView instances. The WebView UI thread is started when a WebView * is created (if it's not already running), and terminated when there are no active instances. */ protected static AtomicInteger instances = new AtomicInteger(); /** Flag to the indicate that the WebView has been disposed. */ protected boolean disposed = false; protected Color backgroundColor; /** * Create a new WebView. * * @param frameSize The size of the WebView rectangle. * * @throws UnsupportedOperationException if this class is instantiated on a non-Windows operating system. * @throws WWRuntimeException if creating the native web browser window fails for any reason. For * example, because the process has run out of User Object handles (see * documentation above). */ public WindowsWebView(Dimension frameSize) { if (frameSize == null) { String message = Logging.getMessage("nullValue.SizeIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (!Configuration.isWindowsOS()) { String message = Logging.getMessage("NativeLib.UnsupportedOperatingSystem", "Windows WebView", System.getProperty("os.name")); Logging.logger().severe(message); throw new UnsupportedOperationException(message); } this.frameSize = frameSize; try { // Increment the instance counter instances.incrementAndGet(); // Make sure that the message loop thread is running this.ensureMessageLoopRunning(); // Create the web view this.webViewWindowPtr = WindowsWebViewJNI.newWebViewWindow(webViewMessageLoop); if (this.webViewWindowPtr == 0) { String message = Logging.getMessage("WebView.NativeExceptionInitializingWebView"); Logging.logger().severe(message); throw new WWRuntimeException(message); } WindowsWebViewJNI.setFrameSize(this.webViewWindowPtr, this.frameSize.width, this.frameSize.height); this.observerPtr = WindowsWebViewJNI.newNotificationAdapter(this); WindowsWebViewJNI.addWindowUpdateObserver(this.webViewWindowPtr, observerPtr); } catch (RuntimeException e) { // If the WebView was not created successfully do not increment the instance counter. instances.decrementAndGet(); this.handleWebViewCreationError(); throw e; } catch (Error e) { // If the WebView was not created successfully do not increment the instance counter. instances.decrementAndGet(); this.handleWebViewCreationError(); throw e; } } /** * This method is called by the constructor if an exception is thrown creating the WebView. It gives the WebView a * change to cleanup static state that may have been set during the failed WebView construction. */ protected void handleWebViewCreationError() { try { this.stopMessageLoopIfNoInstances(); } catch (Throwable t) { String message = Logging.getMessage("WebView.ExceptionStoppingWebViewThread", t); Logging.logger().severe(message); } } /** {@inheritDoc} */ public void dispose() { if (this.disposed) // Do not dispose the WebView multiple times return; try { // Remove the notification adapter if (webViewWindowPtr != 0 && observerPtr != 0) WindowsWebViewJNI.removeWindowUpdateObserver(webViewWindowPtr, observerPtr); // Free the native WebView object associated with this Java WebView object. if (webViewWindowPtr != 0) { WindowsWebViewJNI.releaseWebView(webViewWindowPtr); // Decrement the instance counter. Only do this if the webViewWindow pointer was non-zero, indicating // that native resources were actually allocated. instances.decrementAndGet(); } if (observerPtr != 0) WindowsWebViewJNI.releaseComObject(observerPtr); this.webViewWindowPtr = 0; this.observerPtr = 0; // Terminate the message loop thread if this is the last active instance. this.stopMessageLoopIfNoInstances(); this.disposed = true; } catch (Exception e) { Logging.logger().log(Level.SEVERE, Logging.getMessage("generic.ExceptionAttemptingToDisposeRenderable"), e); } } /** * Ensure that the message loop thread is running. This method simply returns if the thread is already running. It * creates a new thread if the message thread is not running. This method does not return until the message loop is * initialized and ready for use. */ protected void ensureMessageLoopRunning() { synchronized (webViewUILock) { if (webViewUI == null || !webViewUI.isAlive()) { webViewMessageLoop = 0; // Create a new thread to run the web view message loop. webViewUI = new Thread("WebView UI") { public void run() { try { // Create a message loop in native code. This call must return // before any messages are sent to the WebView. webViewMessageLoop = WindowsWebViewJNI.newMessageLoop(); } catch (Throwable t) { webViewMessageLoop = -1; } finally { // Notify the outer thread that the message loop is ready or failed to start. synchronized (webViewUILock) { webViewUILock.notify(); } } // Process messages in native code until the message loop // is terminated. WindowsWebViewJNI.runMessageLoop(webViewMessageLoop); } }; webViewUI.start(); // Wait for the newly started thread to create the message loop. We cannot // safely use the WebView until the message loop has been initialized. while (webViewMessageLoop == 0) { try { webViewUILock.wait(1000); } catch (InterruptedException ignored) { } } } } } /** * Terminate the message loop thread if there are no active (non-disposed) WebView instances. Has no effect if there * are active instances. */ protected void stopMessageLoopIfNoInstances() { synchronized (webViewUILock) { if (instances.get() <= 0) { WindowsWebViewJNI.releaseMessageLoop(webViewMessageLoop); webViewMessageLoop = 0; webViewUI = null; } } } /** {@inheritDoc} */ public void setHTMLString(String htmlString) { if (this.webViewWindowPtr != 0) { WindowsWebViewJNI.setHTMLString(this.webViewWindowPtr, htmlString, null); } } /** {@inheritDoc} */ public void setHTMLString(String htmlString, URL baseURL) { if (this.webViewWindowPtr != 0) { WindowsWebViewJNI.setHTMLString(this.webViewWindowPtr, htmlString, baseURL != null ? baseURL.toString() : null); } } /** {@inheritDoc} */ public void setHTMLString(String htmlString, WebResourceResolver resourceResolver) { if (this.webViewWindowPtr != 0) { WindowsWebViewJNI.setHTMLStringWithResourceResolver(this.webViewWindowPtr, htmlString, resourceResolver); } } /** {@inheritDoc} */ public Dimension getContentSize() { return WindowsWebViewJNI.getContentSize(webViewWindowPtr); } /** {@inheritDoc} */ public Dimension getMinContentSize() { if (this.webViewWindowPtr != 0) { return WindowsWebViewJNI.getMinContentSize(this.webViewWindowPtr); } return null; } /** {@inheritDoc} */ public void setMinContentSize(Dimension size) { if (this.webViewWindowPtr != 0) { WindowsWebViewJNI.setMinContentSize(this.webViewWindowPtr, size.width, size.height); } } /** {@inheritDoc} */ public URL getContentURL() { if (this.webViewWindowPtr != 0) { return WWIO.makeURL(WindowsWebViewJNI.getContentURL(this.webViewWindowPtr)); } return null; } protected void doSetFrameSize(Dimension size) { if (this.webViewWindowPtr != 0) WindowsWebViewJNI.setFrameSize(this.webViewWindowPtr, size.width, size.height); } /** {@inheritDoc} */ public void sendEvent(InputEvent event) { if (event != null) { // Convert OpenGL coordinates to Windows. if (event instanceof MouseEvent) event = convertToWindows((MouseEvent) event); // Send the AWT InputEvent to the native WebView object if (this.webViewWindowPtr != 0) WindowsWebViewJNI.sendEvent(this.webViewWindowPtr, event); } } /** * {@inheritDoc} *

* Overridden to apply the active state to the native WebView. */ @Override public void setActive(boolean active) { super.setActive(active); if (this.webViewWindowPtr != 0) WindowsWebViewJNI.setActive(this.webViewWindowPtr, active); } /** {@inheritDoc} */ public void goBack() { if (this.webViewWindowPtr != 0) WindowsWebViewJNI.goBack(this.webViewWindowPtr); } /** {@inheritDoc} */ public void goForward() { if (this.webViewWindowPtr != 0) WindowsWebViewJNI.goForward(this.webViewWindowPtr); } /** {@inheritDoc} */ public void setBackgroundColor(Color color) { if (color == null) { String message = Logging.getMessage("nullValue.ColorIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } // Only set the color if it actually changed if (!color.equals(this.getBackgroundColor())) { this.backgroundColor = color; // Convert the color to an RGB hex triplet string that the WebBrowser will understand int rgb = (color.getRed() & 0xFF) << 16 | (color.getGreen() & 0xFF) << 8 | (color.getBlue() & 0xFF); String colorString = String.format("#%06X", rgb); WindowsWebViewJNI.setBackgroundColor(this.webViewWindowPtr, colorString); } } /** {@inheritDoc} */ public Color getBackgroundColor() { return this.backgroundColor; } /** {@inheritDoc} */ public Iterable getLinks() { if (this.webViewWindowPtr != 0) { AVList[] links = WindowsWebViewJNI.getLinks(this.webViewWindowPtr); if (links != null) return Arrays.asList(links); } return Collections.emptyList(); } /** * Converts the specified mouse event's screen point from WebView coordinates to Windows coordinates, and returns a * new event who's screen point is in Windows coordinates, with the origin at the upper left corner of the WebView * window. * * @param e The event to convert. * * @return A new mouse event in the Windows coordinate system. */ protected MouseEvent convertToWindows(MouseEvent e) { int x = e.getX(); int y = e.getY(); // Translate OpenGL screen coordinates to Windows by moving the Y origin from the lower left corner to // the upper left corner and flipping the direction of the Y axis. y = this.frameSize.height - y; if (e instanceof MouseWheelEvent) { return new MouseWheelEvent((Component) e.getSource(), e.getID(), e.getWhen(), e.getModifiers(), x, y, e.getClickCount(), e.isPopupTrigger(), ((MouseWheelEvent) e).getScrollType(), ((MouseWheelEvent) e).getScrollAmount(), ((MouseWheelEvent) e).getWheelRotation()); } else { return new MouseEvent((Component) e.getSource(), e.getID(), e.getWhen(), e.getModifiers(), x, y, e.getClickCount(), e.isPopupTrigger(), e.getButton()); } } //**********************************************************************// //******************** Texture Representation ************************// //**********************************************************************// /** {@inheritDoc} */ @Override protected WWTexture createTextureRepresentation(DrawContext dc) { BasicWWTexture texture = new WindowsWebViewTexture(this.getFrameSize(), false); texture.setUseAnisotropy(false); // Do not use anisotropic texture filtering. return texture; } protected class WindowsWebViewTexture extends WebViewTexture { protected long updateTime = -1; public WindowsWebViewTexture(Dimension frameSize, boolean useMipMaps) { super(frameSize, useMipMaps, true); } /** * Update the texture if the native WebView window has changed. * * @param dc Draw context */ @Override protected void updateIfNeeded(DrawContext dc) { // Return immediately if the native WebViewWindow object isn't initialized, and wait to update until the // native object is initialized. This method is called after the texture is bound, so we'll get another // chance to update as long as the WebView generates repaint events when it changes. long webViewWindowPtr = WindowsWebView.this.webViewWindowPtr; if (webViewWindowPtr == 0) return; // Return immediately if the texture isn't in the texture cache, and wait to update until the texture is // initialized and placed in the cache. This method is called after the texture is bound, so we'll get // another chance to update as long as the WebView generates repaint events when it changes. Texture texture = this.getTextureFromCache(dc); if (texture == null) return; // Load the WebViewWindow's current display pixels into the currently bound OGL texture if our update time // is different than the WebViewWindow's update time. long newUpdateTime = WindowsWebViewJNI.getUpdateTime(webViewWindowPtr); if (newUpdateTime != this.updateTime) { WindowsWebViewJNI.loadDisplayInGLTexture(webViewWindowPtr, texture.getTarget()); this.updateTime = newUpdateTime; } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy