1 // Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL]
2 package org.xwt.shoehorn3;
6 import java.lang.reflect.*;
9 import java.util.zip.*;
13 /** This class is XWT's presence on the user's computer; it must be run as trusted code */
14 public class ShoeHorn extends Applet {
16 // Startup Phase ////////////////////////////////////////////////////////////////////
17 private String build = null;
18 public ShoeHorn() { log("*** constructor invoked for " + this.getClass().getName()); }
20 public final String getParameter(String arg) { return super.getParameter(arg); }
21 public final void main(String[] s) { new ShoeHorn().start(); }
22 private void log(String s) { System.out.println(s); }
24 /** this just ensures that we are running with full privileges */
25 public final void start() {
26 build = getParameter("build");
27 new Thread() { public void run() {
28 log("ShoeHorn thread spawned");
31 if (System.getProperty("java.vendor", "").startsWith("Netscape")) {
32 log("Detected Navigator 4.x");
33 Method m = Class.forName("netscape.security.PrivilegeManager").getMethod("enablePrivilege", new Class[] { String.class });
34 m.invoke(null, new Object[] { "MarimbaInternalTarget" });
35 m.invoke(null, new Object[] { "UniversalExecAccess" });
36 m.invoke(null, new Object[] { "UniversalPropertyRead" });
39 } else if (System.getProperty("java.vendor", "").startsWith("Microsoft")) {
40 //com.ms.security.PolicyEngine.assertPermission(com.ms.security.PermissionID.SYSTEM);
41 Class permissionIdClass = Class.forName("com.ms.security.PermissionID");
42 Object o = permissionIdClass.getField("SYSTEM").get(null);
43 Method m = Class.forName("com.ms.security.PolicyEngine").getMethod("assertPermission", new Class[] { permissionIdClass });
44 m.invoke(null, new Object[] { o });
48 log("Detected non-Navigator JVM");
49 Method m = Class.forName("org.xwt.shoehorn3.ShoeHorn$Java12").getMethod("run", new Class[] { Object.class });
50 m.invoke(null, new Object[] { ShoeHorn.this });
52 } catch (Throwable e) {
53 if (e instanceof InvocationTargetException) e = ((InvocationTargetException)e).getTargetException();
55 update(-1.0, "Error; please check the Java console");
60 /** ask Java Plugin for privs */
61 private static class Java12 {
62 public static void run(final Object a) {
63 java.security.AccessController.doPrivileged(new java.security.PrivilegedAction() {
73 // Implantation ////////////////////////////////////////////////////////////////////
75 /** inserts the required entries into the user's ~/.java.policy */
76 private void modifyPolicyFile() throws IOException {
77 log("Adjusting ~/.java.policy");
78 File policy = new File(System.getProperty("user.home") + File.separatorChar + ".java.policy");
79 if (policy.exists()) {
80 BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(policy)));
82 while((s = br.readLine()) != null)
83 if (s.startsWith("// XWT_MARKER:")) {
84 log("Java policy file has already been adjusted");
88 FileOutputStream fos = new FileOutputStream(policy.getAbsolutePath(), true);
89 PrintWriter pw = new PrintWriter(new OutputStreamWriter(fos));
91 pw.println("// XWT_MARKER: this line and the following two grant blocks are required for XWT; DO NOT REMOVE THEM.");
92 pw.println("grant {");
93 pw.println(" permission java.io.FilePermission \"${user.home}${/}.xwt${/}shoehorn.jar\", \"read\";");
95 pw.println("grant codebase \"file:${user.home}${/}.xwt${/}shoehorn.jar\" {");
96 pw.println(" permission java.security.AllPermission;");
98 pw.println("// END_XWT_MARKER");
104 /** read ourselves out of the resources and write a jarfile to some trusted place */
105 private void implantSelf() throws IOException {
106 InputStream manifest = getClass().getClassLoader().getResourceAsStream("META-INF/manifest.mf");
107 log("my classloader is " + getClass().getClassLoader().getClass().getName());
108 ClassLoader loader = getClass().getClassLoader();
109 if (manifest == null || loader == null ||
110 (loader.getClass().getName().indexOf("Applet") == -1 && loader.getClass().getName().indexOf("Plugin") == -1))
112 BufferedReader br = new BufferedReader(new InputStreamReader(manifest));
113 Vector entries = new Vector();
115 while((s = br.readLine()) != null)
116 if (s.startsWith("Name: "))
117 entries.addElement(s.substring(6));
119 String ext_dirs = System.getProperty("java.ext.dirs");
120 log("java.ext.dirs = " + ext_dirs);
121 ext_dirs = ext_dirs + File.pathSeparatorChar + System.getProperty("user.home") + File.separatorChar + ".xwt";
122 StringTokenizer st = new StringTokenizer(ext_dirs, File.pathSeparatorChar + "");
123 while(st.hasMoreTokens()) {
124 String dir = st.nextToken();
125 new File(dir).mkdirs();
127 // we have to modify the policy file BEFORE implanting in ~/.xwt to ensure that the policy mods work
128 if (!st.hasMoreTokens()) modifyPolicyFile();
129 implantInDirectory(dir, entries);
131 } catch (IOException e) {
132 log("Failed to implant in " + dir + " due to " + e);
135 log("Failed to implant self!");
138 private void implantInDirectory(String dir, Vector entries) throws IOException {
139 File f = new File(dir + File.separatorChar + "shoehorn.tmp");
140 ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(f));
141 for(int i=0; i<entries.size(); i++) {
142 zos.putNextEntry(new ZipEntry(entries.elementAt(i).toString()));
143 InputStream is = this.getClass().getClassLoader().getResourceAsStream(entries.elementAt(i).toString());
144 byte[] buf = new byte[1024];
146 int read = is.read(buf, 0, buf.length);
147 if (read == -1) break;
148 zos.write(buf, 0, read);
153 f.renameTo(new File(dir + File.separatorChar + "shoehorn.jar"));
154 log("Succeeded in implanting in " + dir);
158 // Startup Phase ////////////////////////////////////////////////////////////////////
160 public final void go() {
166 String os_name = System.getProperty("os.name", "");
167 log("os.name == " + os_name);
168 Vector command = new Vector();
171 if (os_name.indexOf("Linux") != -1) {
172 arch = new BufferedReader(new InputStreamReader(Runtime.getRuntime().exec("/bin/uname -m").getInputStream())).readLine();
173 log("arch is " + arch);
176 if (os_name.indexOf("Linux") != -1 && arch.charAt(0) == 'i' && arch.substring(2, 4).equals("86")) {
177 file = fetch("xwt-" + build + ".linux.gz");
178 safeWaitFor(Runtime.getRuntime().exec("/bin/chmod +x " + file.getAbsolutePath()));
179 command.addElement(file.getAbsolutePath());
181 } else if (os_name.indexOf("Windows") != -1) {
182 file = fetch("xwt-" + build + ".cab");
183 command.addElement(file.getAbsolutePath());
186 file = fetch("xwt-" + build + ".jar");
187 command.addElement(findJvmBinary());
188 command.addElement("-jar");
189 command.addElement(file.getAbsolutePath());
192 if ("true".equals(getParameter("showrenders"))) command.addElement("-s");
193 if ("true".equals(getParameter("verbose"))) command.addElement("-v");
194 if (getParameter("log") != null) {
195 command.addElement("-l");
196 command.addElement(getParameter("log"));
198 command.addElement(getParameter("xwar"));
200 } catch (Exception e) {
201 update(-1.0, "Error; please check the Java console");
206 /** searches for the JVM binary in the usual places */
207 private String findJvmBinary() throws IOException {
208 String jvmBinary = null;
210 // HACK: prefer jdk1.3 on OSX; it has better fonts
211 if (new File("/System/Library/Frameworks/JavaVM.framework/Versions/1.3.1/Commands/java").exists())
212 jvmBinary = "/System/Library/Frameworks/JavaVM.framework/Versions/1.3.1/Commands/java";
215 String javaHome = getEnv("JAVA_HOME");
216 if (jvmBinary == null && javaHome != null && !javaHome.equals("")) {
217 jvmBinary = javaHome + File.separatorChar + "bin" + File.separatorChar + "java";
218 if (!new File(jvmBinary).exists()) jvmBinary = null;
221 // check common locations
222 if (jvmBinary == null)
223 for(int i=0; i<commonJavaLocations.length; i++)
224 if (new File(commonJavaLocations[i]).exists()) {
225 jvmBinary = commonJavaLocations[i];
230 if (jvmBinary == null) {
231 String path = getEnv("PATH");
232 StringTokenizer st = new StringTokenizer(path, File.pathSeparatorChar + "");
233 while(st.hasMoreTokens()) {
234 String s = st.nextToken();
235 if (new File(s + File.separatorChar + "java").exists()) {
236 jvmBinary = s + File.separatorChar + "java";
242 // check ${java.home}
243 javaHome = System.getProperty("java.home");
244 if (jvmBinary == null && javaHome != null && !javaHome.equals("")) {
245 jvmBinary = javaHome + File.separatorChar + "bin" + File.separatorChar + "java";
246 if (!new File(jvmBinary).exists()) jvmBinary = null;
249 if (jvmBinary == null)
250 throw new Error("couldn't find JVM binary! JAVA_HOME=" + getEnv("JAVA_HOME") + " PATH=" + getEnv("PATH"));
255 private void spawn(Vector command) throws IOException {
256 String proxy = detectProxy();
257 log("proxy settings: " + proxy);
259 String[] command_vec;
260 command.copyInto(command_vec = new String[command.size()]);
262 for(int i=0; i<command_vec.length; i++) log(" \"" + command_vec[i] + "\"");
266 p = Runtime.getRuntime().exec(command_vec);
267 } else if (System.getProperty("os.name").toLowerCase().indexOf("windows") != -1) {
268 // hrm, win32 doesn't like it when we probe the environment; that doesn't matter, since the
269 // win32 environ vars are pretty much useless anyways.
270 p = Runtime.getRuntime().exec(command_vec, new String[] { proxy });
272 p = Runtime.getRuntime().exec(command_vec, dumpEnv(proxy));
274 BufferedReader stderr = new BufferedReader(new InputStreamReader(p.getErrorStream()));
276 if (System.getProperty("os.name").toLowerCase().indexOf("windows") != -1) {
277 update(1.0, "XWT Loaded");
279 String s = stderr.readLine();
280 update(1.0, "XWT Loaded");
283 s = stderr.readLine();
290 // Utility Functions /////////////////////////////////////////////////////////////////////
292 /** Voodoo to extract the proxy settings from Sun's Java Plugin */
293 private String detectProxy() {
295 Vector ret = new Vector();
297 Class PluginProxyHandler = Class.forName("sun.plugin.protocol.PluginProxyHandler");
298 Method getDefaultProxyHandler = PluginProxyHandler.getMethod("getDefaultProxyHandler", new Class[] { });
299 Object proxyHandler = getDefaultProxyHandler.invoke(null, new Object[] { });
301 Class ProxyHandler = Class.forName("sun.plugin.protocol.ProxyHandler");
302 Method getProxyInfo = ProxyHandler.getMethod("getProxyInfo", new Class[] { URL.class });
303 Object proxyInfo = getProxyInfo.invoke(proxyHandler, new Object[] { new URL("http://www.xwt.org") });
305 Class ProxyInfo = Class.forName("sun.plugin.protocol.ProxyInfo");
307 if (((Boolean)ProxyInfo.getMethod("isProxyUsed", new Class[] { }).invoke(proxyInfo, new Object[] { })).booleanValue())
308 return "http_proxy=" +
309 (String)ProxyInfo.getMethod("getProxy", new Class[] { }).invoke(proxyInfo, new Object[] { }) + ":" +
310 ((Integer)ProxyInfo.getMethod("getPort", new Class[] { }).invoke(proxyInfo, new Object[] { })).intValue();
312 if (((Boolean)ProxyInfo.getMethod("isSocksUsed", new Class[] { }).invoke(proxyInfo, new Object[] { })).booleanValue())
313 return "socks_proxy=" +
314 (String)ProxyInfo.getMethod("getSocksProxy", new Class[] { }).invoke(proxyInfo, new Object[] { }) + ":" +
315 ((Integer)ProxyInfo.getMethod("getSocksPort", new Class[] { }).invoke(proxyInfo, new Object[] { })).intValue();
319 } catch (Throwable e) {
320 log("exception while querying sun.plugin.protocol.PluginProxyHandler: " + e);
325 // The Netscape JVM has a bug which causes waitFor() to lock up, so we wait no more than 2000ms
326 private void safeWaitFor(final Process p) {
327 final Object o = new Object();
332 } catch (InterruptedException e) { }
333 synchronized(o) { o.notify(); }
337 synchronized(o) { o.wait(2000); }
338 } catch (InterruptedException e) { }
341 /** fetches a file from the distribution site, writing it to the appropriate place */
342 private File fetch(String filename) throws IOException {
343 String tmpdir = System.getProperty("user.home") + File.separatorChar + ".xwt";
344 new File(tmpdir).mkdirs();
345 URL u = new URL("http://dist.xwt.org/" + filename);
346 if (filename.endsWith(".gz")) filename = filename.substring(0, filename.length() - 3);
347 if (filename.endsWith(".cab")) filename = filename.substring(0, filename.length() - 4) + ".exe";
348 File target = new File(tmpdir + File.separatorChar + filename);
349 if (target.exists()) return target;
350 loadFromURL(u, target, filename);
354 /** loads a file from a url, verifying that it was properly signed */
355 private void loadFromURL(URL u, File target, String filename) throws IOException {
357 final URLConnection uc = u.openConnection();
358 final int signatureLength = 128;
359 final byte[] signature = new byte[signatureLength];
361 InputStream is = uc.getInputStream();
362 final int contentLength = uc.getContentLength();
364 // display the progress indicator as we go
365 InputStream dis = new FilterInputStream(is) {
367 public int read() throws IOException {
368 int ret = super.read();
369 if (ret != -1) total++;
370 double loaded = ((double)total) / ((double)uc.getContentLength());
371 update(loaded, "Loading XWT: " + ((int)Math.ceil(loaded * 100)) + "%");
374 public int read(byte[] buf, int off, int len) throws IOException {
375 int ret = super.read(buf, off, len);
376 if (ret != -1) total += ret;
377 double loaded = ((double)total) / ((double)uc.getContentLength());
378 update(loaded, "Loading XWT: " + ((int)Math.ceil(loaded * 100)) + "%");
383 // decompress if needed
384 if (u.toString().endsWith(".cab")) dis = CAB.getFileInputStream(dis, "xwt-" + build + ".exe");
385 else if (u.toString().endsWith(".gz")) dis = new GZIPInputStream(dis);
387 // digest and copy the file to target.tmp, omitting the last signatureLength bytes
388 SHA1Digest sha1 = new SHA1Digest();
389 OutputStream fos = new DigestOutputStream(new FileOutputStream(target + ".tmp"), sha1);
390 byte[] buf = new byte[1024 * 128];
393 while(numread < buf.length) {
394 int read = dis.read(buf, numread, buf.length - numread);
395 if (read == -1) break;
398 if (numread < buf.length) {
399 System.arraycopy(buf, numread - signatureLength, signature, 0, signatureLength);
400 fos.write(buf, 0, numread - signatureLength);
403 fos.write(buf, 0, buf.length - signatureLength);
404 System.arraycopy(buf, buf.length - signatureLength, buf, 0, signatureLength);
405 numread = signatureLength;
410 // construct our hash
411 byte[] hash = new byte[sha1.getDigestSize()];
412 sha1.doFinal(hash, 0);
414 // decrypt the signature hash
415 DERInputStream deris = new DERInputStream(new ByteArrayInputStream(Base64.decode(XWT_foundation_public_key)));
416 SubjectPublicKeyInfo pki = new SubjectPublicKeyInfo((DERConstructedSequence)deris.readObject());
417 RSAPublicKeyStructure rsa_pks = new RSAPublicKeyStructure((DERConstructedSequence)pki.getPublicKey());
418 BigInteger modulus = rsa_pks.getModulus();
419 BigInteger exponent = rsa_pks.getPublicExponent();
420 AsymmetricBlockCipher rsa = new PKCS1Encoding(new RSAEngine());
421 rsa.init(false, new RSAKeyParameters(false, modulus, exponent));
425 byte[] decryptedHash = rsa.processBlock(signature, 0, signature.length);
426 for(int i=0; i<hash.length; i++)
427 if (decryptedHash[i] != hash[i]) {
428 new File(target + ".tmp").delete();
429 throw new Error("invalid signature on " + filename);
431 } catch (InvalidCipherTextException e) {
432 new File(target + ".tmp").delete();
436 // good to go; rename the file
437 new File(target + ".tmp").renameTo(target);
440 /** retrieves an environment variable */
441 private static String getEnv(String key) throws IOException {
442 Process p = Runtime.getRuntime().exec(new String[] { "/bin/sh", "-c", "echo $" + key });
443 BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
444 return br.readLine();
447 /** dumps the environment plus an additional variable */
448 private static String[] dumpEnv(String newvar) throws IOException {
449 Vector v = new Vector();
450 String os = System.getProperty("os.name").toLowerCase();
452 if (os.indexOf("windows 9") > -1) {
453 p = Runtime.getRuntime().exec("command.com /c set");
454 } else if ((os.indexOf("nt") > -1) || (os.indexOf("windows 2000") > -1) ) {
455 p = Runtime.getRuntime().exec("cmd.exe /c set");
457 p = Runtime.getRuntime().exec("env");
459 BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
461 while ((s = br.readLine()) != null) v.addElement(s);
462 v.addElement(newvar);
464 v.copyInto(ret = new String[v.size()]);
470 // Applet Painting Functions ////////////////////////////////////////////////////////
472 private Image backbuffer = null;
473 public final void paint(Graphics g) { if (backbuffer != null) g.drawImage(backbuffer, 0, 0, null); }
475 public final Graphics getGraphics() { return super.getGraphics(); }
476 public final Dimension getSize() { return super.getSize(); }
478 private void update(double loaded, String text) {
479 // indicates we should paint ourselves
480 Graphics g2 = getGraphics();
482 if (backbuffer == null || backbuffer.getWidth(null) != getSize().width || backbuffer.getHeight(null) != getSize().height)
483 backbuffer = createImage(getSize().width, getSize().height);
484 if (backbuffer == null) return;
485 Graphics g = backbuffer.getGraphics();
487 g.setColor(loaded < 0 ? Color.red : Color.blue);
488 loaded = Math.abs(loaded);
490 int w = (int)((double)getSize().width * loaded);
491 g.fillRect(0, 0, w, getSize().height);
492 g.setColor(Color.darkGray);
493 g.fillRect(w, 0, getSize().width - w, getSize().height);
495 Font f = new Font("Sans-serif", Font.BOLD, 12);
496 FontMetrics fm = Toolkit.getDefaultToolkit().getFontMetrics(f);
499 int x = (getSize().width - fm.stringWidth(s)) / 2;
500 int y = ((getSize().height - fm.getMaxAscent() - fm.getMaxDescent()) / 2) + fm.getMaxAscent();
501 g.setColor(Color.white);
502 g.drawString(s, x, y);
505 g2.setClip(0, 0, getSize().width, getSize().height);
506 g2.drawImage(backbuffer, 0, 0, null);
511 // Misc Constants ///////////////////////////////////////////////////////////
513 private static final String XWT_foundation_public_key =
514 "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCoVweDZ+h3jcN2Qz1YwWJR8gVF0m/IE" +
515 "BasLIcvey9y1VkyN9jY6b9qm/Z2UoVtvAlgezd4CsJedUCIMe7uURyHGnqI4MLrozxLz3" +
516 "zqx5EYChsJt+Ju/f44KYnMx7upUQ4irfxOj6RpHy3E5GbW4XO96WwFlOuaR8+HRwKCXGP" +
519 private static final String[] commonJavaLocations = new String[] {
521 "/usr/java/bin/java",
522 "/usr/local/bin/java",
523 "/usr/local/java/bin/java",
524 "/usr/lib/j2sdk1.4/bin/java",
525 "/usr/lib/j2sdk1.3/bin/java",
526 "/usr/lib/j2sdk1.2/bin/java"
536 /// NSJVM Notes ///////////////////////////////////////////////////////////////////
538 PrivilegeManager.enablePrivilege("MarimbaInternalTarget");
539 PrivilegeManager.enablePrivilege("UniversalSystemClipboardAccess");
540 netscape.security.PrivilegeManager.enablePrivilege("UniversalConnect"); (sockets)
541 netscape.security.PrivilegeManager.enablePrivilege("UniversalSystemClipboardAccess");
542 netscape.security.PrivilegeManager.enablePrivilege("UniversalTopLevelWindow");
544 - Netscape's ClassLoader.getResource() is broken, see http://developer.netscape.com/docs/technote/java/getresource/getresource.html
545 - this will fix it: netscape.security.PrivilegeManager.enablePrivilege("UniversalPropertyRead");
547 - If you create a classloader, include
548 public boolean checkMatchPrincipalAlways(int i) {
549 return ((AppletClassLoader)this.getClass().getClassLoader()).checkMatchPrincipalAlways(i);
552 - protected int _modifiersToButtonNumber(int modifiers) {
553 if ((modifiers & (InputEvent.BUTTON1_MASK & 15)) == (InputEvent.BUTTON1_MASK & 15)) return 1;
554 if ((modifiers & (InputEvent.BUTTON2_MASK & 15)) == (InputEvent.BUTTON2_MASK & 15)) return 3;
555 if ((modifiers & (InputEvent.BUTTON3_MASK & 15)) == (InputEvent.BUTTON3_MASK & 15)) return 2;
558 /// MSJVM Notes ///////////////////////////////////////////////////////////////////
560 - To sniff the JVM, check if (window.clientInformation.javaEnabled()), or if
561 (navigator.javaEnabled()). See also http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dndetect/html/detectvm.asp
563 - MSJVM note: you have to write to the getGraphics() of a Frame before you can setIconImage() it.
565 - How to create trusted classes within an already-running MSJVM:
567 class MicrosoftClassLoader extends SecurityClassLoader {
568 static PermissionSet ps; // a PermissionSet that allows <i>everything</i>
570 PermissionDataSet pds = new PermissionDataSet();
571 IPermission perm = new IPermission() {
572 public void check(Object request) { }
573 public IPermission combine(IPermission other) { return this; }
574 public IPermission copy() { return this; }
575 public int compareSet(Object o) { return SetComparison.DISJOINT; }
577 pds.add(PolicyEngine.permissionNameToID("com.ms.security.permissions.ClientStoragePermission"), perm);
578 pds.add(PolicyEngine.permissionNameToID("com.ms.security.permissions.ExecutionPermission"), perm);
579 pds.add(PolicyEngine.permissionNameToID("com.ms.security.permissions.FileIOPermission"), perm);
580 pds.add(PolicyEngine.permissionNameToID("com.ms.security.permissions.MultimediaPermission"), perm);
581 pds.add(PolicyEngine.permissionNameToID("com.ms.security.permissions.NetIOPermission"), perm);
582 pds.add(PolicyEngine.permissionNameToID("com.ms.security.permissions.PrintingPermission"), perm);
583 pds.add(PolicyEngine.permissionNameToID("com.ms.security.permissions.PropertyPermission"), perm);
584 pds.add(PolicyEngine.permissionNameToID("com.ms.security.permissions.ReflectionPermission"), perm);
585 pds.add(PolicyEngine.permissionNameToID("com.ms.security.permissions.SecurityPermission"), perm);
586 pds.add(PolicyEngine.permissionNameToID("com.ms.security.permissions.SystemStreamsPermission"), perm);
587 pds.add(PolicyEngine.permissionNameToID("com.ms.security.permissions.ThreadPermission"), perm);
588 pds.add(PolicyEngine.permissionNameToID("com.ms.security.permissions.UIPermission"), perm);
589 pds.add(PolicyEngine.permissionNameToID("com.ms.security.permissions.UserFileIOPermission"), perm);
590 ps = new PermissionSet(pds);
592 public Class defineClass(String name, byte[] b) {
593 Class c = super.defineClass(name, b, 0, b.length, ps, com.ms.security.PolicyEngine.getPrincipalOfClass(Resources.class));
594 if (name.startsWith("net")) System.out.println("defineClass " + name + " yielded " + c);
595 // for some reason, the MSJVM doesn't request resolves properly, so we have to do it manually on every class-load
600 - How to inhibit background-clearing on the MSJVM
602 // Used to ensure that getPeer() doesn't go off in an infinite loop
605 // The MSJVM needs us to occasionally falsify getPeer() in order to thwart unneeded background-clearing
606 public ComponentPeer getPeer() {
607 if (Thread.currentThread() == Platform.fastPathThread) return super.getPeer();
608 if (ok) return super.getPeer();
610 // to prevent recursive stack-dumping... =)
612 Dimension d = getSize();
614 if (last != null && last.width == d.width && last.height == d.height)
615 return super.getPeer();
617 String s = com.ms.wfc.util.Debug.getStackTraceText();
618 for(int i = s.indexOf('d'); i != -1; i = s.indexOf('d', i + 1)) {
619 if (s.regionMatches(i, "doClearAndPaint", 0, 15)) {
625 return super.getPeer();