/* * 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 android.util.Log; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; /** * Implements the Java side of GeolocationPermissions. Simply marshalls calls * from the UI thread to the WebKit thread. */ public final class GeolocationPermissions { /** * Callback interface used by the browser to report a Geolocation permission * state set by the user in response to a permissions prompt. */ public interface Callback { public void invoke(String origin, boolean allow, boolean remember); }; // Log tag private static final String TAG = "geolocationPermissions"; // Global instance private static GeolocationPermissions sInstance; private Handler mHandler; private Handler mUIHandler; // Members used to transfer the origins and permissions between threads. private Set<String> mOrigins; private boolean mAllowed; private Set<String> mOriginsToClear; private Set<String> mOriginsToAllow; // 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"; /** * Gets the singleton instance of the class. */ 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<Set> callback = (ValueCallback<Set>) values.get(CALLBACK); callback.onReceiveValue(origins); } break; case RETURN_ALLOWED: { Map values = (Map) msg.obj; Boolean allowed = (Boolean) values.get(ALLOWED); ValueCallback<Boolean> callback = (ValueCallback<Boolean>) values.get(CALLBACK); callback.onReceiveValue(allowed); } break; } } }; } } /** * Creates the message handler. Must be called on the WebKit thread. * @hide */ public 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: { getOriginsImpl(); ValueCallback callback = (ValueCallback) msg.obj; Set origins = new HashSet(mOrigins); Map values = new HashMap<String, Object>(); 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); getAllowedImpl(origin); Map retValues = new HashMap<String, Object>(); retValues.put(CALLBACK, callback); retValues.put(ALLOWED, new Boolean(mAllowed)); 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; } } }; if (mOriginsToClear != null) { for (String origin : mOriginsToClear) { nativeClear(origin); } } if (mOriginsToAllow != null) { for (String origin : mOriginsToAllow) { nativeAllow(origin); } } } } /** * Utility function to send a message to our handler. */ private void postMessage(Message msg) { assert(mHandler != null); 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); } } /** * Gets the set of 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 for the * purposes of displaying UI. */ public void getOrigins(ValueCallback<Set> callback) { if (callback != null) { if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) { getOriginsImpl(); Set origins = new HashSet(mOrigins); callback.onReceiveValue(origins); } else { postMessage(Message.obtain(null, GET_ORIGINS, callback)); } } } /** * Helper method to get the set of origins. */ private void getOriginsImpl() { // Called on the WebKit thread. mOrigins = nativeGetOrigins(); } /** * Gets the permission state for the specified origin. */ public void getAllowed(String origin, ValueCallback<Boolean> callback) { if (callback == null) { return; } if (origin == null) { callback.onReceiveValue(null); return; } if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) { getAllowedImpl(origin); callback.onReceiveValue(new Boolean(mAllowed)); } else { Map values = new HashMap<String, Object>(); values.put(ORIGIN, origin); values.put(CALLBACK, callback); postMessage(Message.obtain(null, GET_ALLOWED, values)); } } /** * Helper method to get the permission state. */ private void getAllowedImpl(String origin) { // Called on the WebKit thread. mAllowed = nativeGetAllowed(origin); } /** * Clears the permission state for the specified origin. 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. if (mHandler == null) { if (mOriginsToClear == null) { mOriginsToClear = new HashSet<String>(); } mOriginsToClear.add(origin); if (mOriginsToAllow != null) { mOriginsToAllow.remove(origin); } } else { postMessage(Message.obtain(null, CLEAR, origin)); } } /** * Allows the specified origin. 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. if (mHandler == null) { if (mOriginsToAllow == null) { mOriginsToAllow = new HashSet<String>(); } mOriginsToAllow.add(origin); if (mOriginsToClear != null) { mOriginsToClear.remove(origin); } } else { postMessage(Message.obtain(null, ALLOW, origin)); } } /** * Clears the permission state for all origins. */ public void clearAll() { // Called on the UI thread. postMessage(Message.obtain(null, CLEAR_ALL)); } // 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(); }