GeolocationPermissions.java revision 7351adf76a97b07bb2d777c56e78752cb7834bb0
1/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.webkit;
18
19import android.os.Handler;
20import android.os.Message;
21import android.util.Log;
22import java.util.HashMap;
23import java.util.HashSet;
24import java.util.Map;
25import java.util.Set;
26import java.util.Vector;
27
28
29/**
30 * This class is used to manage permissions for the WebView's Geolocation
31 * JavaScript API.
32 *
33 * Geolocation permissions are applied to an origin, which consists of the
34 * host, scheme and port of a URI. In order for web content to use the
35 * Geolocation API, permission must be granted for that content's origin.
36 *
37 * This class stores Geolocation permissions. An origin's permission state can
38 * be either allowed or denied. This class uses Strings to represent
39 * an origin.
40 *
41 * When an origin attempts to use the Geolocation API, but no permission state
42 * is currently set for that origin,
43 * {@link WebChromeClient#onGeolocationPermissionsShowPrompt(String,GeolocationPermissions.Callback) WebChromeClient.onGeolocationPermissionsShowPrompt()}
44 * is called. This allows the permission state to be set for that origin.
45 *
46 * The methods of this class can be used to modify and interrogate the stored
47 * Geolocation permissions at any time.
48 */
49// This class is the Java counterpart of the WebKit C++ GeolocationPermissions
50// class. It simply marshalls calls from the UI thread to the WebKit thread.
51//
52// Within WebKit, Geolocation permissions may be applied either temporarily
53// (for the duration of the page) or permanently. This class deals only with
54// permanent permissions.
55public final class GeolocationPermissions {
56    /**
57     * A callback interface used by the host application to set the Geolocation
58     * permission state for an origin.
59     */
60    public interface Callback {
61        /**
62         * Set the Geolocation permission state for the supplied origin.
63         * @param origin The origin for which permissions are set.
64         * @param allow Whether or not the origin should be allowed to use the
65         *              Geolocation API.
66         * @param retain Whether the permission should be retained beyond the
67         *               lifetime of a page currently being displayed by a
68         *               WebView.
69         */
70        public void invoke(String origin, boolean allow, boolean retain);
71    };
72
73    // Log tag
74    private static final String TAG = "geolocationPermissions";
75
76    // Global instance
77    private static GeolocationPermissions sInstance;
78
79    private Handler mHandler;
80    private Handler mUIHandler;
81
82    // A queue to store messages until the handler is ready.
83    private Vector<Message> mQueuedMessages;
84
85    // Message ids
86    static final int GET_ORIGINS = 0;
87    static final int GET_ALLOWED = 1;
88    static final int CLEAR = 2;
89    static final int ALLOW = 3;
90    static final int CLEAR_ALL = 4;
91
92    // Message ids on the UI thread
93    static final int RETURN_ORIGINS = 0;
94    static final int RETURN_ALLOWED = 1;
95
96    private static final String ORIGINS = "origins";
97    private static final String ORIGIN = "origin";
98    private static final String CALLBACK = "callback";
99    private static final String ALLOWED = "allowed";
100
101    /**
102     * Get the singleton instance of this class.
103     * @return The singleton {@link GeolocationPermissions} instance.
104     */
105    public static GeolocationPermissions getInstance() {
106      if (sInstance == null) {
107          sInstance = new GeolocationPermissions();
108      }
109      return sInstance;
110    }
111
112    /**
113     * Creates the UI message handler. Must be called on the UI thread.
114     * @hide
115     */
116    public void createUIHandler() {
117        if (mUIHandler == null) {
118            mUIHandler = new Handler() {
119                @Override
120                public void handleMessage(Message msg) {
121                    // Runs on the UI thread.
122                    switch (msg.what) {
123                        case RETURN_ORIGINS: {
124                            Map values = (Map) msg.obj;
125                            Set<String> origins = (Set<String>) values.get(ORIGINS);
126                            ValueCallback<Set<String> > callback = (ValueCallback<Set<String> >) values.get(CALLBACK);
127                            callback.onReceiveValue(origins);
128                        } break;
129                        case RETURN_ALLOWED: {
130                            Map values = (Map) msg.obj;
131                            Boolean allowed = (Boolean) values.get(ALLOWED);
132                            ValueCallback<Boolean> callback = (ValueCallback<Boolean>) values.get(CALLBACK);
133                            callback.onReceiveValue(allowed);
134                        } break;
135                    }
136                }
137            };
138        }
139    }
140
141    /**
142     * Creates the message handler. Must be called on the WebKit thread.
143     * @hide
144     */
145    public synchronized void createHandler() {
146        if (mHandler == null) {
147            mHandler = new Handler() {
148                @Override
149                public void handleMessage(Message msg) {
150                    // Runs on the WebKit thread.
151                    switch (msg.what) {
152                        case GET_ORIGINS: {
153                            Set origins = nativeGetOrigins();
154                            ValueCallback callback = (ValueCallback) msg.obj;
155                            Map values = new HashMap<String, Object>();
156                            values.put(CALLBACK, callback);
157                            values.put(ORIGINS, origins);
158                            postUIMessage(Message.obtain(null, RETURN_ORIGINS, values));
159                            } break;
160                        case GET_ALLOWED: {
161                            Map values = (Map) msg.obj;
162                            String origin = (String) values.get(ORIGIN);
163                            ValueCallback callback = (ValueCallback) values.get(CALLBACK);
164                            boolean allowed = nativeGetAllowed(origin);
165                            Map retValues = new HashMap<String, Object>();
166                            retValues.put(CALLBACK, callback);
167                            retValues.put(ALLOWED, Boolean.valueOf(allowed));
168                            postUIMessage(Message.obtain(null, RETURN_ALLOWED, retValues));
169                            } break;
170                        case CLEAR:
171                            nativeClear((String) msg.obj);
172                            break;
173                        case ALLOW:
174                            nativeAllow((String) msg.obj);
175                            break;
176                        case CLEAR_ALL:
177                            nativeClearAll();
178                            break;
179                    }
180                }
181            };
182
183            // Handle the queued messages
184            if (mQueuedMessages != null) {
185                while (!mQueuedMessages.isEmpty()) {
186                    mHandler.sendMessage(mQueuedMessages.remove(0));
187                }
188                mQueuedMessages = null;
189            }
190        }
191    }
192
193    /**
194     * Utility function to send a message to our handler.
195     */
196    private synchronized void postMessage(Message msg) {
197        if (mHandler == null) {
198            if (mQueuedMessages == null) {
199                mQueuedMessages = new Vector<Message>();
200            }
201            mQueuedMessages.add(msg);
202        } else {
203            mHandler.sendMessage(msg);
204        }
205    }
206
207    /**
208     * Utility function to send a message to the handler on the UI thread
209     */
210    private void postUIMessage(Message msg) {
211        if (mUIHandler != null) {
212            mUIHandler.sendMessage(msg);
213        }
214    }
215
216    /**
217     * Get the set of origins for which Geolocation permissions are stored.
218     * @param callback A {@link ValueCallback} to receive the result of this
219     *                 request. This object's
220     *                 {@link ValueCallback#onReceiveValue(T) onReceiveValue()}
221     *                 method will be invoked asynchronously with a set of
222     *                 Strings containing the origins for which Geolocation
223     *                 permissions are stored.
224     */
225    // Note that we represent the origins as strings. These are created using
226    // WebCore::SecurityOrigin::toString(). As long as all 'HTML 5 modules'
227    // (Database, Geolocation etc) do so, it's safe to match up origins based
228    // on this string.
229    public void getOrigins(ValueCallback<Set<String> > callback) {
230        if (callback != null) {
231            if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
232                Set origins = nativeGetOrigins();
233                callback.onReceiveValue(origins);
234            } else {
235                postMessage(Message.obtain(null, GET_ORIGINS, callback));
236            }
237        }
238    }
239
240    /**
241     * Get the Geolocation permission state for the specified origin.
242     * @param origin The origin for which Geolocation permission is requested.
243     * @param callback A {@link ValueCallback} to receive the result of this
244     *                 request. This object's
245     *                 {@link ValueCallback#onReceiveValue(T) onReceiveValue()}
246     *                 method will be invoked asynchronously with a boolean
247     *                 indicating whether or not the origin can use the
248     *                 Geolocation API.
249     */
250    public void getAllowed(String origin, ValueCallback<Boolean> callback) {
251        if (callback == null) {
252            return;
253        }
254        if (origin == null) {
255            callback.onReceiveValue(null);
256            return;
257        }
258        if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
259            boolean allowed = nativeGetAllowed(origin);
260            callback.onReceiveValue(new Boolean(allowed));
261        } else {
262            Map values = new HashMap<String, Object>();
263            values.put(ORIGIN, origin);
264            values.put(CALLBACK, callback);
265            postMessage(Message.obtain(null, GET_ALLOWED, values));
266        }
267    }
268
269    /**
270     * Clear the Geolocation permission state for the specified origin.
271     * @param origin The origin for which Geolocation permissions are cleared.
272     */
273    // This method may be called before the WebKit
274    // thread has intialized the message handler. Messages will be queued until
275    // this time.
276    public void clear(String origin) {
277        // Called on the UI thread.
278        postMessage(Message.obtain(null, CLEAR, origin));
279    }
280
281    /**
282     * Allow the specified origin to use the Geolocation API.
283     * @param origin The origin for which Geolocation API use is allowed.
284     */
285    // This method may be called before the WebKit
286    // thread has intialized the message handler. Messages will be queued until
287    // this time.
288    public void allow(String origin) {
289        // Called on the UI thread.
290        postMessage(Message.obtain(null, ALLOW, origin));
291    }
292
293    /**
294     * Clear the Geolocation permission state for all origins.
295     */
296    public void clearAll() {
297        // Called on the UI thread.
298        postMessage(Message.obtain(null, CLEAR_ALL));
299    }
300
301    // Native functions, run on the WebKit thread.
302    private static native Set nativeGetOrigins();
303    private static native boolean nativeGetAllowed(String origin);
304    private static native void nativeClear(String origin);
305    private static native void nativeAllow(String origin);
306    private static native void nativeClearAll();
307}
308