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 17 package android.webkit; 18 19 import android.os.Handler; 20 import android.os.Message; 21 import android.util.Log; 22 import java.util.HashMap; 23 import java.util.HashSet; 24 import java.util.Map; 25 import java.util.Set; 26 import 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 */ 47 public 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 { invoke(String origin, boolean allow, boolean remember)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 */ getInstance()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 */ createUIHandler()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 */ createHandler()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 */ postMessage(Message msg)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 */ postUIMessage(Message msg)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 */ getOrigins(ValueCallback<Set<String> > callback)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 */ getAllowed(String origin, ValueCallback<Boolean> callback)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 */ clear(String origin)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 */ allow(String origin)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 */ clearAll()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. nativeGetOrigins()273 private static native Set nativeGetOrigins(); nativeGetAllowed(String origin)274 private static native boolean nativeGetAllowed(String origin); nativeClear(String origin)275 private static native void nativeClear(String origin); nativeAllow(String origin)276 private static native void nativeAllow(String origin); nativeClearAll()277 private static native void nativeClearAll(); 278 } 279