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