📄 webbrowser.java
字号:
/*
* Copyright (C) 2004 Sun Microsystems, Inc. All rights reserved. Use is
* subject to license terms.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the Lesser GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA.
*/
package org.jdesktop.jdic.browser;
import java.util.Vector;
import java.net.URL;
import java.net.MalformedURLException;
import java.awt.*;
import java.awt.event.*;
import java.security.*;
import org.jdesktop.jdic.init.JdicInitException;
import org.jdesktop.jdic.init.JdicManager;
import org.jdesktop.jdic.browser.internal.WebBrowserUtil;
/**
* A <code>WebBrowser</code> component represents a blank rectangular area of
* the screen onto which the application can display webpages or from which the
* application can trap events from the browser window. In order to show <code>
* WebBrowser</code> component in GUI, users need to add <code>WebBrowser</code>
* to a top-level container, such as <code>Frame</code>.
* <p>
* The class that is interested in processing a <code>WebBrowser</code> event
* should implement interface <code>WebBrowserListener</code>, and the object
* created with that class should use WebBrowser's <code>addWebBrowserListener
* </code> method to register as a listener.
* <p>
* As an AWT component, a <code>WebBrowser</code> component must be hosted by
* a native container somewhere higher up in the component tree (for example,
* by a JPanel object).
*
* @see WebBrowserEvent
* @see WebBrowserListener
*
* @author Kyle Yuan
* @version 0.1, 03/07/17
*/
public class WebBrowser extends Canvas
{
private MyFocusListener focusListener = new MyFocusListener();
// eventThread should be initialized after JdicManager.initShareNative()
// in static block.
private static NativeEventThread eventThread;
private Vector webBrowserListeners = new Vector();
private int instanceNum;
private static int lastInstanceNum = 0;
// WebBrowser status.
private boolean isInitialized = false;
private boolean isBackEnabled = false;
private boolean isForwardEnabled = false;
private String initFailureMessage = "WebBrowser is not initialized.";
/**
* boolean flag used to indicate how to dispose this instance.
* @see #dispose()
* @see #removeNotify()
*/
final private boolean autoDispose;
/**
* Used to cache the url before dispose this instance.
*
* @see #dispose()
* @see #addNotify()
* @see #removeNotify()
*/
private URL urlBeforeDispose = null;
static {
// Add the initialization code from package org.jdesktop.jdic.init.
// To set the environment variables or initialize the set up for
// native libraries and executable files.
try{
JdicManager.loadLibrary("jdic");
}catch(PrivilegedActionException e){
e.printStackTrace();
}
eventThread = new NativeEventThread();
}
void setInitialized(boolean b) {
isInitialized = b;
}
void setInitFailureMessage(String msg) {
initFailureMessage = msg;
}
/**
* Constructs a new <code>WebBrowser</code> object with no URL specified.
* This instance will automatically dispose itself when <code>removeNotify()
* </code> is called.
* <p>
* This constructor is equivalent to <code>WebBrowser(true)</code>.
*
* @see #WebBrowser(boolean)
* @see #removeNotify()
* @see #dispose()
*/
public WebBrowser() {
this(true);
}
/**
* Constructs a new <code>WebBrowser</code> object with the specified
* boolean value <code>autoDispose</code> as the flag which indicates
* whether this instance will automatically dispose itself in <code>
* removeNotify()</code> or should be disposed by the developer directly
* calling <code>dispose()</code>.
* <p>
* This constructor is equivalent to <code>WebBrowser(null, autoDispose)
* </code>.
*
* @param autoDispose ture to indicate this instance will automatically
* dispose itself in <code>removeNotify()</code>; false to indicate
* the developer should call <code>dispose()</code> when this
* instance is no longer needed.
*
* @see #removeNotify()
* @see #dispose()
* @see #isAutoDispose()
*/
public WebBrowser(boolean autoDispose){
this(null, autoDispose);
}
/**
* Constructs a new <code>WebBrowser</code> with an URL specified. This
* instance will automatically dispose itself when <code>removeNotify()
* </code> is called.
* <p>This constructor is equivalent to <code>WebBrowser(url, true)</code>.
* @param url the URL to be shown in this instance.
*
* @see #WebBrowser(boolean)
* @see #removeNotify()
* @see #dispose()
*/
public WebBrowser(URL url) {
this(url, true);
}
/**
* Constructs a new <code>WebBrowser</code> with an specified URL and
* boolean flag to indicate the dispose schema.
*
* @param url the URL to be shown in this instance.
* @param autoDispose ture to indicate this instance will automatically
* dispose itself in <code>removeNotify()</code>; false to indicate
* the developer should call <code>dispose()</code> when this
* instance is no longer needed.
*
* @see #removeNotify()
* @see #dispose()
* @see #isAutoDispose()
*/
public WebBrowser(URL url, boolean autoDispose){
this.autoDispose = autoDispose;
synchronized(WebBrowser.class) {
instanceNum = lastInstanceNum;
lastInstanceNum++;
}
eventThread.attachWebBrowser(this);
if (0 == instanceNum) {
eventThread.start();
eventThread.fireNativeEvent(instanceNum, NativeEventData.EVENT_INIT);
}
if (null != url) {
setURL(url);
}
setFocusable(true);
addFocusListener(focusListener);
}
/**
* Creates the peer for this WebBrowser component. The peer allows us to
* modify the appearance of the WebBrowser component without changing its
* functionality.
*
* @see #removeNotify()
*/
public void addNotify() {
super.addNotify();
if(!isInitialized) {
eventThread.fireNativeEvent(instanceNum,
NativeEventData.EVENT_CREATEWINDOW);
/**
* Reset the URL before this instance was disposed.
* urlBeforeDispose is set in {@link #disposed()}.
*/
if(urlBeforeDispose != null){
this.setURL(urlBeforeDispose);
urlBeforeDispose = null;
}
}
if(!autoDispose){
this.setVisible(true);
}
}
/**
* Makes this WebBrowser component undisplayable by destroying it native
* screen resource if the <code>isAutoDispose()</code> returns true. Or just
* make this instance invisible if the <code>isAutoDispose()</code> return
* false. <p>
* This method is called by the toolkit internally and should not be called
* directly by programs.
* <p>
* If <code>isAutoDispose()</code> return false, developer should call
* <code>dispose()</code> when this instance is no longer needed.
* Otherwise, it resource will never be released untill this JVM exit.
*
* @see #addNotify()
* @see #dispose()
* @see #isAutoDispose()
*/
public void removeNotify() {
if(autoDispose){
dispose();
}else{
this.setVisible(false);
}
}
/**
* Release this instance's resource and make it undisplayable. If <code>
* isAutoDispose()</code> return true, this method will be called by the
* toolkit internally. If <code>isAutoDispose()</code> return false, this
* method should be called by developer when this instance is no longer
* needed.
*
* @see #removeNotify()
* @see #isAutoDispose()
*/
public void dispose() {
urlBeforeDispose = this.getURL();
Thread disposeThread = new Thread() {
public void run() {
synchronized( WebBrowser.this ){
eventThread.fireNativeEvent(instanceNum,
NativeEventData.EVENT_DESTROYWINDOW);
try {
// wait untill we get the message
// WebBrowserEvent.WEBBROWSER_DESTROYWINDOW_SUCC
// from native process.
WebBrowser.this.wait();
} catch (InterruptedException e) {
}
}
WebBrowser.super.removeNotify();
setInitialized(false);
}
};
disposeThread.start();
}
/**
* Return the boolean flag which indicates how to dispose this instance.
*
* @return true if this instance should be disposed by itself when
* <code>removeNotify()</code> is called. false if this instance
* should be disposed by the developer directly calling
* <code>dispose()</code> when it is no longer needed.
*
* @see #removeNotify()
* @see #addNotify()
* @see #dispose()
*/
public boolean isAutoDispose(){
return autoDispose;
}
/**
* Moves and resizes this component. The new location of the top-left
* corner is specified by <code>x</code> and <code>y</code>, and the new
* size is specified by <code>width</code> and <code>height</code>.
*
* @param x - the new x-coordinate of this component
* @param y - the new y-coordinate of this component
* @param width - the new width of this component
* @param height - the new height of this component
*/
public void setBounds(int x, int y, int width, int height) {
super.setBounds(x, y, width, height);
eventThread.fireNativeEvent(instanceNum,
NativeEventData.EVENT_SET_BOUNDS,
new Rectangle(x, y, width, height));
}
/*
* Dispatches a WebBrowserEvent to the Java embeddor, called by
* NativeEventThread.processIncomingMessage.
*/
void dispatchWebBrowserEvent(WebBrowserEvent e) {
int eid = e.getID();
WebBrowserUtil.trace("Got event from NativeEventThread " + eid);
// native browser needs immediate return value for these two events.
// Special trigger messages beginning with a '@' character, to give
// the native browser a yes or no to continue an operation(navigating
// an URL or openning a new window).
String msg = "@" + instanceNum + "," + eid + ",";
if (WebBrowserEvent.WEBBROWSER_BEFORE_NAVIGATE == eid) {
URL url = null;
try {
url = new URL(e.getData());
} catch (MalformedURLException ex1) {
try {
// IE omits the file:/ protocol for local files, append it.
if (!WebBrowserUtil.isDefaultBrowserMozilla()) {
url = new URL("file:/" + e.getData());
}
} catch (MalformedURLException ex2) {
}
}
msg += willOpenURL(url) ? "0" : "1";
eventThread.messenger.sendMessage(msg);
return;
}
else if (WebBrowserEvent.WEBBROWSER_BEFORE_NEWWINDOW == eid) {
msg += willOpenWindow() ? "0" : "1";
eventThread.messenger.sendMessage(msg);
return;
}
else if (WebBrowserEvent.WEBBROWSER_COMMAND_STATE_CHANGE == eid) {
String data = e.getData();
if (data.startsWith("forward")) {
isForwardEnabled = data.substring(8).equals("1");
WebBrowserUtil.trace("Forward State changed = "
+ isForwardEnabled);
}
else if (data.startsWith("back")) {
isBackEnabled = data.substring(5).equals("1");
WebBrowserUtil.trace("Back State changed = " + isBackEnabled);
}
return;
}
else if (WebBrowserEvent.WEBBROWSER_FOCUS_REQUEST == eid) {
WebBrowserUtil.trace("Got event from brower: Focus request.");
requestFocus();
return;
}
else if (WebBrowserEvent.WEBBROWSER_DESTROYWINDOW_SUCC == eid){
WebBrowserUtil.trace("Got event from brower: Destory window " +
"succeeds.");
synchronized(this){
// notify the disposeThread in removeNotify().
this.notify();
}
}
// for the normal case, call the corresponding method in listeners.
int size;
Vector tl;
synchronized (this) {
size = webBrowserListeners.size();
if (size == 0) {
return;
}
tl = (Vector) webBrowserListeners.clone();
}
for (int i = 0; i < size; ++i) {
WebBrowserListener listener = (WebBrowserListener) tl.elementAt(i);
switch (eid) {
case WebBrowserEvent.WEBBROWSER_DOWNLOAD_STARTED:
listener.downloadStarted(e);
break;
case WebBrowserEvent.WEBBROWSER_DOWNLOAD_COMPLETED:
listener.downloadCompleted(e);
break;
case WebBrowserEvent.WEBBROWSER_DOWNLOAD_PROGRESS:
listener.downloadProgress(e);
break;
case WebBrowserEvent.WEBBROWSER_DOWNLOAD_ERROR:
listener.downloadError(e);
break;
case WebBrowserEvent.WEBBROWSER_DOCUMENT_COMPLETED:
listener.documentCompleted(e);
break;
case WebBrowserEvent.WEBBROWSER_TITLE_CHANGE:
listener.titleChange(e);
break;
case WebBrowserEvent.WEBBROWSER_STATUSTEXT_CHANGE:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -