Implementing geolocation for the Android Webview



Enabling geolocation callbacks


BUG=


Review URL: https://chromiumcodereview.appspot.com/12211047

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@182465 0039d316-1c4b-4281-b951-d872f2087c98
parent c37a3c68
......@@ -24,20 +24,25 @@
namespace {
class DummyAccessTokenStore : public content::AccessTokenStore {
class AwAccessTokenStore : public content::AccessTokenStore {
public:
DummyAccessTokenStore() { }
AwAccessTokenStore() { }
// content::AccessTokenStore implementation
virtual void LoadAccessTokens(
const LoadAccessTokensCallbackType& request) OVERRIDE { }
const LoadAccessTokensCallbackType& request) OVERRIDE {
AccessTokenStore::AccessTokenSet access_token_set;
// AccessTokenSet and net::URLRequestContextGetter not used on Android,
// but Run needs to be called to finish the geolocation setup.
request.Run(access_token_set, NULL);
}
virtual void SaveAccessToken(const GURL& server_url,
const string16& access_token) OVERRIDE { }
private:
virtual ~DummyAccessTokenStore() { }
virtual void SaveAccessToken(
const GURL& server_url, const string16& access_token) OVERRIDE { }
virtual ~AwAccessTokenStore() { }
DISALLOW_COPY_AND_ASSIGN(DummyAccessTokenStore);
DISALLOW_COPY_AND_ASSIGN(AwAccessTokenStore);
};
}
......@@ -326,8 +331,7 @@ net::NetLog* AwContentBrowserClient::GetNetLog() {
}
content::AccessTokenStore* AwContentBrowserClient::CreateAccessTokenStore() {
// TODO(boliu): Implement as part of geolocation code.
return new DummyAccessTokenStore();
return new AwAccessTokenStore();
}
bool AwContentBrowserClient::IsFastShutdownPossible() {
......
......@@ -862,33 +862,52 @@ public class AwContents {
mContentsClient.onReceivedHttpAuthRequest(handler, host, realm);
}
private static class ChromiumGeolocationCallback implements GeolocationPermissions.Callback {
final int mRenderProcessId;
final int mRenderViewId;
final int mBridgeId;
final String mRequestingFrame;
private ChromiumGeolocationCallback(int renderProcessId, int renderViewId, int bridgeId,
String requestingFrame) {
mRenderProcessId = renderProcessId;
mRenderViewId = renderViewId;
mBridgeId = bridgeId;
mRequestingFrame = requestingFrame;
private class AwGeolocationCallback implements GeolocationPermissions.Callback {
private final AwGeolocationPermissions mGeolocationPermissions;
private AwGeolocationCallback(AwGeolocationPermissions geolocationPermissions) {
mGeolocationPermissions = geolocationPermissions;
}
@Override
public void invoke(String origin, boolean allow, boolean retain) {
// TODO(kristianm): Implement callback handling
public void invoke(final String origin, final boolean allow, final boolean retain) {
ThreadUtils.runOnUiThread(new Runnable() {
@Override
public void run() {
if (retain) {
if (allow) {
mGeolocationPermissions.allow(origin);
} else {
mGeolocationPermissions.deny(origin);
}
}
nativeInvokeGeolocationCallback(mNativeAwContents, allow, origin);
}
});
}
}
@CalledByNative
private void onGeolocationPermissionsShowPrompt(int renderProcessId, int renderViewId,
int bridgeId, String requestingFrame) {
// TODO(kristianm): Check with GeolocationPermissions if origin already has a policy set
mContentsClient.onGeolocationPermissionsShowPrompt(GURLUtils.getOrigin(requestingFrame),
new ChromiumGeolocationCallback(renderProcessId, renderViewId, bridgeId,
requestingFrame));
private void onGeolocationPermissionsShowPrompt(String origin) {
AwGeolocationPermissions permissions = AwGeolocationPermissions.getInstance();
// Reject if geoloaction is disabled, or the origin has a retained deny
if (!mSettings.getGeolocationEnabled()) {
nativeInvokeGeolocationCallback(mNativeAwContents, false, origin);
return;
}
// Allow if the origin has a retained allow
if (permissions.hasOrigin(origin)) {
nativeInvokeGeolocationCallback(mNativeAwContents, permissions.isOriginAllowed(origin),
origin);
return;
}
mContentsClient.onGeolocationPermissionsShowPrompt(
origin, new AwGeolocationCallback(permissions));
}
@CalledByNative
private void onGeolocationPermissionsHidePrompt() {
mContentsClient.onGeolocationPermissionsHidePrompt();
}
@CalledByNative
......@@ -1084,4 +1103,7 @@ public class AwContents {
private native Picture nativeCapturePicture(int nativeAwContents);
private native void nativeEnableOnNewPicture(int nativeAwContents, boolean enabled,
boolean invalidationOnly);
private native void nativeInvokeGeolocationCallback(
int nativeAwContents, boolean value, String requestingFrame);
}
......@@ -24,10 +24,51 @@ public final class AwGeolocationPermissions {
AwGeolocationPermissions.class.getCanonicalName() + "%";
private final SharedPreferences mSharedPreferences;
private static AwGeolocationPermissions sInstance;
// TODO(kristianm): Rewrite when AwBrowserContext has landed in
// CL: https://codereview.chromium.org/12208099/
public AwGeolocationPermissions(SharedPreferences sharedPreferences) {
mSharedPreferences = sharedPreferences;
setInstance(this);
}
private static void setInstance(AwGeolocationPermissions instance) {
synchronized (AwGeolocationPermissions.class) {
sInstance = instance;
}
}
/**
* Get the static instance after it has been created
*/
public static AwGeolocationPermissions getInstance() {
synchronized (AwGeolocationPermissions.class) {
if (sInstance == null) {
throw new IllegalStateException("This should only be called after createInstance.");
}
}
return sInstance;
}
/**
* Create the static instance of this class
*/
public static AwGeolocationPermissions createInstance(
SharedPreferences sharedPreferences) {
synchronized (AwGeolocationPermissions.class) {
if (sInstance != null) {
throw new IllegalStateException("This should only be called once.");
}
// sInstance set in the constructor
new AwGeolocationPermissions(sharedPreferences);
return sInstance;
}
}
/**
* Set one origin to be allowed.
*/
public void allow(String origin) {
String key = getOriginKey(origin);
if (key != null) {
......@@ -35,6 +76,9 @@ public final class AwGeolocationPermissions {
}
}
/**
* Set one origin to be denied.
*/
public void deny(String origin) {
String key = getOriginKey(origin);
if (key != null) {
......@@ -42,6 +86,9 @@ public final class AwGeolocationPermissions {
}
}
/**
* Clear the stored permission for a particular origin.
*/
public void clear(String origin) {
String key = getOriginKey(origin);
if (key != null) {
......@@ -49,6 +96,9 @@ public final class AwGeolocationPermissions {
}
}
/**
* Clear stored permissions for all origins.
*/
public void clearAll() {
SharedPreferences.Editor editor = null;
for (String name : mSharedPreferences.getAll().keySet()) {
......@@ -64,17 +114,25 @@ public final class AwGeolocationPermissions {
}
}
/**
* Synchronous method to get if an origin is set to be allowed.
*/
public boolean isOriginAllowed(String origin) {
return mSharedPreferences.getBoolean(getOriginKey(origin), false);
}
/**
* Returns true if the origin is either set to allowed or denied.
*/
public boolean hasOrigin(String origin) {
return mSharedPreferences.contains(getOriginKey(origin));
}
/**
* Asynchronous method to get if an origin set to be allowed.
*/
public void getAllowed(String origin, final ValueCallback<Boolean> callback) {
boolean allowed = false;
try {
String key = getOriginKey(origin);
if (key != null) {
allowed = mSharedPreferences.getBoolean(key, false);
}
} catch (ClassCastException e) {
// Want to return false in this case, do nothing here
}
final boolean finalAllowed = allowed;
final boolean finalAllowed = isOriginAllowed(origin);
ThreadUtils.postOnUiThread(new Runnable() {
@Override
public void run() {
......@@ -83,6 +141,9 @@ public final class AwGeolocationPermissions {
});
}
/**
* Async method to get the domains currently allowed or denied.
*/
public void getOrigins(final ValueCallback<Set<String>> callback) {
final Set<String> origins = new HashSet<String>();
for (String name : mSharedPreferences.getAll().keySet()) {
......@@ -98,6 +159,9 @@ public final class AwGeolocationPermissions {
});
}
/**
* Get the domain of an URL using the GURL library.
*/
private String getOriginKey(String url) {
String origin = GURLUtils.getOrigin(url);
if (origin.isEmpty()) {
......
......@@ -26,6 +26,7 @@ public class AwSettings {
private boolean mAllowFileUrlAccess = true;
private int mCacheMode = WebSettings.LOAD_DEFAULT;
private boolean mShouldFocusFirstNode = true;
private boolean mGeolocationEnabled = true;
public AwSettings(Context context) {
mContext = context;
......@@ -137,4 +138,24 @@ public class AwSettings {
return mShouldFocusFirstNode;
}
}
/**
* See {@link android.webkit.WebSettings#setGeolocationEnabled}.
*/
public void setGeolocationEnabled(boolean flag) {
synchronized (mAwSettingsLock) {
if (mGeolocationEnabled != flag) {
mGeolocationEnabled = flag;
}
}
}
/**
* @return Returns if geolocation is currently enabled.
*/
boolean getGeolocationEnabled() {
synchronized (mAwSettingsLock) {
return mGeolocationEnabled;
}
}
}
......@@ -727,20 +727,88 @@ bool RegisterAwContents(JNIEnv* env) {
return RegisterNativesImpl(env) >= 0;
}
void AwContents::OnGeolocationShowPrompt(int render_process_id,
int render_view_id,
int bridge_id,
const GURL& requesting_frame) {
namespace {
void ShowGeolocationPromptHelperTask(const JavaObjectWeakGlobalRef& java_ref,
const GURL& origin) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> j_ref = java_ref.get(env);
if (j_ref.obj()) {
ScopedJavaLocalRef<jstring> j_origin(
ConvertUTF8ToJavaString(env, origin.spec()));
Java_AwContents_onGeolocationPermissionsShowPrompt(env,
j_ref.obj(),
j_origin.obj());
}
}
void ShowGeolocationPromptHelper(const JavaObjectWeakGlobalRef& java_ref,
const GURL& origin) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jstring> j_requesting_frame(
ConvertUTF8ToJavaString(env, requesting_frame.spec()));
Java_AwContents_onGeolocationPermissionsShowPrompt(env,
java_ref_.get(env).obj(), render_process_id, render_view_id, bridge_id,
j_requesting_frame.obj());
if (java_ref.get(env).obj()) {
content::BrowserThread::PostTask(
content::BrowserThread::UI,
FROM_HERE,
base::Bind(&ShowGeolocationPromptHelperTask,
java_ref,
origin));
}
}
} // anonymous namespace
void AwContents::ShowGeolocationPrompt(const GURL& requesting_frame,
base::Callback<void(bool)> callback) {
GURL origin = requesting_frame.GetOrigin();
bool show_prompt = pending_geolocation_prompts_.empty();
pending_geolocation_prompts_.push_back(OriginCallback(origin, callback));
if (show_prompt) {
ShowGeolocationPromptHelper(java_ref_, origin);
}
}
void AwContents::OnGeolocationHidePrompt() {
// TODO(kristianm): Implement this
// Invoked from Java
void AwContents::InvokeGeolocationCallback(JNIEnv* env,
jobject obj,
jboolean value,
jstring origin) {
GURL callback_origin(base::android::ConvertJavaStringToUTF16(env, origin));
if (callback_origin.GetOrigin() ==
pending_geolocation_prompts_.front().first) {
pending_geolocation_prompts_.front().second.Run(value);
pending_geolocation_prompts_.pop_front();
if (!pending_geolocation_prompts_.empty()) {
ShowGeolocationPromptHelper(java_ref_,
pending_geolocation_prompts_.front().first);
}
}
}
void AwContents::HideGeolocationPrompt(const GURL& origin) {
bool removed_current_outstanding_callback = false;
std::list<OriginCallback>::iterator it = pending_geolocation_prompts_.begin();
while (it != pending_geolocation_prompts_.end()) {
if ((*it).first == origin.GetOrigin()) {
if (it == pending_geolocation_prompts_.begin()) {
removed_current_outstanding_callback = true;
}
it = pending_geolocation_prompts_.erase(it);
} else {
++it;
}
}
if (removed_current_outstanding_callback) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> j_ref = java_ref_.get(env);
if (j_ref.obj()) {
Java_AwContents_onGeolocationPermissionsHidePrompt(env, j_ref.obj());
}
if (!pending_geolocation_prompts_.empty()) {
ShowGeolocationPromptHelper(java_ref_,
pending_geolocation_prompts_.front().first);
}
}
}
jint AwContents::FindAllSync(JNIEnv* env, jobject obj, jstring search_string) {
......
......@@ -6,7 +6,9 @@
#define ANDROID_WEBVIEW_NATIVE_AW_CONTENTS_H_
#include <jni.h>
#include <list>
#include <string>
#include <utility>
#include "android_webview/browser/find_helper.h"
#include "android_webview/browser/icon_helper.h"
......@@ -14,6 +16,7 @@
#include "android_webview/public/browser/draw_gl.h"
#include "base/android/scoped_java_ref.h"
#include "base/android/jni_helper.h"
#include "base/callback_forward.h"
#include "base/memory/scoped_ptr.h"
#include "content/public/browser/android/compositor.h"
#include "content/public/browser/javascript_dialog_manager.h"
......@@ -131,11 +134,12 @@ class AwContents : public FindHelper::Listener,
jboolean invalidation_only);
// Geolocation API support
void OnGeolocationShowPrompt(int render_process_id,
int render_view_id,
int bridge_id,
const GURL& requesting_frame);
void OnGeolocationHidePrompt();
void ShowGeolocationPrompt(const GURL& origin, base::Callback<void(bool)>);
void HideGeolocationPrompt(const GURL& origin);
void InvokeGeolocationCallback(JNIEnv* env,
jobject obj,
jboolean value,
jstring origin);
// Find-in-page API and related methods.
jint FindAllSync(JNIEnv* env, jobject obj, jstring search_string);
......@@ -187,6 +191,13 @@ class AwContents : public FindHelper::Listener,
scoped_ptr<IconHelper> icon_helper_;
scoped_ptr<content::WebContents> pending_contents_;
// GURL is supplied by the content layer as requesting frame.
// Callback is supplied by the content layer, and is invoked with the result
// from the permission prompt.
typedef std::pair<const GURL, base::Callback<void(bool)> > OriginCallback;
// The first element in the list is always the currently pending request.
std::list<OriginCallback> pending_geolocation_prompts_;
// Compositor-specific state.
scoped_ptr<content::Compositor> compositor_;
scoped_refptr<cc::Layer> scissor_clip_layer_;
......
......@@ -10,7 +10,6 @@
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "googleurl/src/gurl.h"
namespace android_webview {
......@@ -25,16 +24,14 @@ AwGeolocationPermissionContext::RequestGeolocationPermissionOnUIThread(
const GURL& requesting_frame,
base::Callback<void(bool)> callback) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
const content::RenderViewHost* host =
content::RenderViewHost::FromID(render_process_id, render_view_id);
content::WebContents* web_contents =
content::WebContents::FromRenderViewHost(host);
AwContents* aw_contents = AwContents::FromWebContents(web_contents);
aw_contents->OnGeolocationShowPrompt(
render_process_id,
render_view_id,
bridge_id,
requesting_frame);
AwContents* aw_contents =
AwContents::FromID(render_process_id, render_view_id);
if (!aw_contents) {
callback.Run(false);
return;
}
aw_contents->ShowGeolocationPrompt(requesting_frame, callback);
}
void
......@@ -70,7 +67,12 @@ AwGeolocationPermissionContext::CancelGeolocationPermissionRequestOnUIThread(
int bridge_id,
const GURL& requesting_frame) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
// TODO(kristianm): Implement this
AwContents* aw_contents =
AwContents::FromID(render_process_id, render_view_id);
if (aw_contents) {
aw_contents->HideGeolocationPrompt(requesting_frame);
}
}
void
......@@ -91,13 +93,4 @@ AwGeolocationPermissionContext::CancelGeolocationPermissionRequest(
requesting_frame));
}
void InvokeCallback(
int render_process_id,
int render_view_id,
int bridge_id,
const GURL& requesting_frame,
bool value) {
// TODO(kristianm): Implement this
}
} // namespace android_webview
......@@ -5,7 +5,6 @@
#ifndef ANDROID_WEBVIEW_NATIVE_AW_GEOLOCATION_PERMISSION_CONTEXT_H_
#define ANDROID_WEBVIEW_NATIVE_AW_GEOLOCATION_PERMISSION_CONTEXT_H_
#include "base/callback_forward.h"
#include "content/public/browser/geolocation_permission_context.h"
class GURL;
......@@ -30,13 +29,6 @@ class AwGeolocationPermissionContext :
int bridge_id,
const GURL& requesting_frame) OVERRIDE;
void InvokeCallback(
int render_process_id,
int render_view_id,
int bridge_id,
const GURL& requesting_frame,
bool value);
protected:
virtual ~AwGeolocationPermissionContext();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment