diff options
author | kristianm@chromium.org <kristianm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-02-14 14:58:58 +0000 |
---|---|---|
committer | kristianm@chromium.org <kristianm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-02-14 14:58:58 +0000 |
commit | 3a879683c4422affac21bb8cb4cfb7b3004730f0 (patch) | |
tree | b77bfbdff457644164de7df117f91635b8e09b82 /android_webview | |
parent | c37a3c686abacfe30528c3cdf65d772220392f56 (diff) | |
download | chromium_src-3a879683c4422affac21bb8cb4cfb7b3004730f0.zip chromium_src-3a879683c4422affac21bb8cb4cfb7b3004730f0.tar.gz chromium_src-3a879683c4422affac21bb8cb4cfb7b3004730f0.tar.bz2 |
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
Diffstat (limited to 'android_webview')
8 files changed, 260 insertions, 85 deletions
diff --git a/android_webview/browser/aw_content_browser_client.cc b/android_webview/browser/aw_content_browser_client.cc index 4662e74..bc4918b 100644 --- a/android_webview/browser/aw_content_browser_client.cc +++ b/android_webview/browser/aw_content_browser_client.cc @@ -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() { diff --git a/android_webview/java/src/org/chromium/android_webview/AwContents.java b/android_webview/java/src/org/chromium/android_webview/AwContents.java index e0ada40..51c7a14 100644 --- a/android_webview/java/src/org/chromium/android_webview/AwContents.java +++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java @@ -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); } diff --git a/android_webview/java/src/org/chromium/android_webview/AwGeolocationPermissions.java b/android_webview/java/src/org/chromium/android_webview/AwGeolocationPermissions.java index 133f597..229bf20 100644 --- a/android_webview/java/src/org/chromium/android_webview/AwGeolocationPermissions.java +++ b/android_webview/java/src/org/chromium/android_webview/AwGeolocationPermissions.java @@ -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()) { diff --git a/android_webview/java/src/org/chromium/android_webview/AwSettings.java b/android_webview/java/src/org/chromium/android_webview/AwSettings.java index cb91126..65b0c34 100644 --- a/android_webview/java/src/org/chromium/android_webview/AwSettings.java +++ b/android_webview/java/src/org/chromium/android_webview/AwSettings.java @@ -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; + } + } } diff --git a/android_webview/native/aw_contents.cc b/android_webview/native/aw_contents.cc index 1fd2ec1..60f1f69 100644 --- a/android_webview/native/aw_contents.cc +++ b/android_webview/native/aw_contents.cc @@ -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) { diff --git a/android_webview/native/aw_contents.h b/android_webview/native/aw_contents.h index 31a921d..7313027 100644 --- a/android_webview/native/aw_contents.h +++ b/android_webview/native/aw_contents.h @@ -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_; diff --git a/android_webview/native/aw_geolocation_permission_context.cc b/android_webview/native/aw_geolocation_permission_context.cc index 959c464..3b67862 100644 --- a/android_webview/native/aw_geolocation_permission_context.cc +++ b/android_webview/native/aw_geolocation_permission_context.cc @@ -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 diff --git a/android_webview/native/aw_geolocation_permission_context.h b/android_webview/native/aw_geolocation_permission_context.h index 3235c4d..9e1cf53 100644 --- a/android_webview/native/aw_geolocation_permission_context.h +++ b/android_webview/native/aw_geolocation_permission_context.h @@ -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(); |