GeolocationPermissions.java revision 69e2eff270be8c61fc902fcb337097e86c601295
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 get Geolocation permissions from, and set them on the
31 * WebView. For example, it could be used to allow a user to manage Geolocation
32 * permissions from a browser's UI.
33 *
34 * Permissions are managed on a per-origin basis, as required by the
35 * Geolocation spec - http://dev.w3.org/geo/api/spec-source.html. An origin
36 * specifies the scheme, host and port of particular frame. An origin is
37 * represented here as a string, using the output of
38 * WebCore::SecurityOrigin::toString.
39 *
40 * This class is the Java counterpart of the WebKit C++ GeolocationPermissions
41 * class. It simply marshalls calls from the UI thread to the WebKit thread.
42 *
43 * Within WebKit, Geolocation permissions may be applied either temporarily
44 * (for the duration of the page) or permanently. This class deals only with
45 * permanent permissions.
46 */
47public final class GeolocationPermissions {
48    /**
49     * Callback interface used by the browser to report a Geolocation permission
50     * state set by the user in response to a permissions prompt.
51     */
52    public interface Callback {
53        public void invoke(String origin, boolean allow, boolean remember);
54    };
55
56    // Log tag
57    private static final String TAG = "geolocationPermissions";
58
59    // Global instance
60    private static GeolocationPermissions sInstance;
61
62    private Handler mHandler;
63    private Handler mUIHandler;
64
65    // A queue to store messages until the handler is ready.
66    private Vector<Message> mQueuedMessages;
67
68    // Message ids
69    static final int GET_ORIGINS = 0;
70    static final int GET_ALLOWED = 1;
71    static final int CLEAR = 2;
72    static final int ALLOW = 3;
73    static final int CLEAR_ALL = 4;
74
75    // Message ids on the UI thread
76    static final int RETURN_ORIGINS = 0;
77    static final int RETURN_ALLOWED = 1;
78
79    private static final String ORIGINS = "origins";
80    private static final String ORIGIN = "origin";
81    private static final String CALLBACK = "callback";
82    private static final String ALLOWED = "allowed";
83
84    /**
85     * Gets the singleton instance of the class.
86     */
87    public static GeolocationPermissions getInstance() {
88      if (sInstance == null) {
89          sInstance = new GeolocationPermissions();
90      }
91      return sInstance;
92    }
93
94    /**
95     * Creates the UI message handler. Must be called on the UI thread.
96     * @hide
97     */
98    public void createUIHandler() {
99        if (mUIHandler == null) {
100            mUIHandler = new Handler() {
101                @Override
102                public void handleMessage(Message msg) {
103                    // Runs on the UI thread.
104                    switch (msg.what) {
105                        case RETURN_ORIGINS: {
106                            Map values = (Map) msg.obj;
107                            Set<String> origins = (Set<String>) values.get(ORIGINS);
108                            ValueCallback<Set<String> > callback = (ValueCallback<Set<String> >) values.get(CALLBACK);
109                            callback.onReceiveValue(origins);
110                        } break;
111                        case RETURN_ALLOWED: {
112                            Map values = (Map) msg.obj;
113                            Boolean allowed = (Boolean) values.get(ALLOWED);
114                            ValueCallback<Boolean> callback = (ValueCallback<Boolean>) values.get(CALLBACK);
115                            callback.onReceiveValue(allowed);
116                        } break;
117                    }
118                }
119            };
120        }
121    }
122
123    /**
124     * Creates the message handler. Must be called on the WebKit thread.
125     * @hide
126     */
127    public synchronized void createHandler() {
128        if (mHandler == null) {
129            mHandler = new Handler() {
130                @Override
131                public void handleMessage(Message msg) {
132                    // Runs on the WebKit thread.
133                    switch (msg.what) {
134                        case GET_ORIGINS: {
135                            Set origins = nativeGetOrigins();
136                            ValueCallback callback = (ValueCallback) msg.obj;
137                            Map values = new HashMap<String, Object>();
138                            values.put(CALLBACK, callback);
139                            values.put(ORIGINS, origins);
140                            postUIMessage(Message.obtain(null, RETURN_ORIGINS, values));
141                            } break;
142                        case GET_ALLOWED: {
143                            Map values = (Map) msg.obj;
144                            String origin = (String) values.get(ORIGIN);
145                            ValueCallback callback = (ValueCallback) values.get(CALLBACK);
146                            boolean allowed = nativeGetAllowed(origin);
147                            Map retValues = new HashMap<String, Object>();
148                            retValues.put(CALLBACK, callback);
149                            retValues.put(ALLOWED, Boolean.valueOf(allowed));
150                            postUIMessage(Message.obtain(null, RETURN_ALLOWED, retValues));
151                            } break;
152                        case CLEAR:
153                            nativeClear((String) msg.obj);
154                            break;
155                        case ALLOW:
156                            nativeAllow((String) msg.obj);
157                            break;
158                        case CLEAR_ALL:
159                            nativeClearAll();
160                            break;
161                    }
162                }
163            };
164
165            // Handle the queued messages
166            if (mQueuedMessages != null) {
167                while (!mQueuedMessages.isEmpty()) {
168                    mHandler.sendMessage(mQueuedMessages.remove(0));
169                }
170                mQueuedMessages = null;
171            }
172        }
173    }
174
175    /**
176     * Utility function to send a message to our handler.
177     */
178    private synchronized void postMessage(Message msg) {
179        if (mHandler == null) {
180            if (mQueuedMessages == null) {
181                mQueuedMessages = new Vector<Message>();
182            }
183            mQueuedMessages.add(msg);
184        } else {
185            mHandler.sendMessage(msg);
186        }
187    }
188
189    /**
190     * Utility function to send a message to the handler on the UI thread
191     */
192    private void postUIMessage(Message msg) {
193        if (mUIHandler != null) {
194            mUIHandler.sendMessage(msg);
195        }
196    }
197
198    /**
199     * Gets the set of origins for which Geolocation permissions are stored.
200     * Note that we represent the origins as strings. These are created using
201     * WebCore::SecurityOrigin::toString(). As long as all 'HTML 5 modules'
202     * (Database, Geolocation etc) do so, it's safe to match up origins based
203     * on this string.
204     *
205     * Callback is a ValueCallback object whose onReceiveValue method will be
206     * called asynchronously with the set of origins.
207     */
208    public void getOrigins(ValueCallback<Set<String> > callback) {
209        if (callback != null) {
210            if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
211                Set origins = nativeGetOrigins();
212                callback.onReceiveValue(origins);
213            } else {
214                postMessage(Message.obtain(null, GET_ORIGINS, callback));
215            }
216        }
217    }
218
219    /**
220     * Gets the permission state for the specified origin.
221     *
222     * Callback is a ValueCallback object whose onReceiveValue method will be
223     * called asynchronously with the permission state for the origin.
224     */
225    public void getAllowed(String origin, ValueCallback<Boolean> callback) {
226        if (callback == null) {
227            return;
228        }
229        if (origin == null) {
230            callback.onReceiveValue(null);
231            return;
232        }
233        if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
234            boolean allowed = nativeGetAllowed(origin);
235            callback.onReceiveValue(new Boolean(allowed));
236        } else {
237            Map values = new HashMap<String, Object>();
238            values.put(ORIGIN, origin);
239            values.put(CALLBACK, callback);
240            postMessage(Message.obtain(null, GET_ALLOWED, values));
241        }
242    }
243
244    /**
245     * Clears the permission state for the specified origin. This method may be
246     * called before the WebKit thread has intialized the message handler.
247     * Messages will be queued until this time.
248     */
249    public void clear(String origin) {
250        // Called on the UI thread.
251        postMessage(Message.obtain(null, CLEAR, origin));
252    }
253
254    /**
255     * Allows the specified origin. This method may be called before the WebKit
256     * thread has intialized the message handler. Messages will be queued until
257     * this time.
258     */
259    public void allow(String origin) {
260        // Called on the UI thread.
261        postMessage(Message.obtain(null, ALLOW, origin));
262    }
263
264    /**
265     * Clears the permission state for all origins.
266     */
267    public void clearAll() {
268        // Called on the UI thread.
269        postMessage(Message.obtain(null, CLEAR_ALL));
270    }
271
272    // Native functions, run on the WebKit thread.
273    private static native Set nativeGetOrigins();
274    private static native boolean nativeGetAllowed(String origin);
275    private static native void nativeClear(String origin);
276    private static native void nativeAllow(String origin);
277    private static native void nativeClearAll();
278}
279