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