/* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.webkit; import android.os.Handler; import android.os.Message; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.Vector; /** * This class is used to manage permissions for the WebView's Geolocation * JavaScript API. * * Geolocation permissions are applied to an origin, which consists of the * host, scheme and port of a URI. In order for web content to use the * Geolocation API, permission must be granted for that content's origin. * * This class stores Geolocation permissions. An origin's permission state can * be either allowed or denied. This class uses Strings to represent * an origin. * * When an origin attempts to use the Geolocation API, but no permission state * is currently set for that origin, * {@link WebChromeClient#onGeolocationPermissionsShowPrompt(String,GeolocationPermissions.Callback) WebChromeClient.onGeolocationPermissionsShowPrompt()} * is called. This allows the permission state to be set for that origin. * * The methods of this class can be used to modify and interrogate the stored * Geolocation permissions at any time. */ // This class is the Java counterpart of the WebKit C++ GeolocationPermissions // class. It simply marshals calls from the UI thread to the WebKit thread. // // Within WebKit, Geolocation permissions may be applied either temporarily // (for the duration of the page) or permanently. This class deals only with // permanent permissions. public class GeolocationPermissions { /** * A callback interface used by the host application to set the Geolocation * permission state for an origin. */ public interface Callback { /** * Set the Geolocation permission state for the supplied origin. * @param origin The origin for which permissions are set. * @param allow Whether or not the origin should be allowed to use the * Geolocation API. * @param retain Whether the permission should be retained beyond the * lifetime of a page currently being displayed by a * WebView. */ public void invoke(String origin, boolean allow, boolean retain); }; // Global instance private static GeolocationPermissions sInstance; private Handler mHandler; private Handler mUIHandler; // A queue to store messages until the handler is ready. private Vector mQueuedMessages; // Message ids static final int GET_ORIGINS = 0; static final int GET_ALLOWED = 1; static final int CLEAR = 2; static final int ALLOW = 3; static final int CLEAR_ALL = 4; // Message ids on the UI thread static final int RETURN_ORIGINS = 0; static final int RETURN_ALLOWED = 1; private static final String ORIGINS = "origins"; private static final String ORIGIN = "origin"; private static final String CALLBACK = "callback"; private static final String ALLOWED = "allowed"; /** * Get the singleton instance of this class. * @return The singleton {@link GeolocationPermissions} instance. */ public static GeolocationPermissions getInstance() { if (sInstance == null) { sInstance = new GeolocationPermissions(); } return sInstance; } /** * Creates the UI message handler. Must be called on the UI thread. * @hide */ public void createUIHandler() { if (mUIHandler == null) { mUIHandler = new Handler() { @Override public void handleMessage(Message msg) { // Runs on the UI thread. switch (msg.what) { case RETURN_ORIGINS: { Map values = (Map) msg.obj; Set origins = (Set) values.get(ORIGINS); ValueCallback > callback = (ValueCallback >) values.get(CALLBACK); callback.onReceiveValue(origins); } break; case RETURN_ALLOWED: { Map values = (Map) msg.obj; Boolean allowed = (Boolean) values.get(ALLOWED); ValueCallback callback = (ValueCallback) values.get(CALLBACK); callback.onReceiveValue(allowed); } break; } } }; } } /** * Creates the message handler. Must be called on the WebKit thread. * @hide */ public synchronized void createHandler() { if (mHandler == null) { mHandler = new Handler() { @Override public void handleMessage(Message msg) { // Runs on the WebKit thread. switch (msg.what) { case GET_ORIGINS: { Set origins = nativeGetOrigins(); ValueCallback callback = (ValueCallback) msg.obj; Map values = new HashMap(); values.put(CALLBACK, callback); values.put(ORIGINS, origins); postUIMessage(Message.obtain(null, RETURN_ORIGINS, values)); } break; case GET_ALLOWED: { Map values = (Map) msg.obj; String origin = (String) values.get(ORIGIN); ValueCallback callback = (ValueCallback) values.get(CALLBACK); boolean allowed = nativeGetAllowed(origin); Map retValues = new HashMap(); retValues.put(CALLBACK, callback); retValues.put(ALLOWED, Boolean.valueOf(allowed)); postUIMessage(Message.obtain(null, RETURN_ALLOWED, retValues)); } break; case CLEAR: nativeClear((String) msg.obj); break; case ALLOW: nativeAllow((String) msg.obj); break; case CLEAR_ALL: nativeClearAll(); break; } } }; // Handle the queued messages if (mQueuedMessages != null) { while (!mQueuedMessages.isEmpty()) { mHandler.sendMessage(mQueuedMessages.remove(0)); } mQueuedMessages = null; } } } /** * Utility function to send a message to our handler. */ private synchronized void postMessage(Message msg) { if (mHandler == null) { if (mQueuedMessages == null) { mQueuedMessages = new Vector(); } mQueuedMessages.add(msg); } else { mHandler.sendMessage(msg); } } /** * Utility function to send a message to the handler on the UI thread */ private void postUIMessage(Message msg) { if (mUIHandler != null) { mUIHandler.sendMessage(msg); } } /** * Get the set of origins for which Geolocation permissions are stored. * @param callback A {@link ValueCallback} to receive the result of this * request. This object's * {@link ValueCallback#onReceiveValue(T) onReceiveValue()} * method will be invoked asynchronously with a set of * Strings containing the origins for which Geolocation * permissions are stored. */ // Note that we represent the origins as strings. These are created using // WebCore::SecurityOrigin::toString(). As long as all 'HTML 5 modules' // (Database, Geolocation etc) do so, it's safe to match up origins based // on this string. public void getOrigins(ValueCallback > callback) { if (callback != null) { if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) { Set origins = nativeGetOrigins(); callback.onReceiveValue(origins); } else { postMessage(Message.obtain(null, GET_ORIGINS, callback)); } } } /** * Get the Geolocation permission state for the specified origin. * @param origin The origin for which Geolocation permission is requested. * @param callback A {@link ValueCallback} to receive the result of this * request. This object's * {@link ValueCallback#onReceiveValue(T) onReceiveValue()} * method will be invoked asynchronously with a boolean * indicating whether or not the origin can use the * Geolocation API. */ public void getAllowed(String origin, ValueCallback callback) { if (callback == null) { return; } if (origin == null) { callback.onReceiveValue(null); return; } if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) { boolean allowed = nativeGetAllowed(origin); callback.onReceiveValue(Boolean.valueOf(allowed)); } else { Map values = new HashMap(); values.put(ORIGIN, origin); values.put(CALLBACK, callback); postMessage(Message.obtain(null, GET_ALLOWED, values)); } } /** * Clear the Geolocation permission state for the specified origin. * @param origin The origin for which Geolocation permissions are cleared. */ // This method may be called before the WebKit // thread has intialized the message handler. Messages will be queued until // this time. public void clear(String origin) { // Called on the UI thread. postMessage(Message.obtain(null, CLEAR, origin)); } /** * Allow the specified origin to use the Geolocation API. * @param origin The origin for which Geolocation API use is allowed. */ // This method may be called before the WebKit // thread has intialized the message handler. Messages will be queued until // this time. public void allow(String origin) { // Called on the UI thread. postMessage(Message.obtain(null, ALLOW, origin)); } /** * Clear the Geolocation permission state for all origins. */ public void clearAll() { // Called on the UI thread. postMessage(Message.obtain(null, CLEAR_ALL)); } /** * This class should not be instantiated directly, applications must only use * {@link #getInstance()} to obtain the instance. * Note this constructor was erroneously public and published in SDK levels prior to 16, but * applications using it would receive a non-functional instance of this class (there was no * way to call createHandler() and createUIHandler(), so it would not work). * @hide */ public GeolocationPermissions() {} // Native functions, run on the WebKit thread. private static native Set nativeGetOrigins(); private static native boolean nativeGetAllowed(String origin); private static native void nativeClear(String origin); private static native void nativeAllow(String origin); private static native void nativeClearAll(); }