blob: d7b6adb218e34c62d77f5bde6c8f57ddf0a7ebcb [file] [log] [blame]
/*
* 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;
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 marshalls 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 final 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);
};
// Log tag
private static final String TAG = "geolocationPermissions";
// Global instance
private static GeolocationPermissions sInstance;
private Handler mHandler;
private Handler mUIHandler;
// A queue to store messages until the handler is ready.
private Vector<Message> 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<String> origins = (Set<String>) values.get(ORIGINS);
ValueCallback<Set<String> > callback = (ValueCallback<Set<String> >) 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 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<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);
boolean allowed = nativeGetAllowed(origin);
Map retValues = new HashMap<String, Object>();
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<Message>();
}
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<Set<String> > 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<Boolean> 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(new Boolean(allowed));
} else {
Map values = new HashMap<String, Object>();
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));
}
// 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();
}