|
1 /* |
|
2 * Copyright 1995-2007 Sun Microsystems, Inc. All Rights Reserved. |
|
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
4 * |
|
5 * This code is free software; you can redistribute it and/or modify it |
|
6 * under the terms of the GNU General Public License version 2 only, as |
|
7 * published by the Free Software Foundation. Sun designates this |
|
8 * particular file as subject to the "Classpath" exception as provided |
|
9 * by Sun in the LICENSE file that accompanied this code. |
|
10 * |
|
11 * This code is distributed in the hope that it will be useful, but WITHOUT |
|
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
14 * version 2 for more details (a copy is included in the LICENSE file that |
|
15 * accompanied this code). |
|
16 * |
|
17 * You should have received a copy of the GNU General Public License version |
|
18 * 2 along with this work; if not, write to the Free Software Foundation, |
|
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
20 * |
|
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
|
22 * CA 95054 USA or visit www.sun.com if you need additional information or |
|
23 * have any questions. |
|
24 */ |
|
25 |
|
26 package sun.applet; |
|
27 |
|
28 import java.applet.*; |
|
29 import java.awt.*; |
|
30 import java.awt.event.*; |
|
31 import java.awt.image.ColorModel; |
|
32 import java.awt.image.MemoryImageSource; |
|
33 import java.io.*; |
|
34 import java.lang.ref.WeakReference; |
|
35 import java.lang.reflect.InvocationTargetException; |
|
36 import java.lang.reflect.Method; |
|
37 import java.net.InetAddress; |
|
38 import java.net.JarURLConnection; |
|
39 import java.net.MalformedURLException; |
|
40 import java.net.SocketPermission; |
|
41 import java.net.URL; |
|
42 import java.net.UnknownHostException; |
|
43 import java.security.*; |
|
44 import java.util.*; |
|
45 import java.util.Collections; |
|
46 import java.util.Locale; |
|
47 import java.util.WeakHashMap; |
|
48 import javax.swing.SwingUtilities; |
|
49 import sun.awt.AppContext; |
|
50 import sun.awt.EmbeddedFrame; |
|
51 import sun.awt.SunToolkit; |
|
52 import sun.misc.MessageUtils; |
|
53 import sun.misc.PerformanceLogger; |
|
54 import sun.misc.Queue; |
|
55 import sun.security.util.SecurityConstants; |
|
56 |
|
57 /** |
|
58 * Applet panel class. The panel manages and manipulates the |
|
59 * applet as it is being loaded. It forks a separate thread in a new |
|
60 * thread group to call the applet's init(), start(), stop(), and |
|
61 * destroy() methods. |
|
62 * |
|
63 * @author Arthur van Hoff |
|
64 */ |
|
65 public |
|
66 abstract class AppletPanel extends Panel implements AppletStub, Runnable { |
|
67 |
|
68 /** |
|
69 * The applet (if loaded). |
|
70 */ |
|
71 Applet applet; |
|
72 |
|
73 /** |
|
74 * Applet will allow initialization. Should be |
|
75 * set to false if loading a serialized applet |
|
76 * that was pickled in the init=true state. |
|
77 */ |
|
78 protected boolean doInit = true; |
|
79 |
|
80 |
|
81 /** |
|
82 * The classloader for the applet. |
|
83 */ |
|
84 AppletClassLoader loader; |
|
85 |
|
86 /* applet event ids */ |
|
87 public final static int APPLET_DISPOSE = 0; |
|
88 public final static int APPLET_LOAD = 1; |
|
89 public final static int APPLET_INIT = 2; |
|
90 public final static int APPLET_START = 3; |
|
91 public final static int APPLET_STOP = 4; |
|
92 public final static int APPLET_DESTROY = 5; |
|
93 public final static int APPLET_QUIT = 6; |
|
94 public final static int APPLET_ERROR = 7; |
|
95 |
|
96 /* send to the parent to force relayout */ |
|
97 public final static int APPLET_RESIZE = 51234; |
|
98 |
|
99 /* sent to a (distant) parent to indicate that the applet is being |
|
100 * loaded or as completed loading |
|
101 */ |
|
102 public final static int APPLET_LOADING = 51235; |
|
103 public final static int APPLET_LOADING_COMPLETED = 51236; |
|
104 |
|
105 /** |
|
106 * The current status. One of: |
|
107 * APPLET_DISPOSE, |
|
108 * APPLET_LOAD, |
|
109 * APPLET_INIT, |
|
110 * APPLET_START, |
|
111 * APPLET_STOP, |
|
112 * APPLET_DESTROY, |
|
113 * APPLET_ERROR. |
|
114 */ |
|
115 protected int status; |
|
116 |
|
117 /** |
|
118 * The thread for the applet. |
|
119 */ |
|
120 Thread handler; |
|
121 |
|
122 |
|
123 /** |
|
124 * The initial applet size. |
|
125 */ |
|
126 Dimension defaultAppletSize = new Dimension(10, 10); |
|
127 |
|
128 /** |
|
129 * The current applet size. |
|
130 */ |
|
131 Dimension currentAppletSize = new Dimension(10, 10); |
|
132 |
|
133 MessageUtils mu = new MessageUtils(); |
|
134 |
|
135 /** |
|
136 * The thread to use during applet loading |
|
137 */ |
|
138 |
|
139 Thread loaderThread = null; |
|
140 |
|
141 /** |
|
142 * Flag to indicate that a loading has been cancelled |
|
143 */ |
|
144 boolean loadAbortRequest = false; |
|
145 |
|
146 /* abstract classes */ |
|
147 abstract protected String getCode(); |
|
148 abstract protected String getJarFiles(); |
|
149 abstract protected String getSerializedObject(); |
|
150 |
|
151 abstract public int getWidth(); |
|
152 abstract public int getHeight(); |
|
153 abstract public boolean hasInitialFocus(); |
|
154 |
|
155 private static int threadGroupNumber = 0; |
|
156 |
|
157 protected void setupAppletAppContext() { |
|
158 // do nothing |
|
159 } |
|
160 |
|
161 /* |
|
162 * Creates a thread to run the applet. This method is called |
|
163 * each time an applet is loaded and reloaded. |
|
164 */ |
|
165 synchronized void createAppletThread() { |
|
166 // Create a thread group for the applet, and start a new |
|
167 // thread to load the applet. |
|
168 String nm = "applet-" + getCode(); |
|
169 loader = getClassLoader(getCodeBase(), getClassLoaderCacheKey()); |
|
170 loader.grab(); // Keep this puppy around! |
|
171 |
|
172 // 4668479: Option to turn off codebase lookup in AppletClassLoader |
|
173 // during resource requests. [stanley.ho] |
|
174 String param = getParameter("codebase_lookup"); |
|
175 |
|
176 if (param != null && param.equals("false")) |
|
177 loader.setCodebaseLookup(false); |
|
178 else |
|
179 loader.setCodebaseLookup(true); |
|
180 |
|
181 |
|
182 ThreadGroup appletGroup = loader.getThreadGroup(); |
|
183 |
|
184 handler = new Thread(appletGroup, this, "thread " + nm); |
|
185 // set the context class loader for this thread |
|
186 AccessController.doPrivileged(new PrivilegedAction() { |
|
187 public Object run() { |
|
188 handler.setContextClassLoader(loader); |
|
189 return null; |
|
190 } |
|
191 }); |
|
192 handler.start(); |
|
193 } |
|
194 |
|
195 void joinAppletThread() throws InterruptedException { |
|
196 if (handler != null) { |
|
197 handler.join(); |
|
198 handler = null; |
|
199 } |
|
200 } |
|
201 |
|
202 void release() { |
|
203 if (loader != null) { |
|
204 loader.release(); |
|
205 loader = null; |
|
206 } |
|
207 } |
|
208 |
|
209 /** |
|
210 * Construct an applet viewer and start the applet. |
|
211 */ |
|
212 public void init() { |
|
213 try { |
|
214 // Get the width (if any) |
|
215 defaultAppletSize.width = getWidth(); |
|
216 currentAppletSize.width = defaultAppletSize.width; |
|
217 |
|
218 // Get the height (if any) |
|
219 defaultAppletSize.height = getHeight(); |
|
220 currentAppletSize.height = defaultAppletSize.height; |
|
221 |
|
222 } catch (NumberFormatException e) { |
|
223 // Turn on the error flag and let TagAppletPanel |
|
224 // do the right thing. |
|
225 status = APPLET_ERROR; |
|
226 showAppletStatus("badattribute.exception"); |
|
227 showAppletLog("badattribute.exception"); |
|
228 showAppletException(e); |
|
229 } |
|
230 |
|
231 setLayout(new BorderLayout()); |
|
232 |
|
233 createAppletThread(); |
|
234 } |
|
235 |
|
236 /** |
|
237 * Minimum size |
|
238 */ |
|
239 public Dimension minimumSize() { |
|
240 return new Dimension(defaultAppletSize.width, |
|
241 defaultAppletSize.height); |
|
242 } |
|
243 |
|
244 /** |
|
245 * Preferred size |
|
246 */ |
|
247 public Dimension preferredSize() { |
|
248 return new Dimension(currentAppletSize.width, |
|
249 currentAppletSize.height); |
|
250 } |
|
251 |
|
252 private AppletListener listeners; |
|
253 |
|
254 /** |
|
255 * AppletEvent Queue |
|
256 */ |
|
257 private Queue queue = null; |
|
258 |
|
259 |
|
260 synchronized public void addAppletListener(AppletListener l) { |
|
261 listeners = AppletEventMulticaster.add(listeners, l); |
|
262 } |
|
263 |
|
264 synchronized public void removeAppletListener(AppletListener l) { |
|
265 listeners = AppletEventMulticaster.remove(listeners, l); |
|
266 } |
|
267 |
|
268 /** |
|
269 * Dispatch event to the listeners.. |
|
270 */ |
|
271 public void dispatchAppletEvent(int id, Object argument) { |
|
272 //System.out.println("SEND= " + id); |
|
273 if (listeners != null) { |
|
274 AppletEvent evt = new AppletEvent(this, id, argument); |
|
275 listeners.appletStateChanged(evt); |
|
276 } |
|
277 } |
|
278 |
|
279 /** |
|
280 * Send an event. Queue it for execution by the handler thread. |
|
281 */ |
|
282 public void sendEvent(int id) { |
|
283 synchronized(this) { |
|
284 if (queue == null) { |
|
285 //System.out.println("SEND0= " + id); |
|
286 queue = new Queue(); |
|
287 } |
|
288 Integer eventId = new Integer(id); |
|
289 queue.enqueue(eventId); |
|
290 notifyAll(); |
|
291 } |
|
292 if (id == APPLET_QUIT) { |
|
293 try { |
|
294 joinAppletThread(); // Let the applet event handler exit |
|
295 } catch (InterruptedException e) { |
|
296 } |
|
297 |
|
298 // AppletClassLoader.release() must be called by a Thread |
|
299 // not within the applet's ThreadGroup |
|
300 if (loader == null) |
|
301 loader = getClassLoader(getCodeBase(), getClassLoaderCacheKey()); |
|
302 release(); |
|
303 } |
|
304 } |
|
305 |
|
306 /** |
|
307 * Get an event from the queue. |
|
308 */ |
|
309 synchronized AppletEvent getNextEvent() throws InterruptedException { |
|
310 while (queue == null || queue.isEmpty()) { |
|
311 wait(); |
|
312 } |
|
313 Integer eventId = (Integer)queue.dequeue(); |
|
314 return new AppletEvent(this, eventId.intValue(), null); |
|
315 } |
|
316 |
|
317 boolean emptyEventQueue() { |
|
318 if ((queue == null) || (queue.isEmpty())) |
|
319 return true; |
|
320 else |
|
321 return false; |
|
322 } |
|
323 |
|
324 /** |
|
325 * This kludge is specific to get over AccessControlException thrown during |
|
326 * Applet.stop() or destroy() when static thread is suspended. Set a flag |
|
327 * in AppletClassLoader to indicate that an |
|
328 * AccessControlException for RuntimePermission "modifyThread" or |
|
329 * "modifyThreadGroup" had occurred. |
|
330 */ |
|
331 private void setExceptionStatus(AccessControlException e) { |
|
332 Permission p = e.getPermission(); |
|
333 if (p instanceof RuntimePermission) { |
|
334 if (p.getName().startsWith("modifyThread")) { |
|
335 if (loader == null) |
|
336 loader = getClassLoader(getCodeBase(), getClassLoaderCacheKey()); |
|
337 loader.setExceptionStatus(); |
|
338 } |
|
339 } |
|
340 } |
|
341 |
|
342 /** |
|
343 * Execute applet events. |
|
344 * Here is the state transition diagram |
|
345 * |
|
346 * Note: (XXX) is the action |
|
347 * APPLET_XXX is the state |
|
348 * (applet code loaded) --> APPLET_LOAD -- (applet init called)--> APPLET_INIT -- ( |
|
349 * applet start called) --> APPLET_START -- (applet stop called) -->APPLET_STOP --(applet |
|
350 * destroyed called) --> APPLET_DESTROY -->(applet gets disposed) --> |
|
351 * APPLET_DISPOSE -->.... |
|
352 * |
|
353 * In the legacy lifecycle model. The applet gets loaded, inited and started. So it stays |
|
354 * in the APPLET_START state unless the applet goes away(refresh page or leave the page). |
|
355 * So the applet stop method called and the applet enters APPLET_STOP state. Then if the applet |
|
356 * is revisited, it will call applet start method and enter the APPLET_START state and stay there. |
|
357 * |
|
358 * In the modern lifecycle model. When the applet first time visited, it is same as legacy lifecycle |
|
359 * model. However, when the applet page goes away. It calls applet stop method and enters APPLET_STOP |
|
360 * state and then applet destroyed method gets called and enters APPLET_DESTROY state. |
|
361 * |
|
362 * This code is also called by AppletViewer. In AppletViewer "Restart" menu, the applet is jump from |
|
363 * APPLET_STOP to APPLET_DESTROY and to APPLET_INIT . |
|
364 * |
|
365 * Also, the applet can jump from APPLET_INIT state to APPLET_DESTROY (in Netscape/Mozilla case). |
|
366 * Same as APPLET_LOAD to |
|
367 * APPLET_DISPOSE since all of this are triggered by browser. |
|
368 * |
|
369 * |
|
370 */ |
|
371 public void run() { |
|
372 |
|
373 Thread curThread = Thread.currentThread(); |
|
374 if (curThread == loaderThread) { |
|
375 // if we are in the loader thread, cause |
|
376 // loading to occur. We may exit this with |
|
377 // status being APPLET_DISPOSE, APPLET_ERROR, |
|
378 // or APPLET_LOAD |
|
379 runLoader(); |
|
380 return; |
|
381 } |
|
382 |
|
383 boolean disposed = false; |
|
384 while (!disposed && !curThread.isInterrupted()) { |
|
385 AppletEvent evt; |
|
386 try { |
|
387 evt = getNextEvent(); |
|
388 } catch (InterruptedException e) { |
|
389 showAppletStatus("bail"); |
|
390 return; |
|
391 } |
|
392 |
|
393 //showAppletStatus("EVENT = " + evt.getID()); |
|
394 try { |
|
395 switch (evt.getID()) { |
|
396 case APPLET_LOAD: |
|
397 if (!okToLoad()) { |
|
398 break; |
|
399 } |
|
400 // This complexity allows loading of applets to be |
|
401 // interruptable. The actual thread loading runs |
|
402 // in a separate thread, so it can be interrupted |
|
403 // without harming the applet thread. |
|
404 // So that we don't have to worry about |
|
405 // concurrency issues, the main applet thread waits |
|
406 // until the loader thread terminates. |
|
407 // (one way or another). |
|
408 if (loaderThread == null) { |
|
409 // REMIND: do we want a name? |
|
410 //System.out.println("------------------- loading applet"); |
|
411 setLoaderThread(new Thread(this)); |
|
412 loaderThread.start(); |
|
413 // we get to go to sleep while this runs |
|
414 loaderThread.join(); |
|
415 setLoaderThread(null); |
|
416 } else { |
|
417 // REMIND: issue an error -- this case should never |
|
418 // occur. |
|
419 } |
|
420 break; |
|
421 |
|
422 case APPLET_INIT: |
|
423 // AppletViewer "Restart" will jump from destroy method to |
|
424 // init, that is why we need to check status w/ APPLET_DESTROY |
|
425 if (status != APPLET_LOAD && status != APPLET_DESTROY) { |
|
426 showAppletStatus("notloaded"); |
|
427 break; |
|
428 } |
|
429 applet.resize(defaultAppletSize); |
|
430 if (doInit) { |
|
431 if (PerformanceLogger.loggingEnabled()) { |
|
432 PerformanceLogger.setTime("Applet Init"); |
|
433 PerformanceLogger.outputLog(); |
|
434 } |
|
435 applet.init(); |
|
436 } |
|
437 |
|
438 //Need the default(fallback) font to be created in this AppContext |
|
439 Font f = getFont(); |
|
440 if (f == null || |
|
441 "dialog".equals(f.getFamily().toLowerCase(Locale.ENGLISH)) && |
|
442 f.getSize() == 12 && f.getStyle() == Font.PLAIN) { |
|
443 setFont(new Font(Font.DIALOG, Font.PLAIN, 12)); |
|
444 } |
|
445 |
|
446 doInit = true; // allow restarts |
|
447 |
|
448 // Validate the applet in event dispatch thread |
|
449 // to avoid deadlock. |
|
450 try { |
|
451 final AppletPanel p = this; |
|
452 |
|
453 SwingUtilities.invokeAndWait(new Runnable() { |
|
454 public void run() { |
|
455 p.validate(); |
|
456 } |
|
457 }); |
|
458 } |
|
459 catch(InterruptedException ie) { |
|
460 } |
|
461 catch(InvocationTargetException ite) { |
|
462 } |
|
463 |
|
464 status = APPLET_INIT; |
|
465 showAppletStatus("inited"); |
|
466 break; |
|
467 |
|
468 case APPLET_START: |
|
469 { |
|
470 if (status != APPLET_INIT && status != APPLET_STOP) { |
|
471 showAppletStatus("notinited"); |
|
472 break; |
|
473 } |
|
474 applet.resize(currentAppletSize); |
|
475 applet.start(); |
|
476 |
|
477 // Validate and show the applet in event dispatch thread |
|
478 // to avoid deadlock. |
|
479 try { |
|
480 final AppletPanel p = this; |
|
481 final Applet a = applet; |
|
482 |
|
483 SwingUtilities.invokeAndWait(new Runnable() { |
|
484 public void run() { |
|
485 p.validate(); |
|
486 a.setVisible(true); |
|
487 |
|
488 // Fix for BugTraq ID 4041703. |
|
489 // Set the default focus for an applet. |
|
490 if (hasInitialFocus()) |
|
491 setDefaultFocus(); |
|
492 } |
|
493 }); |
|
494 } |
|
495 catch(InterruptedException ie) { |
|
496 } |
|
497 catch(InvocationTargetException ite) { |
|
498 } |
|
499 |
|
500 status = APPLET_START; |
|
501 showAppletStatus("started"); |
|
502 break; |
|
503 } |
|
504 |
|
505 case APPLET_STOP: |
|
506 if (status != APPLET_START) { |
|
507 showAppletStatus("notstarted"); |
|
508 break; |
|
509 } |
|
510 status = APPLET_STOP; |
|
511 |
|
512 // Hide the applet in event dispatch thread |
|
513 // to avoid deadlock. |
|
514 try { |
|
515 final Applet a = applet; |
|
516 |
|
517 SwingUtilities.invokeAndWait(new Runnable() { |
|
518 public void run() |
|
519 { |
|
520 a.setVisible(false); |
|
521 } |
|
522 }); |
|
523 } |
|
524 catch(InterruptedException ie) { |
|
525 } |
|
526 catch(InvocationTargetException ite) { |
|
527 } |
|
528 |
|
529 |
|
530 // During Applet.stop(), any AccessControlException on an involved Class remains in |
|
531 // the "memory" of the AppletClassLoader. If the same instance of the ClassLoader is |
|
532 // reused, the same exception will occur during class loading. Set the AppletClassLoader's |
|
533 // exceptionStatusSet flag to allow recognition of what had happened |
|
534 // when reusing AppletClassLoader object. |
|
535 try { |
|
536 applet.stop(); |
|
537 } catch (java.security.AccessControlException e) { |
|
538 setExceptionStatus(e); |
|
539 // rethrow exception to be handled as it normally would be. |
|
540 throw e; |
|
541 } |
|
542 showAppletStatus("stopped"); |
|
543 break; |
|
544 |
|
545 case APPLET_DESTROY: |
|
546 if (status != APPLET_STOP && status != APPLET_INIT) { |
|
547 showAppletStatus("notstopped"); |
|
548 break; |
|
549 } |
|
550 status = APPLET_DESTROY; |
|
551 |
|
552 // During Applet.destroy(), any AccessControlException on an involved Class remains in |
|
553 // the "memory" of the AppletClassLoader. If the same instance of the ClassLoader is |
|
554 // reused, the same exception will occur during class loading. Set the AppletClassLoader's |
|
555 // exceptionStatusSet flag to allow recognition of what had happened |
|
556 // when reusing AppletClassLoader object. |
|
557 try { |
|
558 applet.destroy(); |
|
559 } catch (java.security.AccessControlException e) { |
|
560 setExceptionStatus(e); |
|
561 // rethrow exception to be handled as it normally would be. |
|
562 throw e; |
|
563 } |
|
564 showAppletStatus("destroyed"); |
|
565 break; |
|
566 |
|
567 case APPLET_DISPOSE: |
|
568 if (status != APPLET_DESTROY && status != APPLET_LOAD) { |
|
569 showAppletStatus("notdestroyed"); |
|
570 break; |
|
571 } |
|
572 status = APPLET_DISPOSE; |
|
573 |
|
574 try |
|
575 { |
|
576 final Applet a = applet; |
|
577 |
|
578 EventQueue.invokeAndWait(new Runnable() |
|
579 { |
|
580 public void run() |
|
581 { |
|
582 remove(a); |
|
583 } |
|
584 }); |
|
585 } |
|
586 catch(InterruptedException ie) |
|
587 { |
|
588 } |
|
589 catch(InvocationTargetException ite) |
|
590 { |
|
591 } |
|
592 applet = null; |
|
593 showAppletStatus("disposed"); |
|
594 disposed = true; |
|
595 break; |
|
596 |
|
597 case APPLET_QUIT: |
|
598 return; |
|
599 } |
|
600 } catch (Exception e) { |
|
601 status = APPLET_ERROR; |
|
602 if (e.getMessage() != null) { |
|
603 showAppletStatus("exception2", e.getClass().getName(), |
|
604 e.getMessage()); |
|
605 } else { |
|
606 showAppletStatus("exception", e.getClass().getName()); |
|
607 } |
|
608 showAppletException(e); |
|
609 } catch (ThreadDeath e) { |
|
610 showAppletStatus("death"); |
|
611 return; |
|
612 } catch (Error e) { |
|
613 status = APPLET_ERROR; |
|
614 if (e.getMessage() != null) { |
|
615 showAppletStatus("error2", e.getClass().getName(), |
|
616 e.getMessage()); |
|
617 } else { |
|
618 showAppletStatus("error", e.getClass().getName()); |
|
619 } |
|
620 showAppletException(e); |
|
621 } |
|
622 clearLoadAbortRequest(); |
|
623 } |
|
624 } |
|
625 |
|
626 /** |
|
627 * Gets most recent focus owner component associated with the given window. |
|
628 * It does that without calling Window.getMostRecentFocusOwner since it |
|
629 * provides its own logic contradicting with setDefautlFocus. Instead, it |
|
630 * calls KeyboardFocusManager directly. |
|
631 */ |
|
632 private Component getMostRecentFocusOwnerForWindow(Window w) { |
|
633 Method meth = (Method)AccessController.doPrivileged(new PrivilegedAction() { |
|
634 public Object run() { |
|
635 Method meth = null; |
|
636 try { |
|
637 meth = KeyboardFocusManager.class.getDeclaredMethod("getMostRecentFocusOwner", new Class[] {Window.class}); |
|
638 meth.setAccessible(true); |
|
639 } catch (Exception e) { |
|
640 // Must never happen |
|
641 e.printStackTrace(); |
|
642 } |
|
643 return meth; |
|
644 } |
|
645 }); |
|
646 if (meth != null) { |
|
647 // Meth refers static method |
|
648 try { |
|
649 return (Component)meth.invoke(null, new Object[] {w}); |
|
650 } catch (Exception e) { |
|
651 // Must never happen |
|
652 e.printStackTrace(); |
|
653 } |
|
654 } |
|
655 // Will get here if exception was thrown or meth is null |
|
656 return w.getMostRecentFocusOwner(); |
|
657 } |
|
658 |
|
659 /* |
|
660 * Fix for BugTraq ID 4041703. |
|
661 * Set the focus to a reasonable default for an Applet. |
|
662 */ |
|
663 private void setDefaultFocus() { |
|
664 Component toFocus = null; |
|
665 Container parent = getParent(); |
|
666 |
|
667 if(parent != null) { |
|
668 if (parent instanceof Window) { |
|
669 toFocus = getMostRecentFocusOwnerForWindow((Window)parent); |
|
670 if (toFocus == parent || toFocus == null) { |
|
671 toFocus = parent.getFocusTraversalPolicy(). |
|
672 getInitialComponent((Window)parent); |
|
673 } |
|
674 } else if (parent.isFocusCycleRoot()) { |
|
675 toFocus = parent.getFocusTraversalPolicy(). |
|
676 getDefaultComponent(parent); |
|
677 } |
|
678 } |
|
679 |
|
680 if (toFocus != null) { |
|
681 if (parent instanceof EmbeddedFrame) { |
|
682 ((EmbeddedFrame)parent).synthesizeWindowActivation(true); |
|
683 } |
|
684 // EmbeddedFrame might have focus before the applet was added. |
|
685 // Thus after its activation the most recent focus owner will be |
|
686 // restored. We need the applet's initial focusabled component to |
|
687 // be focused here. |
|
688 toFocus.requestFocusInWindow(); |
|
689 } |
|
690 } |
|
691 |
|
692 /** |
|
693 * Load the applet into memory. |
|
694 * Runs in a seperate (and interruptible) thread from the rest of the |
|
695 * applet event processing so that it can be gracefully interrupted from |
|
696 * things like HotJava. |
|
697 */ |
|
698 private void runLoader() { |
|
699 if (status != APPLET_DISPOSE) { |
|
700 showAppletStatus("notdisposed"); |
|
701 return; |
|
702 } |
|
703 |
|
704 dispatchAppletEvent(APPLET_LOADING, null); |
|
705 |
|
706 // REMIND -- might be cool to visually indicate loading here -- |
|
707 // maybe do animation? |
|
708 status = APPLET_LOAD; |
|
709 |
|
710 // Create a class loader |
|
711 loader = getClassLoader(getCodeBase(), getClassLoaderCacheKey()); |
|
712 |
|
713 // Load the archives if present. |
|
714 // REMIND - this probably should be done in a separate thread, |
|
715 // or at least the additional archives (epll). |
|
716 |
|
717 String code = getCode(); |
|
718 |
|
719 // setup applet AppContext |
|
720 // this must be called before loadJarFiles |
|
721 setupAppletAppContext(); |
|
722 |
|
723 try { |
|
724 loadJarFiles(loader); |
|
725 applet = createApplet(loader); |
|
726 } catch (ClassNotFoundException e) { |
|
727 status = APPLET_ERROR; |
|
728 showAppletStatus("notfound", code); |
|
729 showAppletLog("notfound", code); |
|
730 showAppletException(e); |
|
731 return; |
|
732 } catch (InstantiationException e) { |
|
733 status = APPLET_ERROR; |
|
734 showAppletStatus("nocreate", code); |
|
735 showAppletLog("nocreate", code); |
|
736 showAppletException(e); |
|
737 return; |
|
738 } catch (IllegalAccessException e) { |
|
739 status = APPLET_ERROR; |
|
740 showAppletStatus("noconstruct", code); |
|
741 showAppletLog("noconstruct", code); |
|
742 showAppletException(e); |
|
743 // sbb -- I added a return here |
|
744 return; |
|
745 } catch (Exception e) { |
|
746 status = APPLET_ERROR; |
|
747 showAppletStatus("exception", e.getMessage()); |
|
748 showAppletException(e); |
|
749 return; |
|
750 } catch (ThreadDeath e) { |
|
751 status = APPLET_ERROR; |
|
752 showAppletStatus("death"); |
|
753 return; |
|
754 } catch (Error e) { |
|
755 status = APPLET_ERROR; |
|
756 showAppletStatus("error", e.getMessage()); |
|
757 showAppletException(e); |
|
758 return; |
|
759 } finally { |
|
760 // notify that loading is no longer going on |
|
761 dispatchAppletEvent(APPLET_LOADING_COMPLETED, null); |
|
762 } |
|
763 |
|
764 // Fixed #4508194: NullPointerException thrown during |
|
765 // quick page switch |
|
766 // |
|
767 if (applet != null) |
|
768 { |
|
769 // Stick it in the frame |
|
770 applet.setStub(this); |
|
771 applet.hide(); |
|
772 add("Center", applet); |
|
773 showAppletStatus("loaded"); |
|
774 validate(); |
|
775 } |
|
776 } |
|
777 |
|
778 protected Applet createApplet(final AppletClassLoader loader) throws ClassNotFoundException, |
|
779 IllegalAccessException, IOException, InstantiationException, InterruptedException { |
|
780 final String serName = getSerializedObject(); |
|
781 String code = getCode(); |
|
782 |
|
783 if (code != null && serName != null) { |
|
784 System.err.println(amh.getMessage("runloader.err")); |
|
785 // return null; |
|
786 throw new InstantiationException("Either \"code\" or \"object\" should be specified, but not both."); |
|
787 } |
|
788 if (code == null && serName == null) { |
|
789 String msg = "nocode"; |
|
790 status = APPLET_ERROR; |
|
791 showAppletStatus(msg); |
|
792 showAppletLog(msg); |
|
793 repaint(); |
|
794 } |
|
795 if (code != null) { |
|
796 applet = (Applet)loader.loadCode(code).newInstance(); |
|
797 doInit = true; |
|
798 } else { |
|
799 // serName is not null; |
|
800 InputStream is = (InputStream) |
|
801 java.security.AccessController.doPrivileged( |
|
802 new java.security.PrivilegedAction() { |
|
803 public Object run() { |
|
804 return loader.getResourceAsStream(serName); |
|
805 } |
|
806 }); |
|
807 ObjectInputStream ois = |
|
808 new AppletObjectInputStream(is, loader); |
|
809 Object serObject = ois.readObject(); |
|
810 applet = (Applet) serObject; |
|
811 doInit = false; // skip over the first init |
|
812 } |
|
813 |
|
814 // Determine the JDK level that the applet targets. |
|
815 // This is critical for enabling certain backward |
|
816 // compatibility switch if an applet is a JDK 1.1 |
|
817 // applet. [stanley.ho] |
|
818 findAppletJDKLevel(applet); |
|
819 |
|
820 if (Thread.interrupted()) { |
|
821 try { |
|
822 status = APPLET_DISPOSE; // APPLET_ERROR? |
|
823 applet = null; |
|
824 // REMIND: This may not be exactly the right thing: the |
|
825 // status is set by the stop button and not necessarily |
|
826 // here. |
|
827 showAppletStatus("death"); |
|
828 } finally { |
|
829 Thread.currentThread().interrupt(); // resignal interrupt |
|
830 } |
|
831 return null; |
|
832 } |
|
833 return applet; |
|
834 } |
|
835 |
|
836 protected void loadJarFiles(AppletClassLoader loader) throws IOException, |
|
837 InterruptedException { |
|
838 // Load the archives if present. |
|
839 // REMIND - this probably should be done in a separate thread, |
|
840 // or at least the additional archives (epll). |
|
841 String jarFiles = getJarFiles(); |
|
842 |
|
843 if (jarFiles != null) { |
|
844 StringTokenizer st = new StringTokenizer(jarFiles, ",", false); |
|
845 while(st.hasMoreTokens()) { |
|
846 String tok = st.nextToken().trim(); |
|
847 try { |
|
848 loader.addJar(tok); |
|
849 } catch (IllegalArgumentException e) { |
|
850 // bad archive name |
|
851 continue; |
|
852 } |
|
853 } |
|
854 } |
|
855 } |
|
856 |
|
857 /** |
|
858 * Request that the loading of the applet be stopped. |
|
859 */ |
|
860 protected synchronized void stopLoading() { |
|
861 // REMIND: fill in the body |
|
862 if (loaderThread != null) { |
|
863 //System.out.println("Interrupting applet loader thread: " + loaderThread); |
|
864 loaderThread.interrupt(); |
|
865 } else { |
|
866 setLoadAbortRequest(); |
|
867 } |
|
868 } |
|
869 |
|
870 |
|
871 protected synchronized boolean okToLoad() { |
|
872 return !loadAbortRequest; |
|
873 } |
|
874 |
|
875 protected synchronized void clearLoadAbortRequest() { |
|
876 loadAbortRequest = false; |
|
877 } |
|
878 |
|
879 protected synchronized void setLoadAbortRequest() { |
|
880 loadAbortRequest = true; |
|
881 } |
|
882 |
|
883 |
|
884 private synchronized void setLoaderThread(Thread loaderThread) { |
|
885 this.loaderThread = loaderThread; |
|
886 } |
|
887 |
|
888 /** |
|
889 * Return true when the applet has been started. |
|
890 */ |
|
891 public boolean isActive() { |
|
892 return status == APPLET_START; |
|
893 } |
|
894 |
|
895 |
|
896 private EventQueue appEvtQ = null; |
|
897 /** |
|
898 * Is called when the applet wants to be resized. |
|
899 */ |
|
900 public void appletResize(int width, int height) { |
|
901 currentAppletSize.width = width; |
|
902 currentAppletSize.height = height; |
|
903 final Dimension currentSize = new Dimension(currentAppletSize.width, |
|
904 currentAppletSize.height); |
|
905 |
|
906 if(loader != null) { |
|
907 AppContext appCtxt = loader.getAppContext(); |
|
908 if(appCtxt != null) |
|
909 appEvtQ = (java.awt.EventQueue)appCtxt.get(AppContext.EVENT_QUEUE_KEY); |
|
910 } |
|
911 |
|
912 final AppletPanel ap = this; |
|
913 if (appEvtQ != null){ |
|
914 appEvtQ.postEvent(new InvocationEvent(Toolkit.getDefaultToolkit(), |
|
915 new Runnable(){ |
|
916 public void run(){ |
|
917 if(ap != null) |
|
918 { |
|
919 ap.dispatchAppletEvent(APPLET_RESIZE, currentSize); |
|
920 } |
|
921 } |
|
922 })); |
|
923 } |
|
924 } |
|
925 |
|
926 public void setBounds(int x, int y, int width, int height) { |
|
927 super.setBounds(x, y, width, height); |
|
928 currentAppletSize.width = width; |
|
929 currentAppletSize.height = height; |
|
930 } |
|
931 |
|
932 public Applet getApplet() { |
|
933 return applet; |
|
934 } |
|
935 |
|
936 /** |
|
937 * Status line. Called by the AppletPanel to provide |
|
938 * feedback on the Applet's state. |
|
939 */ |
|
940 protected void showAppletStatus(String status) { |
|
941 getAppletContext().showStatus(amh.getMessage(status)); |
|
942 } |
|
943 |
|
944 protected void showAppletStatus(String status, Object arg) { |
|
945 getAppletContext().showStatus(amh.getMessage(status, arg)); |
|
946 } |
|
947 protected void showAppletStatus(String status, Object arg1, Object arg2) { |
|
948 getAppletContext().showStatus(amh.getMessage(status, arg1, arg2)); |
|
949 } |
|
950 |
|
951 /** |
|
952 * Called by the AppletPanel to print to the log. |
|
953 */ |
|
954 protected void showAppletLog(String msg) { |
|
955 System.out.println(amh.getMessage(msg)); |
|
956 } |
|
957 |
|
958 protected void showAppletLog(String msg, Object arg) { |
|
959 System.out.println(amh.getMessage(msg, arg)); |
|
960 } |
|
961 |
|
962 /** |
|
963 * Called by the AppletPanel to provide |
|
964 * feedback when an exception has happened. |
|
965 */ |
|
966 protected void showAppletException(Throwable t) { |
|
967 t.printStackTrace(); |
|
968 repaint(); |
|
969 } |
|
970 |
|
971 /** |
|
972 * Get caching key for classloader cache |
|
973 */ |
|
974 public String getClassLoaderCacheKey() |
|
975 { |
|
976 /** |
|
977 * Fixed #4501142: Classlaoder sharing policy doesn't |
|
978 * take "archive" into account. This will be overridden |
|
979 * by Java Plug-in. [stanleyh] |
|
980 */ |
|
981 return getCodeBase().toString(); |
|
982 } |
|
983 |
|
984 /** |
|
985 * The class loaders |
|
986 */ |
|
987 private static HashMap classloaders = new HashMap(); |
|
988 |
|
989 /** |
|
990 * Flush a class loader. |
|
991 */ |
|
992 public static synchronized void flushClassLoader(String key) { |
|
993 classloaders.remove(key); |
|
994 } |
|
995 |
|
996 /** |
|
997 * Flush all class loaders. |
|
998 */ |
|
999 public static synchronized void flushClassLoaders() { |
|
1000 classloaders = new HashMap(); |
|
1001 } |
|
1002 |
|
1003 /** |
|
1004 * This method actually creates an AppletClassLoader. |
|
1005 * |
|
1006 * It can be override by subclasses (such as the Plug-in) |
|
1007 * to provide different classloaders. |
|
1008 */ |
|
1009 protected AppletClassLoader createClassLoader(final URL codebase) { |
|
1010 return new AppletClassLoader(codebase); |
|
1011 } |
|
1012 |
|
1013 /** |
|
1014 * Get a class loader. Create in a restricted context |
|
1015 */ |
|
1016 synchronized AppletClassLoader getClassLoader(final URL codebase, final String key) { |
|
1017 AppletClassLoader c = (AppletClassLoader)classloaders.get(key); |
|
1018 if (c == null) { |
|
1019 AccessControlContext acc = |
|
1020 getAccessControlContext(codebase); |
|
1021 c = (AppletClassLoader) |
|
1022 AccessController.doPrivileged(new PrivilegedAction() { |
|
1023 public Object run() { |
|
1024 AppletClassLoader ac = createClassLoader(codebase); |
|
1025 /* Should the creation of the classloader be |
|
1026 * within the class synchronized block? Since |
|
1027 * this class is used by the plugin, take care |
|
1028 * to avoid deadlocks, or specialize |
|
1029 * AppletPanel within the plugin. It may take |
|
1030 * an arbitrary amount of time to create a |
|
1031 * class loader (involving getting Jar files |
|
1032 * etc.) and may block unrelated applets from |
|
1033 * finishing createAppletThread (due to the |
|
1034 * class synchronization). If |
|
1035 * createAppletThread does not finish quickly, |
|
1036 * the applet cannot process other messages, |
|
1037 * particularly messages such as destroy |
|
1038 * (which timeout when called from the browser). |
|
1039 */ |
|
1040 synchronized (getClass()) { |
|
1041 AppletClassLoader res = |
|
1042 (AppletClassLoader)classloaders.get(key); |
|
1043 if (res == null) { |
|
1044 classloaders.put(key, ac); |
|
1045 return ac; |
|
1046 } else { |
|
1047 return res; |
|
1048 } |
|
1049 } |
|
1050 } |
|
1051 },acc); |
|
1052 } |
|
1053 return c; |
|
1054 } |
|
1055 |
|
1056 /** |
|
1057 * get the context for the AppletClassLoader we are creating. |
|
1058 * the context is granted permission to create the class loader, |
|
1059 * connnect to the codebase, and whatever else the policy grants |
|
1060 * to all codebases. |
|
1061 */ |
|
1062 private AccessControlContext getAccessControlContext(final URL codebase) { |
|
1063 |
|
1064 PermissionCollection perms = (PermissionCollection) |
|
1065 AccessController.doPrivileged(new PrivilegedAction() { |
|
1066 public Object run() { |
|
1067 Policy p = java.security.Policy.getPolicy(); |
|
1068 if (p != null) { |
|
1069 return p.getPermissions(new CodeSource(null, |
|
1070 (java.security.cert.Certificate[]) null)); |
|
1071 } else { |
|
1072 return null; |
|
1073 } |
|
1074 } |
|
1075 }); |
|
1076 |
|
1077 if (perms == null) |
|
1078 perms = new Permissions(); |
|
1079 |
|
1080 //XXX: this is needed to be able to create the classloader itself! |
|
1081 |
|
1082 perms.add(SecurityConstants.CREATE_CLASSLOADER_PERMISSION); |
|
1083 |
|
1084 Permission p; |
|
1085 java.net.URLConnection urlConnection = null; |
|
1086 try { |
|
1087 urlConnection = codebase.openConnection(); |
|
1088 p = urlConnection.getPermission(); |
|
1089 } catch (java.io.IOException ioe) { |
|
1090 p = null; |
|
1091 } |
|
1092 |
|
1093 if (p != null) |
|
1094 perms.add(p); |
|
1095 |
|
1096 if (p instanceof FilePermission) { |
|
1097 |
|
1098 String path = p.getName(); |
|
1099 |
|
1100 int endIndex = path.lastIndexOf(File.separatorChar); |
|
1101 |
|
1102 if (endIndex != -1) { |
|
1103 path = path.substring(0, endIndex+1); |
|
1104 |
|
1105 if (path.endsWith(File.separator)) { |
|
1106 path += "-"; |
|
1107 } |
|
1108 perms.add(new FilePermission(path, |
|
1109 SecurityConstants.FILE_READ_ACTION)); |
|
1110 } |
|
1111 } else { |
|
1112 URL locUrl = codebase; |
|
1113 if (urlConnection instanceof JarURLConnection) { |
|
1114 locUrl = ((JarURLConnection)urlConnection).getJarFileURL(); |
|
1115 } |
|
1116 String host = locUrl.getHost(); |
|
1117 if (host != null && (host.length() > 0)) |
|
1118 perms.add(new SocketPermission(host, |
|
1119 SecurityConstants.SOCKET_CONNECT_ACCEPT_ACTION)); |
|
1120 } |
|
1121 |
|
1122 ProtectionDomain domain = |
|
1123 new ProtectionDomain(new CodeSource(codebase, |
|
1124 (java.security.cert.Certificate[]) null), perms); |
|
1125 AccessControlContext acc = |
|
1126 new AccessControlContext(new ProtectionDomain[] { domain }); |
|
1127 |
|
1128 return acc; |
|
1129 } |
|
1130 |
|
1131 public Thread getAppletHandlerThread() { |
|
1132 return handler; |
|
1133 } |
|
1134 |
|
1135 public int getAppletWidth() { |
|
1136 return currentAppletSize.width; |
|
1137 } |
|
1138 |
|
1139 public int getAppletHeight() { |
|
1140 return currentAppletSize.height; |
|
1141 } |
|
1142 |
|
1143 public static void changeFrameAppContext(Frame frame, AppContext newAppContext) |
|
1144 { |
|
1145 // Fixed #4754451: Applet can have methods running on main |
|
1146 // thread event queue. |
|
1147 // |
|
1148 // The cause of this bug is that the frame of the applet |
|
1149 // is created in main thread group. Thus, when certain |
|
1150 // AWT/Swing events are generated, the events will be |
|
1151 // dispatched through the wrong event dispatch thread. |
|
1152 // |
|
1153 // To fix this, we rearrange the AppContext with the frame, |
|
1154 // so the proper event queue will be looked up. |
|
1155 // |
|
1156 // Swing also maintains a Frame list for the AppContext, |
|
1157 // so we will have to rearrange it as well. |
|
1158 |
|
1159 // Check if frame's AppContext has already been set properly |
|
1160 AppContext oldAppContext = SunToolkit.targetToAppContext(frame); |
|
1161 |
|
1162 if (oldAppContext == newAppContext) |
|
1163 return; |
|
1164 |
|
1165 // Synchronization on Window.class is needed for locking the |
|
1166 // critical section of the window list in AppContext. |
|
1167 synchronized (Window.class) |
|
1168 { |
|
1169 WeakReference weakRef = null; |
|
1170 // Remove frame from the Window list in wrong AppContext |
|
1171 { |
|
1172 // Lookup current frame's AppContext |
|
1173 Vector<WeakReference<Window>> windowList = (Vector<WeakReference<Window>>)oldAppContext.get(Window.class); |
|
1174 if (windowList != null) { |
|
1175 for (WeakReference ref : windowList) { |
|
1176 if (ref.get() == frame) { |
|
1177 weakRef = ref; |
|
1178 break; |
|
1179 } |
|
1180 } |
|
1181 // Remove frame from wrong AppContext |
|
1182 if (weakRef != null) |
|
1183 windowList.remove(weakRef); |
|
1184 } |
|
1185 } |
|
1186 |
|
1187 // Put the frame into the applet's AppContext map |
|
1188 SunToolkit.insertTargetMapping(frame, newAppContext); |
|
1189 |
|
1190 // Insert frame into the Window list in the applet's AppContext map |
|
1191 { |
|
1192 Vector<WeakReference<Window>> windowList = (Vector)newAppContext.get(Window.class); |
|
1193 if (windowList == null) { |
|
1194 windowList = new Vector<WeakReference<Window>>(); |
|
1195 newAppContext.put(Window.class, windowList); |
|
1196 } |
|
1197 // use the same weakRef here as it is used elsewhere |
|
1198 windowList.add(weakRef); |
|
1199 } |
|
1200 } |
|
1201 } |
|
1202 |
|
1203 // Flag to indicate if applet is targeted for JDK 1.1. |
|
1204 private boolean jdk11Applet = false; |
|
1205 |
|
1206 // Flag to indicate if applet is targeted for JDK 1.2. |
|
1207 private boolean jdk12Applet = false; |
|
1208 |
|
1209 /** |
|
1210 * Determine JDK level of an applet. |
|
1211 */ |
|
1212 private void findAppletJDKLevel(Applet applet) |
|
1213 { |
|
1214 // To determine the JDK level of an applet, the |
|
1215 // most reliable way is to check the major version |
|
1216 // of the applet class file. |
|
1217 |
|
1218 // synchronized on applet class object, so calling from |
|
1219 // different instances of the same applet will be |
|
1220 // serialized. |
|
1221 Class appletClass = applet.getClass(); |
|
1222 |
|
1223 synchronized(appletClass) { |
|
1224 // Determine if the JDK level of an applet has been |
|
1225 // checked before. |
|
1226 Boolean jdk11Target = (Boolean) loader.isJDK11Target(appletClass); |
|
1227 Boolean jdk12Target = (Boolean) loader.isJDK12Target(appletClass); |
|
1228 |
|
1229 // if applet JDK level has been checked before, retrieve |
|
1230 // value and return. |
|
1231 if (jdk11Target != null || jdk12Target != null) { |
|
1232 jdk11Applet = (jdk11Target == null) ? false : jdk11Target.booleanValue(); |
|
1233 jdk12Applet = (jdk12Target == null) ? false : jdk12Target.booleanValue(); |
|
1234 return; |
|
1235 } |
|
1236 |
|
1237 String name = appletClass.getName(); |
|
1238 |
|
1239 // first convert any '.' to '/' |
|
1240 name = name.replace('.', '/'); |
|
1241 |
|
1242 // append .class |
|
1243 final String resourceName = name + ".class"; |
|
1244 |
|
1245 InputStream is = null; |
|
1246 byte[] classHeader = new byte[8]; |
|
1247 |
|
1248 try { |
|
1249 is = (InputStream) java.security.AccessController.doPrivileged( |
|
1250 new java.security.PrivilegedAction() { |
|
1251 public Object run() { |
|
1252 return loader.getResourceAsStream(resourceName); |
|
1253 } |
|
1254 }); |
|
1255 |
|
1256 // Read the first 8 bytes of the class file |
|
1257 int byteRead = is.read(classHeader, 0, 8); |
|
1258 is.close(); |
|
1259 |
|
1260 // return if the header is not read in entirely |
|
1261 // for some reasons. |
|
1262 if (byteRead != 8) |
|
1263 return; |
|
1264 } |
|
1265 catch (IOException e) { |
|
1266 return; |
|
1267 } |
|
1268 |
|
1269 // Check major version in class file header |
|
1270 int major_version = readShort(classHeader, 6); |
|
1271 |
|
1272 // Major version in class file is as follows: |
|
1273 // 45 - JDK 1.1 |
|
1274 // 46 - JDK 1.2 |
|
1275 // 47 - JDK 1.3 |
|
1276 // 48 - JDK 1.4 |
|
1277 // 49 - JDK 1.5 |
|
1278 if (major_version < 46) |
|
1279 jdk11Applet = true; |
|
1280 else if (major_version == 46) |
|
1281 jdk12Applet = true; |
|
1282 |
|
1283 // Store applet JDK level in AppContext for later lookup, |
|
1284 // e.g. page switch. |
|
1285 loader.setJDK11Target(appletClass, jdk11Applet); |
|
1286 loader.setJDK12Target(appletClass, jdk12Applet); |
|
1287 } |
|
1288 } |
|
1289 |
|
1290 /** |
|
1291 * Return true if applet is targeted to JDK 1.1. |
|
1292 */ |
|
1293 protected boolean isJDK11Applet() { |
|
1294 return jdk11Applet; |
|
1295 } |
|
1296 |
|
1297 /** |
|
1298 * Return true if applet is targeted to JDK1.2. |
|
1299 */ |
|
1300 protected boolean isJDK12Applet() { |
|
1301 return jdk12Applet; |
|
1302 } |
|
1303 |
|
1304 /** |
|
1305 * Read short from byte array. |
|
1306 */ |
|
1307 private int readShort(byte[] b, int off) { |
|
1308 int hi = readByte(b[off]); |
|
1309 int lo = readByte(b[off + 1]); |
|
1310 return (hi << 8) | lo; |
|
1311 } |
|
1312 |
|
1313 private int readByte(byte b) { |
|
1314 return ((int)b) & 0xFF; |
|
1315 } |
|
1316 |
|
1317 |
|
1318 private static AppletMessageHandler amh = new AppletMessageHandler("appletpanel"); |
|
1319 } |