1// Copyright 2013 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
5package org.chromium.android_webview.test;
6
7import android.test.suitebuilder.annotation.MediumTest;
8import android.webkit.GeolocationPermissions;
9
10import org.chromium.android_webview.AwContents;
11import org.chromium.base.test.util.Feature;
12import org.chromium.content.browser.LocationProviderFactory;
13import org.chromium.content.browser.test.util.MockLocationProvider;
14
15import java.util.concurrent.Callable;
16
17/**
18 * Test suite for Geolocation in AwContents. Smoke tests for
19 * basic functionality, and tests to ensure the AwContents.onPause
20 * and onResume APIs affect Geolocation as expected.
21 */
22public class GeolocationTest extends AwTestBase {
23    private TestAwContentsClient mContentsClient;
24    private AwContents mAwContents;
25    private MockLocationProvider mMockLocationProvider;
26
27    private static final String RAW_HTML =
28            "<!DOCTYPE html>\n" +
29            "<html>\n" +
30            "  <head>\n" +
31            "    <title>Geolocation</title>\n" +
32            "    <script>\n" +
33            "      var positionCount = 0;\n" +
34            "      function gotPos(position) {\n" +
35            "        positionCount++;\n" +
36            "      }\n" +
37            "      function initiate_getCurrentPosition() {\n" +
38            "        navigator.geolocation.getCurrentPosition(\n" +
39            "            gotPos, function() { }, { });\n" +
40            "      }\n" +
41            "      function initiate_watchPosition() {\n" +
42            "        navigator.geolocation.watchPosition(\n" +
43            "            gotPos, function() { }, { });\n" +
44            "      }\n" +
45            "    </script>\n" +
46            "  </head>\n" +
47            "  <body>\n" +
48            "  </body>\n" +
49            "</html>";
50
51    @Override
52    public void setUp() throws Exception {
53        super.setUp();
54        mContentsClient = new TestAwContentsClient() {
55                @Override
56                public void onGeolocationPermissionsShowPrompt(String origin,
57                        GeolocationPermissions.Callback callback) {
58                    callback.invoke(origin, true, true);
59                }
60        };
61        mAwContents = createAwTestContainerViewOnMainSync(mContentsClient).getAwContents();
62        enableJavaScriptOnUiThread(mAwContents);
63        setupGeolocation();
64    }
65
66    @Override
67    public void tearDown() throws Exception {
68        mMockLocationProvider.stopUpdates();
69        super.tearDown();
70    }
71
72    private void setupGeolocation() {
73        getInstrumentation().runOnMainSync(new Runnable() {
74            @Override
75            public void run() {
76                mAwContents.getSettings().setGeolocationEnabled(true);
77            }
78        });
79        mMockLocationProvider = new MockLocationProvider();
80        LocationProviderFactory.setLocationProviderImpl(mMockLocationProvider);
81    }
82
83    private int getPositionCountFromJS() {
84        int result = -1;
85        try {
86            result = Integer.parseInt(executeJavaScriptAndWaitForResult(
87                    mAwContents, mContentsClient, "positionCount"));
88        } catch (Exception e) {
89            fail("Unable to get positionCount");
90        }
91        return result;
92    }
93
94    private void ensureGeolocationRunning(final boolean running) throws Exception {
95        poll(new Callable<Boolean>() {
96            @Override
97            public Boolean call() throws Exception {
98                return mMockLocationProvider.isRunning() == running;
99            }
100        });
101    }
102
103
104    /**
105     * Ensure that a call to navigator.getCurrentPosition works in WebView.
106     */
107    @MediumTest
108    @Feature({"AndroidWebView"})
109    public void testGetPosition() throws Throwable {
110        loadDataSync(mAwContents, mContentsClient.getOnPageFinishedHelper(),
111                RAW_HTML, "text/html", false);
112
113        mAwContents.evaluateJavaScript("initiate_getCurrentPosition();", null);
114
115        poll(new Callable<Boolean>() {
116            @Override
117            public Boolean call() throws Exception {
118               return getPositionCountFromJS() == 1;
119            }
120        });
121
122        mAwContents.evaluateJavaScript("initiate_getCurrentPosition();", null);
123        poll(new Callable<Boolean>() {
124            @Override
125            public Boolean call() throws Exception {
126                return getPositionCountFromJS() == 2;
127            }
128        });
129    }
130
131    /**
132     * Ensure that a call to navigator.watchPosition works in WebView.
133     */
134    @MediumTest
135    @Feature({"AndroidWebView"})
136    public void testWatchPosition() throws Throwable {
137        loadDataSync(mAwContents, mContentsClient.getOnPageFinishedHelper(),
138                RAW_HTML, "text/html", false);
139
140        mAwContents.evaluateJavaScript("initiate_watchPosition();", null);
141
142        poll(new Callable<Boolean>() {
143            @Override
144            public Boolean call() throws Exception {
145                return getPositionCountFromJS() > 1;
146            }
147        });
148    }
149
150    @MediumTest
151    @Feature({"AndroidWebView"})
152    public void testPauseGeolocationOnPause() throws Throwable {
153        // Start a watch going.
154        loadDataSync(mAwContents, mContentsClient.getOnPageFinishedHelper(),
155                RAW_HTML, "text/html", false);
156
157        mAwContents.evaluateJavaScript("initiate_watchPosition();", null);
158
159        poll(new Callable<Boolean>() {
160            @Override
161            public Boolean call() throws Exception {
162                return getPositionCountFromJS() > 1;
163            }
164        });
165
166        ensureGeolocationRunning(true);
167
168        getInstrumentation().runOnMainSync(new Runnable() {
169            @Override
170            public void run() {
171                mAwContents.onPause();
172            }
173        });
174
175        ensureGeolocationRunning(false);
176
177        try {
178            executeJavaScriptAndWaitForResult(mAwContents, mContentsClient, "positionCount = 0");
179        } catch (Exception e) {
180            fail("Unable to clear positionCount");
181        }
182        assertEquals(0, getPositionCountFromJS());
183
184        getInstrumentation().runOnMainSync(new Runnable() {
185            @Override
186            public void run() {
187                mAwContents.onResume();
188            }
189        });
190
191        ensureGeolocationRunning(true);
192
193        poll(new Callable<Boolean>() {
194            @Override
195            public Boolean call() throws Exception {
196                return getPositionCountFromJS() > 1;
197            }
198        });
199    }
200
201    @MediumTest
202    @Feature({"AndroidWebView"})
203    public void testPauseAwContentsBeforeNavigating() throws Throwable {
204        getInstrumentation().runOnMainSync(new Runnable() {
205            @Override
206            public void run() {
207                mAwContents.onPause();
208            }
209        });
210
211        // Start a watch going.
212        loadDataSync(mAwContents, mContentsClient.getOnPageFinishedHelper(),
213                RAW_HTML, "text/html", false);
214
215        mAwContents.evaluateJavaScript("initiate_watchPosition();", null);
216
217        assertEquals(0, getPositionCountFromJS());
218
219        ensureGeolocationRunning(false);
220
221        getInstrumentation().runOnMainSync(new Runnable() {
222            @Override
223            public void run() {
224                mAwContents.onResume();
225            }
226        });
227
228        ensureGeolocationRunning(true);
229
230        poll(new Callable<Boolean>() {
231            @Override
232            public Boolean call() throws Exception {
233                return getPositionCountFromJS() > 1;
234            }
235        });
236    }
237
238    @MediumTest
239    @Feature({"AndroidWebView"})
240    public void testResumeWhenNotStarted() throws Throwable {
241        getInstrumentation().runOnMainSync(new Runnable() {
242            @Override
243            public void run() {
244                mAwContents.onPause();
245            }
246        });
247
248        loadDataSync(mAwContents, mContentsClient.getOnPageFinishedHelper(),
249                RAW_HTML, "text/html", false);
250
251        getInstrumentation().runOnMainSync(new Runnable() {
252            @Override
253            public void run() {
254                mAwContents.onResume();
255            }
256        });
257
258        ensureGeolocationRunning(false);
259    }
260
261}
262