OLD | NEW |
1 // Copyright 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 package org.chromium.content.browser; | 5 package org.chromium.content.browser; |
6 | 6 |
| 7 import android.os.Handler; |
| 8 import android.os.Looper; |
7 import android.test.suitebuilder.annotation.SmallTest; | 9 import android.test.suitebuilder.annotation.SmallTest; |
8 | 10 |
9 import org.chromium.base.test.util.DisabledTest; | 11 import org.chromium.base.test.util.DisabledTest; |
10 import org.chromium.base.test.util.Feature; | 12 import org.chromium.base.test.util.Feature; |
11 import org.chromium.content.browser.test.util.TestCallbackHelperContainer; | 13 import org.chromium.content.browser.test.util.TestCallbackHelperContainer; |
12 import org.chromium.content_shell_apk.ContentShellActivity; | 14 import org.chromium.content_shell_apk.ContentShellActivity; |
13 | 15 |
14 import java.lang.annotation.Annotation; | 16 import java.lang.annotation.Annotation; |
15 import java.lang.annotation.ElementType; | 17 import java.lang.annotation.ElementType; |
16 import java.lang.annotation.Retention; | 18 import java.lang.annotation.Retention; |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
63 return mLongValue; | 65 return mLongValue; |
64 } | 66 } |
65 public synchronized String waitForStringValue() { | 67 public synchronized String waitForStringValue() { |
66 waitForResult(); | 68 waitForResult(); |
67 return mStringValue; | 69 return mStringValue; |
68 } | 70 } |
69 public synchronized boolean waitForBooleanValue() { | 71 public synchronized boolean waitForBooleanValue() { |
70 waitForResult(); | 72 waitForResult(); |
71 return mBooleanValue; | 73 return mBooleanValue; |
72 } | 74 } |
| 75 |
| 76 public synchronized String getStringValue() { |
| 77 return mStringValue; |
| 78 } |
73 } | 79 } |
74 | 80 |
75 private static class ObjectWithStaticMethod { | 81 private static class ObjectWithStaticMethod { |
76 public static String staticMethod() { | 82 public static String staticMethod() { |
77 return "foo"; | 83 return "foo"; |
78 } | 84 } |
79 } | 85 } |
80 | 86 |
81 TestController mTestController; | 87 TestController mTestController; |
82 | 88 |
(...skipping 476 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
559 "for (x in testObject) { result += \" \" + x } " + | 565 "for (x in testObject) { result += \" \" + x } " + |
560 "testController.setStringValue(result);"); | 566 "testController.setStringValue(result);"); |
561 assertEquals(" equals getClass hashCode method notify notifyAll toString
wait", | 567 assertEquals(" equals getClass hashCode method notify notifyAll toString
wait", |
562 mTestController.waitForStringValue()); | 568 mTestController.waitForStringValue()); |
563 } | 569 } |
564 | 570 |
565 @SmallTest | 571 @SmallTest |
566 @Feature({"AndroidWebView", "Android-JavaBridge"}) | 572 @Feature({"AndroidWebView", "Android-JavaBridge"}) |
567 public void testReflectPublicMethod() throws Throwable { | 573 public void testReflectPublicMethod() throws Throwable { |
568 injectObjectAndReload(new Object() { | 574 injectObjectAndReload(new Object() { |
| 575 public Class<?> myGetClass() { return getClass(); } |
569 public String method() { return "foo"; } | 576 public String method() { return "foo"; } |
570 }, "testObject"); | 577 }, "testObject"); |
571 assertEquals("foo", executeJavaScriptAndGetStringResult( | 578 assertEquals("foo", executeJavaScriptAndGetStringResult( |
572 "testObject.getClass().getMethod('method', null).invoke(testObje
ct, null)" + | 579 "testObject.myGetClass().getMethod('method', null).invoke(testOb
ject, null)" + |
573 ".toString()")); | 580 ".toString()")); |
574 } | 581 } |
575 | 582 |
576 @SmallTest | 583 @SmallTest |
577 @Feature({"AndroidWebView", "Android-JavaBridge"}) | 584 @Feature({"AndroidWebView", "Android-JavaBridge"}) |
578 public void testReflectPublicField() throws Throwable { | 585 public void testReflectPublicField() throws Throwable { |
579 injectObjectAndReload(new Object() { | 586 injectObjectAndReload(new Object() { |
| 587 public Class<?> myGetClass() { return getClass(); } |
580 public String field = "foo"; | 588 public String field = "foo"; |
581 }, "testObject"); | 589 }, "testObject"); |
582 assertEquals("foo", executeJavaScriptAndGetStringResult( | 590 assertEquals("foo", executeJavaScriptAndGetStringResult( |
583 "testObject.getClass().getField('field').get(testObject).toStrin
g()")); | 591 "testObject.myGetClass().getField('field').get(testObject).toStr
ing()")); |
584 } | 592 } |
585 | 593 |
586 @SmallTest | 594 @SmallTest |
587 @Feature({"AndroidWebView", "Android-JavaBridge"}) | 595 @Feature({"AndroidWebView", "Android-JavaBridge"}) |
588 public void testReflectPrivateMethodRaisesException() throws Throwable { | 596 public void testReflectPrivateMethodRaisesException() throws Throwable { |
589 injectObjectAndReload(new Object() { | 597 injectObjectAndReload(new Object() { |
| 598 public Class<?> myGetClass() { return getClass(); } |
590 private void method() {}; | 599 private void method() {}; |
591 }, "testObject"); | 600 }, "testObject"); |
592 assertRaisesException("testObject.getClass().getMethod('method', null)")
; | 601 assertRaisesException("testObject.myGetClass().getMethod('method', null)
"); |
593 // getDeclaredMethod() is able to access a private method, but invoke() | 602 // getDeclaredMethod() is able to access a private method, but invoke() |
594 // throws a Java exception. | 603 // throws a Java exception. |
595 assertRaisesException( | 604 assertRaisesException( |
596 "testObject.getClass().getDeclaredMethod('method', null).invoke(
testObject, null)"); | 605 "testObject.myGetClass().getDeclaredMethod('method', null)." + |
| 606 "invoke(testObject, null)"); |
597 } | 607 } |
598 | 608 |
599 @SmallTest | 609 @SmallTest |
600 @Feature({"AndroidWebView", "Android-JavaBridge"}) | 610 @Feature({"AndroidWebView", "Android-JavaBridge"}) |
601 public void testReflectPrivateFieldRaisesException() throws Throwable { | 611 public void testReflectPrivateFieldRaisesException() throws Throwable { |
602 injectObjectAndReload(new Object() { | 612 injectObjectAndReload(new Object() { |
| 613 public Class<?> myGetClass() { return getClass(); } |
603 private int field; | 614 private int field; |
604 }, "testObject"); | 615 }, "testObject"); |
605 assertRaisesException("testObject.getClass().getField('field')"); | 616 assertRaisesException("testObject.myGetClass().getField('field')"); |
606 // getDeclaredField() is able to access a private field, but getInt() | 617 // getDeclaredField() is able to access a private field, but getInt() |
607 // throws a Java exception. | 618 // throws a Java exception. |
608 assertRaisesException( | 619 assertRaisesException( |
609 "testObject.getClass().getDeclaredField('field').getInt(testObje
ct)"); | 620 "testObject.myGetClass().getDeclaredField('field').getInt(testOb
ject)"); |
610 } | 621 } |
611 | 622 |
612 @SmallTest | 623 @SmallTest |
613 @Feature({"AndroidWebView", "Android-JavaBridge"}) | 624 @Feature({"AndroidWebView", "Android-JavaBridge"}) |
614 public void testAllowNonAnnotatedMethods() throws Throwable { | 625 public void testAllowNonAnnotatedMethods() throws Throwable { |
615 injectObjectAndReload(new Object() { | 626 injectObjectAndReload(new Object() { |
616 public String allowed() { return "foo"; } | 627 public String allowed() { return "foo"; } |
617 }, "testObject", null); | 628 }, "testObject", null); |
618 | 629 |
619 // Test calling a method of an explicitly inherited class (Base#allowed(
)). | 630 // Test calling a method of an explicitly inherited class (Base#allowed(
)). |
620 assertEquals("foo", executeJavaScriptAndGetStringResult("testObject.allo
wed()")); | 631 assertEquals("foo", executeJavaScriptAndGetStringResult("testObject.allo
wed()")); |
621 | 632 |
622 // Test calling a method of an implicitly inherited class (Object#getCla
ss()). | 633 // Test calling a method of an implicitly inherited class (Object#toStri
ng()). |
623 assertEquals("object", executeJavaScriptAndGetStringResult("typeof testO
bject.getClass()")); | 634 assertEquals("string", executeJavaScriptAndGetStringResult("typeof testO
bject.toString()")); |
624 } | 635 } |
625 | 636 |
626 @SmallTest | 637 @SmallTest |
627 @Feature({"AndroidWebView", "Android-JavaBridge"}) | 638 @Feature({"AndroidWebView", "Android-JavaBridge"}) |
628 public void testAllowOnlyAnnotatedMethods() throws Throwable { | 639 public void testAllowOnlyAnnotatedMethods() throws Throwable { |
629 injectObjectAndReload(new Object() { | 640 injectObjectAndReload(new Object() { |
630 @JavascriptInterface | 641 @JavascriptInterface |
631 public String allowed() { return "foo"; } | 642 public String allowed() { return "foo"; } |
632 | 643 |
633 public String disallowed() { return "bar"; } | 644 public String disallowed() { return "bar"; } |
(...skipping 194 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
828 } | 839 } |
829 }); | 840 }); |
830 | 841 |
831 injectObjectAndReload(new Test(), nonInspectableObjectName, JavascriptIn
terface.class); | 842 injectObjectAndReload(new Test(), nonInspectableObjectName, JavascriptIn
terface.class); |
832 | 843 |
833 assertEquals("", executeJavaScriptAndGetStringResult( | 844 assertEquals("", executeJavaScriptAndGetStringResult( |
834 String.format(jsObjectKeysTestTemplate, nonInspectableOb
jectName))); | 845 String.format(jsObjectKeysTestTemplate, nonInspectableOb
jectName))); |
835 assertEquals("", executeJavaScriptAndGetStringResult( | 846 assertEquals("", executeJavaScriptAndGetStringResult( |
836 String.format(jsForInTestTemplate, nonInspectableObjectN
ame))); | 847 String.format(jsForInTestTemplate, nonInspectableObjectN
ame))); |
837 } | 848 } |
| 849 |
| 850 @SmallTest |
| 851 @Feature({"AndroidWebView", "Android-JavaBridge"}) |
| 852 public void testAccessToObjectGetClassIsBlocked() throws Throwable { |
| 853 injectObjectAndReload(new Object(), "testObject"); |
| 854 assertEquals("function", executeJavaScriptAndGetStringResult("typeof tes
tObject.getClass")); |
| 855 boolean securityExceptionThrown = false; |
| 856 try { |
| 857 final String result = executeJavaScriptAndWaitForExceptionSynchronou
sly( |
| 858 "typeof testObject.getClass()"); |
| 859 fail("A call to java.lang.Object.getClass has been allowed, result:
'" + result + "'"); |
| 860 } catch (SecurityException exception) { |
| 861 securityExceptionThrown = true; |
| 862 } |
| 863 assertTrue(securityExceptionThrown); |
| 864 } |
| 865 |
| 866 // Unlike executeJavaScriptAndGetStringResult, this method is sitting on the
UI thread |
| 867 // until a non-null result is obtained or a Java exception has been thrown.
This method is |
| 868 // capable of catching Java RuntimeExceptions happening on the UI thread asy
nchronously. |
| 869 private String executeJavaScriptAndWaitForExceptionSynchronously(final Strin
g script) |
| 870 throws Throwable { |
| 871 class ExitLoopException extends RuntimeException { |
| 872 } |
| 873 mTestController.setStringValue(null); |
| 874 runTestOnUiThread(new Runnable() { |
| 875 @Override |
| 876 public void run() { |
| 877 getContentView().loadUrl(new LoadUrlParams("javascript:(function
() { " + |
| 878 "testController.setStringValue(" + script + ") }
)()")); |
| 879 do { |
| 880 final Boolean[] deactivateExitLoopTask = new Boolean[1]; |
| 881 deactivateExitLoopTask[0] = false; |
| 882 // We can't use Loop.quit(), as this is the main looper, so
we throw |
| 883 // an exception to bail out from the loop. |
| 884 new Handler(Looper.myLooper()).post(new Runnable() { |
| 885 @Override |
| 886 public void run() { |
| 887 if (!deactivateExitLoopTask[0]) { |
| 888 throw new ExitLoopException(); |
| 889 } |
| 890 } |
| 891 }); |
| 892 try { |
| 893 Looper.loop(); |
| 894 } catch (ExitLoopException e) { |
| 895 // Intentionally empty. |
| 896 } catch (RuntimeException e) { |
| 897 // Prevent the task that throws the ExitLoopException fr
om exploding |
| 898 // on the main loop outside of this function. |
| 899 deactivateExitLoopTask[0] = true; |
| 900 throw e; |
| 901 } |
| 902 } while (mTestController.getStringValue() == null || |
| 903 // When an exception in an injected method happens, the
function returns |
| 904 // null. We ignore this and wait until the exception on
the browser side |
| 905 // will be thrown. |
| 906 mTestController.getStringValue().equals("null")); |
| 907 } |
| 908 }); |
| 909 return mTestController.getStringValue(); |
| 910 } |
838 } | 911 } |
OLD | NEW |