1 // Copyright 2000-2005 the Contributors, as shown in the revision logs.
2 // Licensed under the GNU General Public License version 2 ("the License").
3 // You may not use this file except in compliance with the License.
5 package org.xwt.shoehorn3;
9 import java.lang.reflect.*;
12 import java.util.zip.*;
16 /** This class is XWT's presence on the user's computer; it must be run as trusted code */
17 public class ShoeHorn extends Applet {
19 // Startup Phase ////////////////////////////////////////////////////////////////////
20 private String build = null;
21 public ShoeHorn() { log("*** constructor invoked for " + this.getClass().getName()); }
23 public final String getParameter(String arg) { return super.getParameter(arg); }
24 public final void main(String[] s) { new ShoeHorn().start(); }
25 private void log(String s) { System.out.println(s); }
27 /** this just ensures that we are running with full privileges */
28 public final void start() {
29 build = getParameter("build");
30 new Thread() { public void run() {
31 log("ShoeHorn thread spawned");
34 if (System.getProperty("java.vendor", "").startsWith("Netscape")) {
35 log("Detected Navigator 4.x");
36 Method m = Class.forName("netscape.security.PrivilegeManager").getMethod("enablePrivilege", new Class[] { String.class });
37 m.invoke(null, new Object[] { "MarimbaInternalTarget" });
38 m.invoke(null, new Object[] { "UniversalExecAccess" });
39 m.invoke(null, new Object[] { "UniversalPropertyRead" });
42 } else if (System.getProperty("java.vendor", "").startsWith("Microsoft")) {
43 //com.ms.security.PolicyEngine.assertPermission(com.ms.security.PermissionID.SYSTEM);
44 Class permissionIdClass = Class.forName("com.ms.security.PermissionID");
45 Object o = permissionIdClass.getField("SYSTEM").get(null);
46 Method m = Class.forName("com.ms.security.PolicyEngine").getMethod("assertPermission", new Class[] { permissionIdClass });
47 m.invoke(null, new Object[] { o });
51 log("Detected non-Navigator JVM");
52 Method m = Class.forName("org.xwt.shoehorn3.ShoeHorn$Java12").getMethod("run", new Class[] { Object.class });
53 m.invoke(null, new Object[] { ShoeHorn.this });
55 } catch (Throwable e) {
56 if (e instanceof InvocationTargetException) e = ((InvocationTargetException)e).getTargetException();
58 update(-1.0, "Error; please check the Java console");
63 /** ask Java Plugin for privs */
64 private static class Java12 {
65 public static void run(final Object a) {
66 java.security.AccessController.doPrivileged(new java.security.PrivilegedAction() {
76 // Implantation ////////////////////////////////////////////////////////////////////
78 /** inserts the required entries into the user's ~/.java.policy */
79 private void modifyPolicyFile() throws IOException {
80 log("Adjusting ~/.java.policy");
81 File policy = new File(System.getProperty("user.home") + File.separatorChar + ".java.policy");
82 if (policy.exists()) {
83 BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(policy)));
85 while((s = br.readLine()) != null)
86 if (s.startsWith("// XWT_MARKER:")) {
87 log("Java policy file has already been adjusted");
91 FileOutputStream fos = new FileOutputStream(policy.getAbsolutePath(), true);
92 PrintWriter pw = new PrintWriter(new OutputStreamWriter(fos));
94 pw.println("// XWT_MARKER: this line and the following two grant blocks are required for XWT; DO NOT REMOVE THEM.");
95 pw.println("grant {");
96 pw.println(" permission java.io.FilePermission \"${user.home}${/}.xwt${/}shoehorn.jar\", \"read\";");
98 pw.println("grant codebase \"file:${user.home}${/}.xwt${/}shoehorn.jar\" {");
99 pw.println(" permission java.security.AllPermission;");
101 pw.println("// END_XWT_MARKER");
107 /** read ourselves out of the resources and write a jarfile to some trusted place */
108 private void implantSelf() throws IOException {
109 InputStream manifest = getClass().getClassLoader().getResourceAsStream("META-INF/manifest.mf");
110 log("my classloader is " + getClass().getClassLoader().getClass().getName());
111 ClassLoader loader = getClass().getClassLoader();
112 if (manifest == null || loader == null ||
113 (loader.getClass().getName().indexOf("Applet") == -1 && loader.getClass().getName().indexOf("Plugin") == -1))
115 BufferedReader br = new BufferedReader(new InputStreamReader(manifest));
116 Vector entries = new Vector();
118 while((s = br.readLine()) != null)
119 if (s.startsWith("Name: "))
120 entries.addElement(s.substring(6));
122 String ext_dirs = System.getProperty("java.ext.dirs");
123 log("java.ext.dirs = " + ext_dirs);
124 ext_dirs = ext_dirs + File.pathSeparatorChar + System.getProperty("user.home") + File.separatorChar + ".xwt";
125 StringTokenizer st = new StringTokenizer(ext_dirs, File.pathSeparatorChar + "");
126 while(st.hasMoreTokens()) {
127 String dir = st.nextToken();
128 new File(dir).mkdirs();
130 // we have to modify the policy file BEFORE implanting in ~/.xwt to ensure that the policy mods work
131 if (!st.hasMoreTokens()) modifyPolicyFile();
132 implantInDirectory(dir, entries);
134 } catch (IOException e) {
135 log("Failed to implant in " + dir + " due to " + e);
138 log("Failed to implant self!");
141 private void implantInDirectory(String dir, Vector entries) throws IOException {
142 File f = new File(dir + File.separatorChar + "shoehorn.tmp");
143 ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(f));
144 for(int i=0; i<entries.size(); i++) {
145 zos.putNextEntry(new ZipEntry(entries.elementAt(i).toString()));
146 InputStream is = this.getClass().getClassLoader().getResourceAsStream(entries.elementAt(i).toString());
147 byte[] buf = new byte[1024];
149 int read = is.read(buf, 0, buf.length);
150 if (read == -1) break;
151 zos.write(buf, 0, read);
156 f.renameTo(new File(dir + File.separatorChar + "shoehorn.jar"));
157 log("Succeeded in implanting in " + dir);
161 // Startup Phase ////////////////////////////////////////////////////////////////////
163 public final void go() {
169 String os_name = System.getProperty("os.name", "");
170 log("os.name == " + os_name);
171 Vector command = new Vector();
174 if (os_name.indexOf("Linux") != -1) {
175 arch = new BufferedReader(new InputStreamReader(Runtime.getRuntime().exec("/bin/uname -m").getInputStream())).readLine();
176 log("arch is " + arch);
179 if (os_name.indexOf("Linux") != -1 && arch.charAt(0) == 'i' && arch.substring(2, 4).equals("86")) {
180 file = fetch("xwt-" + build + ".linux.gz");
181 safeWaitFor(Runtime.getRuntime().exec("/bin/chmod +x " + file.getAbsolutePath()));
182 command.addElement(file.getAbsolutePath());
184 } else if (os_name.indexOf("Windows") != -1) {
185 file = fetch("xwt-" + build + ".cab");
186 command.addElement(file.getAbsolutePath());
189 file = fetch("xwt-" + build + ".jar");
190 command.addElement(findJvmBinary());
191 command.addElement("-jar");
192 command.addElement(file.getAbsolutePath());
195 if ("true".equals(getParameter("showrenders"))) command.addElement("-s");
196 if ("true".equals(getParameter("verbose"))) command.addElement("-v");
197 if (getParameter("log") != null) {
198 command.addElement("-l");
199 command.addElement(getParameter("log"));
201 command.addElement(getParameter("xwar"));
203 } catch (Exception e) {
204 update(-1.0, "Error; please check the Java console");
209 /** searches for the JVM binary in the usual places */
210 private String findJvmBinary() throws IOException {
211 String jvmBinary = null;
213 // HACK: prefer jdk1.3 on OSX; it has better fonts
214 if (new File("/System/Library/Frameworks/JavaVM.framework/Versions/1.3.1/Commands/java").exists())
215 jvmBinary = "/System/Library/Frameworks/JavaVM.framework/Versions/1.3.1/Commands/java";
218 String javaHome = getEnv("JAVA_HOME");
219 if (jvmBinary == null && javaHome != null && !javaHome.equals("")) {
220 jvmBinary = javaHome + File.separatorChar + "bin" + File.separatorChar + "java";
221 if (!new File(jvmBinary).exists()) jvmBinary = null;
224 // check common locations
225 if (jvmBinary == null)
226 for(int i=0; i<commonJavaLocations.length; i++)
227 if (new File(commonJavaLocations[i]).exists()) {
228 jvmBinary = commonJavaLocations[i];
233 if (jvmBinary == null) {
234 String path = getEnv("PATH");
235 StringTokenizer st = new StringTokenizer(path, File.pathSeparatorChar + "");
236 while(st.hasMoreTokens()) {
237 String s = st.nextToken();
238 if (new File(s + File.separatorChar + "java").exists()) {
239 jvmBinary = s + File.separatorChar + "java";
245 // check ${java.home}
246 javaHome = System.getProperty("java.home");
247 if (jvmBinary == null && javaHome != null && !javaHome.equals("")) {
248 jvmBinary = javaHome + File.separatorChar + "bin" + File.separatorChar + "java";
249 if (!new File(jvmBinary).exists()) jvmBinary = null;
252 if (jvmBinary == null)
253 throw new Error("couldn't find JVM binary! JAVA_HOME=" + getEnv("JAVA_HOME") + " PATH=" + getEnv("PATH"));
258 private void spawn(Vector command) throws IOException {
259 String proxy = detectProxy();
260 log("proxy settings: " + proxy);
262 String[] command_vec;
263 command.copyInto(command_vec = new String[command.size()]);
265 for(int i=0; i<command_vec.length; i++) log(" \"" + command_vec[i] + "\"");
269 p = Runtime.getRuntime().exec(command_vec);
270 } else if (System.getProperty("os.name").toLowerCase().indexOf("windows") != -1) {
271 // hrm, win32 doesn't like it when we probe the environment; that doesn't matter, since the
272 // win32 environ vars are pretty much useless anyways.
273 p = Runtime.getRuntime().exec(command_vec, new String[] { proxy });
275 p = Runtime.getRuntime().exec(command_vec, dumpEnv(proxy));
277 BufferedReader stderr = new BufferedReader(new InputStreamReader(p.getErrorStream()));
279 if (System.getProperty("os.name").toLowerCase().indexOf("windows") != -1) {
280 update(1.0, "XWT Loaded");
282 String s = stderr.readLine();
283 update(1.0, "XWT Loaded");
286 s = stderr.readLine();
293 // Utility Functions /////////////////////////////////////////////////////////////////////
295 /** Voodoo to extract the proxy settings from Sun's Java Plugin */
296 private String detectProxy() {
298 Vector ret = new Vector();
300 Class PluginProxyHandler = Class.forName("sun.plugin.protocol.PluginProxyHandler");
301 Method getDefaultProxyHandler = PluginProxyHandler.getMethod("getDefaultProxyHandler", new Class[] { });
302 Object proxyHandler = getDefaultProxyHandler.invoke(null, new Object[] { });
304 Class ProxyHandler = Class.forName("sun.plugin.protocol.ProxyHandler");
305 Method getProxyInfo = ProxyHandler.getMethod("getProxyInfo", new Class[] { URL.class });
306 Object proxyInfo = getProxyInfo.invoke(proxyHandler, new Object[] { new URL("http://www.xwt.org") });
308 Class ProxyInfo = Class.forName("sun.plugin.protocol.ProxyInfo");
310 if (((Boolean)ProxyInfo.getMethod("isProxyUsed", new Class[] { }).invoke(proxyInfo, new Object[] { })).booleanValue())
311 return "http_proxy=" +
312 (String)ProxyInfo.getMethod("getProxy", new Class[] { }).invoke(proxyInfo, new Object[] { }) + ":" +
313 ((Integer)ProxyInfo.getMethod("getPort", new Class[] { }).invoke(proxyInfo, new Object[] { })).intValue();
315 if (((Boolean)ProxyInfo.getMethod("isSocksUsed", new Class[] { }).invoke(proxyInfo, new Object[] { })).booleanValue())
316 return "socks_proxy=" +
317 (String)ProxyInfo.getMethod("getSocksProxy", new Class[] { }).invoke(proxyInfo, new Object[] { }) + ":" +
318 ((Integer)ProxyInfo.getMethod("getSocksPort", new Class[] { }).invoke(proxyInfo, new Object[] { })).intValue();
322 } catch (Throwable e) {
323 log("exception while querying sun.plugin.protocol.PluginProxyHandler: " + e);
328 // The Netscape JVM has a bug which causes waitFor() to lock up, so we wait no more than 2000ms
329 private void safeWaitFor(final Process p) {
330 final Object o = new Object();
335 } catch (InterruptedException e) { }
336 synchronized(o) { o.notify(); }
340 synchronized(o) { o.wait(2000); }
341 } catch (InterruptedException e) { }
344 /** fetches a file from the distribution site, writing it to the appropriate place */
345 private File fetch(String filename) throws IOException {
346 String tmpdir = System.getProperty("user.home") + File.separatorChar + ".xwt";
347 new File(tmpdir).mkdirs();
348 URL u = new URL("http://dist.xwt.org/" + filename);
349 if (filename.endsWith(".gz")) filename = filename.substring(0, filename.length() - 3);
350 if (filename.endsWith(".cab")) filename = filename.substring(0, filename.length() - 4) + ".exe";
351 File target = new File(tmpdir + File.separatorChar + filename);
352 if (target.exists()) return target;
353 loadFromURL(u, target, filename);
357 /** loads a file from a url, verifying that it was properly signed */
358 private void loadFromURL(URL u, File target, String filename) throws IOException {
360 final URLConnection uc = u.openConnection();
361 final int signatureLength = 128;
362 final byte[] signature = new byte[signatureLength];
364 InputStream is = uc.getInputStream();
365 final int contentLength = uc.getContentLength();
367 // display the progress indicator as we go
368 InputStream dis = new FilterInputStream(is) {
370 public int read() throws IOException {
371 int ret = super.read();
372 if (ret != -1) total++;
373 double loaded = ((double)total) / ((double)uc.getContentLength());
374 update(loaded, "Loading XWT: " + ((int)Math.ceil(loaded * 100)) + "%");
377 public int read(byte[] buf, int off, int len) throws IOException {
378 int ret = super.read(buf, off, len);
379 if (ret != -1) total += ret;
380 double loaded = ((double)total) / ((double)uc.getContentLength());
381 update(loaded, "Loading XWT: " + ((int)Math.ceil(loaded * 100)) + "%");
386 // decompress if needed
387 if (u.toString().endsWith(".cab")) dis = CAB.getFileInputStream(dis, "xwt-" + build + ".exe");
388 else if (u.toString().endsWith(".gz")) dis = new GZIPInputStream(dis);
390 // digest and copy the file to target.tmp, omitting the last signatureLength bytes
391 SHA1Digest sha1 = new SHA1Digest();
392 OutputStream fos = new DigestOutputStream(new FileOutputStream(target + ".tmp"), sha1);
393 byte[] buf = new byte[1024 * 128];
396 while(numread < buf.length) {
397 int read = dis.read(buf, numread, buf.length - numread);
398 if (read == -1) break;
401 if (numread < buf.length) {
402 System.arraycopy(buf, numread - signatureLength, signature, 0, signatureLength);
403 fos.write(buf, 0, numread - signatureLength);
406 fos.write(buf, 0, buf.length - signatureLength);
407 System.arraycopy(buf, buf.length - signatureLength, buf, 0, signatureLength);
408 numread = signatureLength;
413 // construct our hash
414 byte[] hash = new byte[sha1.getDigestSize()];
415 sha1.doFinal(hash, 0);
417 // decrypt the signature hash
418 DERInputStream deris = new DERInputStream(new ByteArrayInputStream(Base64.decode(XWT_foundation_public_key)));
419 SubjectPublicKeyInfo pki = new SubjectPublicKeyInfo((DERConstructedSequence)deris.readObject());
420 RSAPublicKeyStructure rsa_pks = new RSAPublicKeyStructure((DERConstructedSequence)pki.getPublicKey());
421 BigInteger modulus = rsa_pks.getModulus();
422 BigInteger exponent = rsa_pks.getPublicExponent();
423 AsymmetricBlockCipher rsa = new PKCS1Encoding(new RSAEngine());
424 rsa.init(false, new RSAKeyParameters(false, modulus, exponent));
428 byte[] decryptedHash = rsa.processBlock(signature, 0, signature.length);
429 for(int i=0; i<hash.length; i++)
430 if (decryptedHash[i] != hash[i]) {
431 new File(target + ".tmp").delete();
432 throw new Error("invalid signature on " + filename);
434 } catch (InvalidCipherTextException e) {
435 new File(target + ".tmp").delete();
439 // good to go; rename the file
440 new File(target + ".tmp").renameTo(target);
443 /** retrieves an environment variable */
444 private static String getEnv(String key) throws IOException {
445 Process p = Runtime.getRuntime().exec(new String[] { "/bin/sh", "-c", "echo $" + key });
446 BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
447 return br.readLine();
450 /** dumps the environment plus an additional variable */
451 private static String[] dumpEnv(String newvar) throws IOException {
452 Vector v = new Vector();
453 String os = System.getProperty("os.name").toLowerCase();
455 if (os.indexOf("windows 9") > -1) {
456 p = Runtime.getRuntime().exec("command.com /c set");
457 } else if ((os.indexOf("nt") > -1) || (os.indexOf("windows 2000") > -1) ) {
458 p = Runtime.getRuntime().exec("cmd.exe /c set");
460 p = Runtime.getRuntime().exec("env");
462 BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
464 while ((s = br.readLine()) != null) v.addElement(s);
465 v.addElement(newvar);
467 v.copyInto(ret = new String[v.size()]);
473 // Applet Painting Functions ////////////////////////////////////////////////////////
475 private Image backbuffer = null;
476 public final void paint(Graphics g) { if (backbuffer != null) g.drawImage(backbuffer, 0, 0, null); }
478 public final Graphics getGraphics() { return super.getGraphics(); }
479 public final Dimension getSize() { return super.getSize(); }
481 private void update(double loaded, String text) {
482 // indicates we should paint ourselves
483 Graphics g2 = getGraphics();
485 if (backbuffer == null || backbuffer.getWidth(null) != getSize().width || backbuffer.getHeight(null) != getSize().height)
486 backbuffer = createImage(getSize().width, getSize().height);
487 if (backbuffer == null) return;
488 Graphics g = backbuffer.getGraphics();
490 g.setColor(loaded < 0 ? Color.red : Color.blue);
491 loaded = Math.abs(loaded);
493 int w = (int)((double)getSize().width * loaded);
494 g.fillRect(0, 0, w, getSize().height);
495 g.setColor(Color.darkGray);
496 g.fillRect(w, 0, getSize().width - w, getSize().height);
498 Font f = new Font("Sans-serif", Font.BOLD, 12);
499 FontMetrics fm = Toolkit.getDefaultToolkit().getFontMetrics(f);
502 int x = (getSize().width - fm.stringWidth(s)) / 2;
503 int y = ((getSize().height - fm.getMaxAscent() - fm.getMaxDescent()) / 2) + fm.getMaxAscent();
504 g.setColor(Color.white);
505 g.drawString(s, x, y);
508 g2.setClip(0, 0, getSize().width, getSize().height);
509 g2.drawImage(backbuffer, 0, 0, null);
514 // Misc Constants ///////////////////////////////////////////////////////////
516 private static final String XWT_foundation_public_key =
517 "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCoVweDZ+h3jcN2Qz1YwWJR8gVF0m/IE" +
518 "BasLIcvey9y1VkyN9jY6b9qm/Z2UoVtvAlgezd4CsJedUCIMe7uURyHGnqI4MLrozxLz3" +
519 "zqx5EYChsJt+Ju/f44KYnMx7upUQ4irfxOj6RpHy3E5GbW4XO96WwFlOuaR8+HRwKCXGP" +
522 private static final String[] commonJavaLocations = new String[] {
524 "/usr/java/bin/java",
525 "/usr/local/bin/java",
526 "/usr/local/java/bin/java",
527 "/usr/lib/j2sdk1.4/bin/java",
528 "/usr/lib/j2sdk1.3/bin/java",
529 "/usr/lib/j2sdk1.2/bin/java"
539 /// NSJVM Notes ///////////////////////////////////////////////////////////////////
541 PrivilegeManager.enablePrivilege("MarimbaInternalTarget");
542 PrivilegeManager.enablePrivilege("UniversalSystemClipboardAccess");
543 netscape.security.PrivilegeManager.enablePrivilege("UniversalConnect"); (sockets)
544 netscape.security.PrivilegeManager.enablePrivilege("UniversalSystemClipboardAccess");
545 netscape.security.PrivilegeManager.enablePrivilege("UniversalTopLevelWindow");
547 - Netscape's ClassLoader.getResource() is broken, see http://developer.netscape.com/docs/technote/java/getresource/getresource.html
548 - this will fix it: netscape.security.PrivilegeManager.enablePrivilege("UniversalPropertyRead");
550 - If you create a classloader, include
551 public boolean checkMatchPrincipalAlways(int i) {
552 return ((AppletClassLoader)this.getClass().getClassLoader()).checkMatchPrincipalAlways(i);
555 - protected int _modifiersToButtonNumber(int modifiers) {
556 if ((modifiers & (InputEvent.BUTTON1_MASK & 15)) == (InputEvent.BUTTON1_MASK & 15)) return 1;
557 if ((modifiers & (InputEvent.BUTTON2_MASK & 15)) == (InputEvent.BUTTON2_MASK & 15)) return 3;
558 if ((modifiers & (InputEvent.BUTTON3_MASK & 15)) == (InputEvent.BUTTON3_MASK & 15)) return 2;
561 /// MSJVM Notes ///////////////////////////////////////////////////////////////////
563 - To sniff the JVM, check if (window.clientInformation.javaEnabled()), or if
564 (navigator.javaEnabled()). See also http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dndetect/html/detectvm.asp
566 - MSJVM note: you have to write to the getGraphics() of a Frame before you can setIconImage() it.
568 - How to create trusted classes within an already-running MSJVM:
570 class MicrosoftClassLoader extends SecurityClassLoader {
571 static PermissionSet ps; // a PermissionSet that allows <i>everything</i>
573 PermissionDataSet pds = new PermissionDataSet();
574 IPermission perm = new IPermission() {
575 public void check(Object request) { }
576 public IPermission combine(IPermission other) { return this; }
577 public IPermission copy() { return this; }
578 public int compareSet(Object o) { return SetComparison.DISJOINT; }
580 pds.add(PolicyEngine.permissionNameToID("com.ms.security.permissions.ClientStoragePermission"), perm);
581 pds.add(PolicyEngine.permissionNameToID("com.ms.security.permissions.ExecutionPermission"), perm);
582 pds.add(PolicyEngine.permissionNameToID("com.ms.security.permissions.FileIOPermission"), perm);
583 pds.add(PolicyEngine.permissionNameToID("com.ms.security.permissions.MultimediaPermission"), perm);
584 pds.add(PolicyEngine.permissionNameToID("com.ms.security.permissions.NetIOPermission"), perm);
585 pds.add(PolicyEngine.permissionNameToID("com.ms.security.permissions.PrintingPermission"), perm);
586 pds.add(PolicyEngine.permissionNameToID("com.ms.security.permissions.PropertyPermission"), perm);
587 pds.add(PolicyEngine.permissionNameToID("com.ms.security.permissions.ReflectionPermission"), perm);
588 pds.add(PolicyEngine.permissionNameToID("com.ms.security.permissions.SecurityPermission"), perm);
589 pds.add(PolicyEngine.permissionNameToID("com.ms.security.permissions.SystemStreamsPermission"), perm);
590 pds.add(PolicyEngine.permissionNameToID("com.ms.security.permissions.ThreadPermission"), perm);
591 pds.add(PolicyEngine.permissionNameToID("com.ms.security.permissions.UIPermission"), perm);
592 pds.add(PolicyEngine.permissionNameToID("com.ms.security.permissions.UserFileIOPermission"), perm);
593 ps = new PermissionSet(pds);
595 public Class defineClass(String name, byte[] b) {
596 Class c = super.defineClass(name, b, 0, b.length, ps, com.ms.security.PolicyEngine.getPrincipalOfClass(Resources.class));
597 if (name.startsWith("net")) System.out.println("defineClass " + name + " yielded " + c);
598 // for some reason, the MSJVM doesn't request resolves properly, so we have to do it manually on every class-load
603 - How to inhibit background-clearing on the MSJVM
605 // Used to ensure that getPeer() doesn't go off in an infinite loop
608 // The MSJVM needs us to occasionally falsify getPeer() in order to thwart unneeded background-clearing
609 public ComponentPeer getPeer() {
610 if (Thread.currentThread() == Platform.fastPathThread) return super.getPeer();
611 if (ok) return super.getPeer();
613 // to prevent recursive stack-dumping... =)
615 Dimension d = getSize();
617 if (last != null && last.width == d.width && last.height == d.height)
618 return super.getPeer();
620 String s = com.ms.wfc.util.Debug.getStackTraceText();
621 for(int i = s.indexOf('d'); i != -1; i = s.indexOf('d', i + 1)) {
622 if (s.regionMatches(i, "doClearAndPaint", 0, 15)) {
628 return super.getPeer();