Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / android_webview / javatests / src / org / chromium / android_webview / test / AwContentsTest.java
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
3 // found in the LICENSE file.
4
5 package org.chromium.android_webview.test;
6
7 import android.graphics.Bitmap;
8 import android.graphics.BitmapFactory;
9 import android.os.Handler;
10 import android.os.Looper;
11 import android.os.Message;
12 import android.test.UiThreadTest;
13 import android.test.suitebuilder.annotation.LargeTest;
14 import android.test.suitebuilder.annotation.SmallTest;
15 import android.util.Pair;
16
17 import org.apache.http.Header;
18 import org.apache.http.HttpRequest;
19
20 import org.chromium.android_webview.AwContents;
21 import org.chromium.android_webview.AwSettings;
22 import org.chromium.android_webview.test.TestAwContentsClient.OnDownloadStartHelper;
23 import org.chromium.android_webview.test.util.CommonResources;
24 import org.chromium.base.test.util.Feature;
25 import org.chromium.content.browser.test.util.CallbackHelper;
26 import org.chromium.net.test.util.TestWebServer;
27
28 import java.io.InputStream;
29 import java.net.URL;
30 import java.util.ArrayList;
31 import java.util.HashMap;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.concurrent.Callable;
35 import java.util.concurrent.Semaphore;
36 import java.util.concurrent.TimeUnit;
37 import java.util.concurrent.atomic.AtomicInteger;
38
39 /**
40  * AwContents tests.
41  */
42 public class AwContentsTest extends AwTestBase {
43
44     private TestAwContentsClient mContentsClient = new TestAwContentsClient();
45
46     @SmallTest
47     @Feature({"AndroidWebView"})
48     @UiThreadTest
49     public void testCreateDestroy() throws Throwable {
50         // NOTE this test runs on UI thread, so we cannot call any async methods.
51         createAwTestContainerView(mContentsClient).getAwContents().destroy();
52     }
53
54     @SmallTest
55     @Feature({"AndroidWebView"})
56     public void testCreateLoadPageDestroy() throws Throwable {
57         AwTestContainerView awTestContainerView =
58                 createAwTestContainerViewOnMainSync(mContentsClient);
59         loadUrlSync(awTestContainerView.getAwContents(),
60                 mContentsClient.getOnPageFinishedHelper(), CommonResources.ABOUT_HTML);
61         destroyAwContentsOnMainSync(awTestContainerView.getAwContents());
62         // It should be safe to call destroy multiple times.
63         destroyAwContentsOnMainSync(awTestContainerView.getAwContents());
64     }
65
66     @LargeTest
67     @Feature({"AndroidWebView"})
68     public void testCreateLoadDestroyManyTimes() throws Throwable {
69         final int CREATE_AND_DESTROY_REPEAT_COUNT = 10;
70         for (int i = 0; i < CREATE_AND_DESTROY_REPEAT_COUNT; ++i) {
71             AwTestContainerView testView = createAwTestContainerViewOnMainSync(mContentsClient);
72             AwContents awContents = testView.getAwContents();
73
74             loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(), "about:blank");
75             destroyAwContentsOnMainSync(awContents);
76         }
77     }
78
79     @LargeTest
80     @Feature({"AndroidWebView"})
81     public void testCreateLoadDestroyManyAtOnce() throws Throwable {
82         final int CREATE_AND_DESTROY_REPEAT_COUNT = 10;
83         AwTestContainerView views[] = new AwTestContainerView[CREATE_AND_DESTROY_REPEAT_COUNT];
84
85         for (int i = 0; i < views.length; ++i) {
86             views[i] = createAwTestContainerViewOnMainSync(mContentsClient);
87             loadUrlSync(views[i].getAwContents(), mContentsClient.getOnPageFinishedHelper(),
88                     "about:blank");
89         }
90
91         for (int i = 0; i < views.length; ++i) {
92             destroyAwContentsOnMainSync(views[i].getAwContents());
93             views[i] = null;
94         }
95     }
96
97     @LargeTest
98     @Feature({"AndroidWebView"})
99     public void testCreateAndGcManyTimes() throws Throwable {
100         final int CONCURRENT_INSTANCES = 4;
101         final int REPETITIONS = 16;
102         // The system retains a strong ref to the last focused view (in InputMethodManager)
103         // so allow for 1 'leaked' instance.
104         final int MAX_IDLE_INSTANCES = 1;
105
106         System.gc();
107
108         pollOnUiThread(new Callable<Boolean>() {
109             @Override
110             public Boolean call() {
111                 return AwContents.getNativeInstanceCount() <= MAX_IDLE_INSTANCES;
112             }
113         });
114         for (int i = 0; i < REPETITIONS; ++i) {
115             for (int j = 0; j < CONCURRENT_INSTANCES; ++j) {
116                 AwTestContainerView view = createAwTestContainerViewOnMainSync(mContentsClient);
117                 loadUrlAsync(view.getAwContents(), "about:blank");
118             }
119             assertTrue(AwContents.getNativeInstanceCount() >= CONCURRENT_INSTANCES);
120             assertTrue(AwContents.getNativeInstanceCount() <= (i + 1) * CONCURRENT_INSTANCES);
121             runTestOnUiThread(new Runnable() {
122                 @Override
123                 public void run() {
124                     getActivity().removeAllViews();
125                 }
126             });
127         }
128
129         System.gc();
130
131         pollOnUiThread(new Callable<Boolean>() {
132             @Override
133             public Boolean call() {
134                 return AwContents.getNativeInstanceCount() <= MAX_IDLE_INSTANCES;
135             }
136         });
137     }
138
139     @SmallTest
140     @Feature({"AndroidWebView"})
141     public void testUseAwSettingsAfterDestroy() throws Throwable {
142         AwTestContainerView awTestContainerView =
143                 createAwTestContainerViewOnMainSync(mContentsClient);
144         AwSettings awSettings = getAwSettingsOnUiThread(awTestContainerView.getAwContents());
145         loadUrlSync(awTestContainerView.getAwContents(),
146                 mContentsClient.getOnPageFinishedHelper(), CommonResources.ABOUT_HTML);
147         destroyAwContentsOnMainSync(awTestContainerView.getAwContents());
148
149         // AwSettings should still be usable even after native side is destroyed.
150         String newFontFamily = "serif";
151         awSettings.setStandardFontFamily(newFontFamily);
152         assertEquals(newFontFamily, awSettings.getStandardFontFamily());
153         boolean newBlockNetworkLoads = !awSettings.getBlockNetworkLoads();
154         awSettings.setBlockNetworkLoads(newBlockNetworkLoads);
155         assertEquals(newBlockNetworkLoads, awSettings.getBlockNetworkLoads());
156     }
157
158     private int callDocumentHasImagesSync(final AwContents awContents)
159             throws Throwable, InterruptedException {
160         // Set up a container to hold the result object and a semaphore to
161         // make the test wait for the result.
162         final AtomicInteger val = new AtomicInteger();
163         final Semaphore s = new Semaphore(0);
164         final Message msg = Message.obtain(new Handler(Looper.getMainLooper()) {
165             @Override
166             public void handleMessage(Message msg) {
167                 val.set(msg.arg1);
168                 s.release();
169             }
170         });
171         runTestOnUiThread(new Runnable() {
172             @Override
173             public void run() {
174               awContents.documentHasImages(msg);
175             }
176         });
177         assertTrue(s.tryAcquire(WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS));
178         int result = val.get();
179         return result;
180     }
181
182     @SmallTest
183     @Feature({"AndroidWebView"})
184     public void testDocumentHasImages() throws Throwable {
185         AwTestContainerView testView = createAwTestContainerViewOnMainSync(mContentsClient);
186         AwContents awContents = testView.getAwContents();
187
188         final CallbackHelper loadHelper = mContentsClient.getOnPageFinishedHelper();
189
190         final String mime = "text/html";
191         final String emptyDoc = "<head/><body/>";
192         final String imageDoc = "<head/><body><img/><img/></body>";
193
194         // Make sure a document that does not have images returns 0
195         loadDataSync(awContents, loadHelper, emptyDoc, mime, false);
196         int result = callDocumentHasImagesSync(awContents);
197         assertEquals(0, result);
198
199         // Make sure a document that does have images returns 1
200         loadDataSync(awContents, loadHelper, imageDoc, mime, false);
201         result = callDocumentHasImagesSync(awContents);
202         assertEquals(1, result);
203     }
204
205     @SmallTest
206     @Feature({"AndroidWebView"})
207     public void testClearCacheMemoryAndDisk() throws Throwable {
208         final AwTestContainerView testContainer =
209                 createAwTestContainerViewOnMainSync(mContentsClient);
210         final AwContents awContents = testContainer.getAwContents();
211
212         TestWebServer webServer = null;
213         try {
214             webServer = new TestWebServer(false);
215             final String pagePath = "/clear_cache_test.html";
216             List<Pair<String, String>> headers = new ArrayList<Pair<String, String>>();
217             // Set Cache-Control headers to cache this request. One century should be long enough.
218             headers.add(Pair.create("Cache-Control", "max-age=3153600000"));
219             headers.add(Pair.create("Last-Modified", "Wed, 3 Oct 2012 00:00:00 GMT"));
220             final String pageUrl = webServer.setResponse(
221                     pagePath, "<html><body>foo</body></html>", headers);
222
223             // First load to populate cache.
224             clearCacheOnUiThread(awContents, true);
225             loadUrlSync(awContents,
226                         mContentsClient.getOnPageFinishedHelper(),
227                         pageUrl);
228             assertEquals(1, webServer.getRequestCount(pagePath));
229
230             // Load about:blank so next load is not treated as reload by webkit and force
231             // revalidate with the server.
232             loadUrlSync(awContents,
233                         mContentsClient.getOnPageFinishedHelper(),
234                         "about:blank");
235
236             // No clearCache call, so should be loaded from cache.
237             loadUrlSync(awContents,
238                         mContentsClient.getOnPageFinishedHelper(),
239                         pageUrl);
240             assertEquals(1, webServer.getRequestCount(pagePath));
241
242             // Same as above.
243             loadUrlSync(awContents,
244                         mContentsClient.getOnPageFinishedHelper(),
245                         "about:blank");
246
247             // Clear cache, so should hit server again.
248             clearCacheOnUiThread(awContents, true);
249             loadUrlSync(awContents,
250                         mContentsClient.getOnPageFinishedHelper(),
251                         pageUrl);
252             assertEquals(2, webServer.getRequestCount(pagePath));
253         } finally {
254             if (webServer != null) webServer.shutdown();
255         }
256     }
257
258     @SmallTest
259     @Feature({"AndroidWebView"})
260     public void testClearCacheInQuickSuccession() throws Throwable {
261         final AwTestContainerView testContainer =
262                 createAwTestContainerViewOnMainSync(new TestAwContentsClient());
263         final AwContents awContents = testContainer.getAwContents();
264
265         runTestOnUiThread(new Runnable() {
266             @Override
267             public void run() {
268                 for (int i = 0; i < 10; ++i) {
269                     awContents.clearCache(true);
270                 }
271             }
272         });
273     }
274
275     @SmallTest
276     @Feature({"AndroidWebView"})
277     public void testGetFavicon() throws Throwable {
278         AwContents.setShouldDownloadFavicons();
279         final AwTestContainerView testView = createAwTestContainerViewOnMainSync(mContentsClient);
280         final AwContents awContents = testView.getAwContents();
281
282         TestWebServer webServer = null;
283         try {
284             webServer = new TestWebServer(false);
285
286             final String faviconUrl = webServer.setResponseBase64(
287                     "/" + CommonResources.FAVICON_FILENAME, CommonResources.FAVICON_DATA_BASE64,
288                     CommonResources.getImagePngHeaders(false));
289             final String pageUrl = webServer.setResponse("/favicon.html",
290                     CommonResources.FAVICON_STATIC_HTML, null);
291
292             // The getFavicon will return the right icon a certain time after
293             // the page load completes which makes it slightly hard to test.
294             final Bitmap defaultFavicon = awContents.getFavicon();
295
296             getAwSettingsOnUiThread(awContents).setImagesEnabled(true);
297             loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(), pageUrl);
298
299             pollOnUiThread(new Callable<Boolean>() {
300                 @Override
301                 public Boolean call() {
302                     return awContents.getFavicon() != null &&
303                         !awContents.getFavicon().sameAs(defaultFavicon);
304                 }
305             });
306
307             final Object originalFaviconSource = (new URL(faviconUrl)).getContent();
308             final Bitmap originalFavicon =
309                 BitmapFactory.decodeStream((InputStream) originalFaviconSource);
310             assertNotNull(originalFavicon);
311
312             assertTrue(awContents.getFavicon().sameAs(originalFavicon));
313
314         } finally {
315             if (webServer != null) webServer.shutdown();
316         }
317     }
318
319     @Feature({"AndroidWebView", "Downloads"})
320     @SmallTest
321     public void testDownload() throws Throwable {
322         AwTestContainerView testView = createAwTestContainerViewOnMainSync(mContentsClient);
323         AwContents awContents = testView.getAwContents();
324
325         final String data = "download data";
326         final String contentDisposition = "attachment;filename=\"download.txt\"";
327         final String mimeType = "text/plain";
328
329         List<Pair<String, String>> downloadHeaders = new ArrayList<Pair<String, String>>();
330         downloadHeaders.add(Pair.create("Content-Disposition", contentDisposition));
331         downloadHeaders.add(Pair.create("Content-Type", mimeType));
332         downloadHeaders.add(Pair.create("Content-Length", Integer.toString(data.length())));
333
334         TestWebServer webServer = null;
335         try {
336             webServer = new TestWebServer(false);
337             final String pageUrl = webServer.setResponse(
338                     "/download.txt", data, downloadHeaders);
339             final OnDownloadStartHelper downloadStartHelper =
340                 mContentsClient.getOnDownloadStartHelper();
341             final int callCount = downloadStartHelper.getCallCount();
342             loadUrlAsync(awContents, pageUrl);
343             downloadStartHelper.waitForCallback(callCount);
344
345             assertEquals(pageUrl, downloadStartHelper.getUrl());
346             assertEquals(contentDisposition, downloadStartHelper.getContentDisposition());
347             assertEquals(mimeType, downloadStartHelper.getMimeType());
348             assertEquals(data.length(), downloadStartHelper.getContentLength());
349         } finally {
350             if (webServer != null) webServer.shutdown();
351         }
352     }
353
354     @Feature({"AndroidWebView", "setNetworkAvailable"})
355     @SmallTest
356     public void testSetNetworkAvailable() throws Throwable {
357         AwTestContainerView testView = createAwTestContainerViewOnMainSync(mContentsClient);
358         AwContents awContents = testView.getAwContents();
359         String SCRIPT = "navigator.onLine";
360
361         enableJavaScriptOnUiThread(awContents);
362         loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(), "about:blank");
363
364         // Default to "online".
365         assertEquals("true", executeJavaScriptAndWaitForResult(awContents, mContentsClient,
366               SCRIPT));
367
368         // Forcing "offline".
369         setNetworkAvailableOnUiThread(awContents, false);
370         assertEquals("false", executeJavaScriptAndWaitForResult(awContents, mContentsClient,
371               SCRIPT));
372
373         // Forcing "online".
374         setNetworkAvailableOnUiThread(awContents, true);
375         assertEquals("true", executeJavaScriptAndWaitForResult(awContents, mContentsClient,
376               SCRIPT));
377     }
378
379
380     static class JavaScriptObject {
381         private CallbackHelper mCallbackHelper;
382         public JavaScriptObject(CallbackHelper callbackHelper) {
383             mCallbackHelper = callbackHelper;
384         }
385
386         public void run() {
387             mCallbackHelper.notifyCalled();
388         }
389     }
390
391     @Feature({"AndroidWebView", "JavaBridge"})
392     @SmallTest
393     public void testJavaBridge() throws Throwable {
394         final AwTestContainerView testView = createAwTestContainerViewOnMainSync(mContentsClient);
395         final CallbackHelper callback = new CallbackHelper();
396
397         runTestOnUiThread(new Runnable() {
398             @Override
399             public void run() {
400                 AwContents awContents = testView.getAwContents();
401                 AwSettings awSettings = awContents.getSettings();
402                 awSettings.setJavaScriptEnabled(true);
403                 awContents.addPossiblyUnsafeJavascriptInterface(
404                         new JavaScriptObject(callback), "bridge", null);
405                 awContents.evaluateJavaScriptEvenIfNotYetNavigated(
406                         "javascript:window.bridge.run();");
407             }
408         });
409         callback.waitForCallback(0, 1, WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
410     }
411
412     @Feature({"AndroidWebView"})
413     @SmallTest
414     public void testEscapingOfErrorPage() throws Throwable {
415         AwTestContainerView testView = createAwTestContainerViewOnMainSync(mContentsClient);
416         AwContents awContents = testView.getAwContents();
417         String SCRIPT = "window.failed == true";
418
419         enableJavaScriptOnUiThread(awContents);
420         CallbackHelper onPageFinishedHelper = mContentsClient.getOnPageFinishedHelper();
421         int currentCallCount = onPageFinishedHelper.getCallCount();
422         loadUrlAsync(awContents,
423                 "file:///file-that-does-not-exist#<script>window.failed = true;</script>");
424         // We must wait for two onPageFinished callbacks. One for the original failing URL, and
425         // one for the error page that we then display to the user.
426         onPageFinishedHelper.waitForCallback(currentCallCount, 2, WAIT_TIMEOUT_MS,
427                                              TimeUnit.MILLISECONDS);
428
429         assertEquals("false", executeJavaScriptAndWaitForResult(awContents, mContentsClient,
430                 SCRIPT));
431     }
432
433     @Feature({"AndroidWebView"})
434     @SmallTest
435     public void testCanInjectHeaders() throws Throwable {
436         final AwTestContainerView testContainer =
437                 createAwTestContainerViewOnMainSync(mContentsClient);
438         final AwContents awContents = testContainer.getAwContents();
439
440         TestWebServer webServer = null;
441         try {
442             webServer = new TestWebServer(false);
443             final String pagePath = "/test_can_inject_headers.html";
444             final String pageUrl = webServer.setResponse(
445                     pagePath, "<html><body>foo</body></html>", null);
446
447             final Map<String, String> extraHeaders = new HashMap<String, String>();
448             extraHeaders.put("Referer", "foo");
449             extraHeaders.put("X-foo", "bar");
450             loadUrlSync(awContents, mContentsClient.getOnPageFinishedHelper(),
451                     webServer.getResponseUrl(pagePath), extraHeaders);
452
453             assertEquals(1, webServer.getRequestCount(pagePath));
454
455             HttpRequest request = webServer.getLastRequest(pagePath);
456             assertNotNull(request);
457
458             for (Map.Entry<String, String> value : extraHeaders.entrySet()) {
459                 String header = value.getKey();
460                 Header[] matchingHeaders = request.getHeaders(header);
461                 assertEquals("header " + header + " not found", 1, matchingHeaders.length);
462                 assertEquals(value.getValue(), matchingHeaders[0].getValue());
463             }
464         } finally {
465             if (webServer != null) webServer.shutdown();
466         }
467     }
468
469 }