// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "android_webview/native/aw_contents_io_thread_client_impl.h" #include #include #include "android_webview/native/intercepted_request_data_impl.h" #include "base/android/jni_helper.h" #include "base/android/jni_string.h" #include "base/lazy_instance.h" #include "base/memory/linked_ptr.h" #include "base/memory/scoped_ptr.h" #include "base/synchronization/lock.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/resource_request_info.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents_observer.h" #include "jni/AwContentsIoThreadClient_jni.h" #include "net/url_request/url_request.h" #include "url/gurl.h" using base::android::AttachCurrentThread; using base::android::ConvertUTF8ToJavaString; using base::android::JavaRef; using base::android::ScopedJavaLocalRef; using base::LazyInstance; using content::BrowserThread; using content::RenderViewHost; using content::WebContents; using std::map; using std::pair; namespace android_webview { namespace { struct IoThreadClientData { bool pending_association; JavaObjectWeakGlobalRef io_thread_client; IoThreadClientData(); }; IoThreadClientData::IoThreadClientData() : pending_association(false) {} typedef map, IoThreadClientData> RenderViewHostToIoThreadClientType; static pair GetRenderViewHostIdPair(RenderViewHost* rvh) { return pair(rvh->GetProcess()->GetID(), rvh->GetRoutingID()); } // RvhToIoThreadClientMap ----------------------------------------------------- class RvhToIoThreadClientMap { public: static RvhToIoThreadClientMap* GetInstance(); void Set(pair rvh_id, const IoThreadClientData& client); bool Get(pair rvh_id, IoThreadClientData* client); void Erase(pair rvh_id); private: static LazyInstance g_instance_; base::Lock map_lock_; RenderViewHostToIoThreadClientType rvh_to_io_thread_client_; }; // static LazyInstance RvhToIoThreadClientMap::g_instance_ = LAZY_INSTANCE_INITIALIZER; // static RvhToIoThreadClientMap* RvhToIoThreadClientMap::GetInstance() { return g_instance_.Pointer(); } void RvhToIoThreadClientMap::Set(pair rvh_id, const IoThreadClientData& client) { base::AutoLock lock(map_lock_); rvh_to_io_thread_client_[rvh_id] = client; } bool RvhToIoThreadClientMap::Get( pair rvh_id, IoThreadClientData* client) { base::AutoLock lock(map_lock_); RenderViewHostToIoThreadClientType::iterator iterator = rvh_to_io_thread_client_.find(rvh_id); if (iterator == rvh_to_io_thread_client_.end()) return false; *client = iterator->second; return true; } void RvhToIoThreadClientMap::Erase(pair rvh_id) { base::AutoLock lock(map_lock_); rvh_to_io_thread_client_.erase(rvh_id); } // ClientMapEntryUpdater ------------------------------------------------------ class ClientMapEntryUpdater : public content::WebContentsObserver { public: ClientMapEntryUpdater(JNIEnv* env, WebContents* web_contents, jobject jdelegate); virtual void RenderViewCreated(RenderViewHost* render_view_host) OVERRIDE; virtual void RenderViewDeleted(RenderViewHost* render_view_host) OVERRIDE; virtual void WebContentsDestroyed(WebContents* web_contents) OVERRIDE; private: JavaObjectWeakGlobalRef jdelegate_; }; ClientMapEntryUpdater::ClientMapEntryUpdater(JNIEnv* env, WebContents* web_contents, jobject jdelegate) : content::WebContentsObserver(web_contents), jdelegate_(env, jdelegate) { DCHECK(web_contents); DCHECK(jdelegate); if (web_contents->GetRenderViewHost()) RenderViewCreated(web_contents->GetRenderViewHost()); } void ClientMapEntryUpdater::RenderViewCreated(RenderViewHost* rvh) { IoThreadClientData client_data; client_data.io_thread_client = jdelegate_; client_data.pending_association = false; RvhToIoThreadClientMap::GetInstance()->Set( GetRenderViewHostIdPair(rvh), client_data); } void ClientMapEntryUpdater::RenderViewDeleted(RenderViewHost* rvh) { RvhToIoThreadClientMap::GetInstance()->Erase(GetRenderViewHostIdPair(rvh)); } void ClientMapEntryUpdater::WebContentsDestroyed(WebContents* web_contents) { delete this; } } // namespace // AwContentsIoThreadClientImpl ----------------------------------------------- // static scoped_ptr AwContentsIoThreadClient::FromID(int render_process_id, int render_view_id) { pair rvh_id(render_process_id, render_view_id); IoThreadClientData client_data; if (!RvhToIoThreadClientMap::GetInstance()->Get(rvh_id, &client_data)) return scoped_ptr(); JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef java_delegate = client_data.io_thread_client.get(env); DCHECK(!client_data.pending_association || java_delegate.is_null()); return scoped_ptr(new AwContentsIoThreadClientImpl( client_data.pending_association, java_delegate)); } // static void AwContentsIoThreadClientImpl::RegisterPendingContents( WebContents* web_contents) { IoThreadClientData client_data; client_data.pending_association = true; RvhToIoThreadClientMap::GetInstance()->Set( GetRenderViewHostIdPair(web_contents->GetRenderViewHost()), client_data); } // static void AwContentsIoThreadClientImpl::Associate( WebContents* web_contents, const JavaRef& jclient) { JNIEnv* env = AttachCurrentThread(); // The ClientMapEntryUpdater lifespan is tied to the WebContents. new ClientMapEntryUpdater(env, web_contents, jclient.obj()); } AwContentsIoThreadClientImpl::AwContentsIoThreadClientImpl( bool pending_association, const JavaRef& obj) : pending_association_(pending_association), java_object_(obj) { } AwContentsIoThreadClientImpl::~AwContentsIoThreadClientImpl() { // explict, out-of-line destructor. } bool AwContentsIoThreadClientImpl::PendingAssociation() const { return pending_association_; } AwContentsIoThreadClient::CacheMode AwContentsIoThreadClientImpl::GetCacheMode() const { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (java_object_.is_null()) return AwContentsIoThreadClient::LOAD_DEFAULT; JNIEnv* env = AttachCurrentThread(); return static_cast( Java_AwContentsIoThreadClient_getCacheMode( env, java_object_.obj())); } scoped_ptr AwContentsIoThreadClientImpl::ShouldInterceptRequest( const GURL& location, const net::URLRequest* request) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (java_object_.is_null()) return scoped_ptr(); const content::ResourceRequestInfo* info = content::ResourceRequestInfo::ForRequest(request); bool is_main_frame = info && info->GetResourceType() == ResourceType::MAIN_FRAME; JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef jstring_url = ConvertUTF8ToJavaString(env, location.spec()); ScopedJavaLocalRef ret = Java_AwContentsIoThreadClient_shouldInterceptRequest( env, java_object_.obj(), jstring_url.obj(), is_main_frame); if (ret.is_null()) return scoped_ptr(); return scoped_ptr( new InterceptedRequestDataImpl(ret)); } bool AwContentsIoThreadClientImpl::ShouldBlockContentUrls() const { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (java_object_.is_null()) return false; JNIEnv* env = AttachCurrentThread(); return Java_AwContentsIoThreadClient_shouldBlockContentUrls( env, java_object_.obj()); } bool AwContentsIoThreadClientImpl::ShouldBlockFileUrls() const { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (java_object_.is_null()) return false; JNIEnv* env = AttachCurrentThread(); return Java_AwContentsIoThreadClient_shouldBlockFileUrls( env, java_object_.obj()); } bool AwContentsIoThreadClientImpl::ShouldBlockNetworkLoads() const { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (java_object_.is_null()) return false; JNIEnv* env = AttachCurrentThread(); return Java_AwContentsIoThreadClient_shouldBlockNetworkLoads( env, java_object_.obj()); } void AwContentsIoThreadClientImpl::NewDownload( const GURL& url, const std::string& user_agent, const std::string& content_disposition, const std::string& mime_type, int64 content_length) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (java_object_.is_null()) return; JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef jstring_url = ConvertUTF8ToJavaString(env, url.spec()); ScopedJavaLocalRef jstring_user_agent = ConvertUTF8ToJavaString(env, user_agent); ScopedJavaLocalRef jstring_content_disposition = ConvertUTF8ToJavaString(env, content_disposition); ScopedJavaLocalRef jstring_mime_type = ConvertUTF8ToJavaString(env, mime_type); Java_AwContentsIoThreadClient_onDownloadStart( env, java_object_.obj(), jstring_url.obj(), jstring_user_agent.obj(), jstring_content_disposition.obj(), jstring_mime_type.obj(), content_length); } void AwContentsIoThreadClientImpl::NewLoginRequest(const std::string& realm, const std::string& account, const std::string& args) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (java_object_.is_null()) return; JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef jrealm = ConvertUTF8ToJavaString(env, realm); ScopedJavaLocalRef jargs = ConvertUTF8ToJavaString(env, args); ScopedJavaLocalRef jaccount; if (!account.empty()) jaccount = ConvertUTF8ToJavaString(env, account); Java_AwContentsIoThreadClient_newLoginRequest( env, java_object_.obj(), jrealm.obj(), jaccount.obj(), jargs.obj()); } bool RegisterAwContentsIoThreadClientImpl(JNIEnv* env) { return RegisterNativesImpl(env); } } // namespace android_webview