1 // Copyright 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "content/browser/media/android/media_resource_getter_impl.h" 6 7 #include "base/android/jni_android.h" 8 #include "base/android/jni_string.h" 9 #include "base/bind.h" 10 #include "base/path_service.h" 11 #include "base/threading/sequenced_worker_pool.h" 12 #include "content/browser/child_process_security_policy_impl.h" 13 #include "content/browser/fileapi/browser_file_system_helper.h" 14 #include "content/browser/fileapi/chrome_blob_storage_context.h" 15 #include "content/public/browser/browser_context.h" 16 #include "content/public/browser/browser_thread.h" 17 #include "content/public/browser/content_browser_client.h" 18 #include "content/public/common/content_client.h" 19 #include "content/public/common/url_constants.h" 20 #include "jni/MediaResourceGetter_jni.h" 21 #include "media/base/android/media_url_interceptor.h" 22 #include "net/base/auth.h" 23 #include "net/cookies/cookie_monster.h" 24 #include "net/cookies/cookie_store.h" 25 #include "net/url_request/url_request_context.h" 26 #include "net/url_request/url_request_context_getter.h" 27 #include "url/gurl.h" 28 #include "webkit/browser/blob/blob_data_handle.h" 29 #include "webkit/browser/blob/blob_storage_context.h" 30 31 using base::android::ConvertUTF8ToJavaString; 32 using base::android::ScopedJavaLocalRef; 33 34 namespace content { 35 36 static void ReturnResultOnUIThread( 37 const base::Callback<void(const std::string&)>& callback, 38 const std::string& result) { 39 BrowserThread::PostTask( 40 BrowserThread::UI, FROM_HERE, base::Bind(callback, result)); 41 } 42 43 static void RequestPlatformPathFromBlobURL( 44 const GURL& url, 45 BrowserContext* browser_context, 46 const base::Callback<void(const std::string&)>& callback) { 47 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 48 ChromeBlobStorageContext* context = 49 ChromeBlobStorageContext::GetFor(browser_context); 50 scoped_ptr<webkit_blob::BlobDataHandle> handle = 51 context->context()->GetBlobDataFromPublicURL(url); 52 const std::vector<webkit_blob::BlobData::Item> items = 53 handle->data()->items(); 54 55 // TODO(qinmin): handle the case when the blob data is not a single file. 56 DLOG_IF(WARNING, items.size() != 1u) 57 << "More than one blob data are present: " << items.size(); 58 ReturnResultOnUIThread(callback, items[0].path().value()); 59 } 60 61 static void RequestPlaformPathFromFileSystemURL( 62 const GURL& url, 63 int render_process_id, 64 scoped_refptr<fileapi::FileSystemContext> file_system_context, 65 const base::Callback<void(const std::string&)>& callback) { 66 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 67 base::FilePath platform_path; 68 SyncGetPlatformPath(file_system_context.get(), 69 render_process_id, 70 url, 71 &platform_path); 72 base::FilePath data_storage_path; 73 PathService::Get(base::DIR_ANDROID_APP_DATA, &data_storage_path); 74 if (data_storage_path.IsParent(platform_path)) 75 ReturnResultOnUIThread(callback, platform_path.value()); 76 else 77 ReturnResultOnUIThread(callback, std::string()); 78 } 79 80 // Posts a task to the UI thread to run the callback function. 81 static void PostMediaMetadataCallbackTask( 82 const media::MediaResourceGetter::ExtractMediaMetadataCB& callback, 83 JNIEnv* env, ScopedJavaLocalRef<jobject>& j_metadata) { 84 BrowserThread::PostTask( 85 BrowserThread::UI, FROM_HERE, 86 base::Bind(callback, base::TimeDelta::FromMilliseconds( 87 Java_MediaMetadata_getDurationInMilliseconds( 88 env, j_metadata.obj())), 89 Java_MediaMetadata_getWidth(env, j_metadata.obj()), 90 Java_MediaMetadata_getHeight(env, j_metadata.obj()), 91 Java_MediaMetadata_isSuccess(env, j_metadata.obj()))); 92 } 93 94 // Gets the metadata from a media URL. When finished, a task is posted to the UI 95 // thread to run the callback function. 96 static void GetMediaMetadata( 97 const std::string& url, const std::string& cookies, 98 const std::string& user_agent, 99 const media::MediaResourceGetter::ExtractMediaMetadataCB& callback) { 100 JNIEnv* env = base::android::AttachCurrentThread(); 101 102 ScopedJavaLocalRef<jstring> j_url_string = ConvertUTF8ToJavaString(env, url); 103 ScopedJavaLocalRef<jstring> j_cookies = ConvertUTF8ToJavaString(env, cookies); 104 jobject j_context = base::android::GetApplicationContext(); 105 ScopedJavaLocalRef<jstring> j_user_agent = ConvertUTF8ToJavaString( 106 env, user_agent); 107 ScopedJavaLocalRef<jobject> j_metadata = 108 Java_MediaResourceGetter_extractMediaMetadata(env, 109 j_context, 110 j_url_string.obj(), 111 j_cookies.obj(), 112 j_user_agent.obj()); 113 114 PostMediaMetadataCallbackTask(callback, env, j_metadata); 115 } 116 117 // Gets the metadata from a file descriptor. When finished, a task is posted to 118 // the UI thread to run the callback function. 119 static void GetMediaMetadataFromFd( 120 const int fd, const int64 offset, const int64 size, 121 const media::MediaResourceGetter::ExtractMediaMetadataCB& callback) { 122 JNIEnv* env = base::android::AttachCurrentThread(); 123 124 ScopedJavaLocalRef<jobject> j_metadata = 125 Java_MediaResourceGetter_extractMediaMetadataFromFd( 126 env, fd, offset, size); 127 128 PostMediaMetadataCallbackTask(callback, env, j_metadata); 129 } 130 131 // The task object that retrieves cookie on the IO thread. 132 // TODO(qinmin): refactor this class to make the code reusable by others as 133 // there are lots of duplicated functionalities elsewhere. 134 class CookieGetterTask 135 : public base::RefCountedThreadSafe<CookieGetterTask> { 136 public: 137 CookieGetterTask(BrowserContext* browser_context, 138 int render_process_id, int render_frame_id); 139 140 // Called by CookieGetterImpl to start getting cookies for a URL. 141 void RequestCookies( 142 const GURL& url, const GURL& first_party_for_cookies, 143 const media::MediaResourceGetter::GetCookieCB& callback); 144 145 private: 146 friend class base::RefCountedThreadSafe<CookieGetterTask>; 147 virtual ~CookieGetterTask(); 148 149 void CheckPolicyForCookies( 150 const GURL& url, const GURL& first_party_for_cookies, 151 const media::MediaResourceGetter::GetCookieCB& callback, 152 const net::CookieList& cookie_list); 153 154 // Context getter used to get the CookieStore. 155 net::URLRequestContextGetter* context_getter_; 156 157 // Resource context for checking cookie policies. 158 ResourceContext* resource_context_; 159 160 // Render process id, used to check whether the process can access cookies. 161 int render_process_id_; 162 163 // Render frame id, used to check tab specific cookie policy. 164 int render_frame_id_; 165 166 DISALLOW_COPY_AND_ASSIGN(CookieGetterTask); 167 }; 168 169 CookieGetterTask::CookieGetterTask( 170 BrowserContext* browser_context, int render_process_id, int render_frame_id) 171 : context_getter_(browser_context->GetRequestContext()), 172 resource_context_(browser_context->GetResourceContext()), 173 render_process_id_(render_process_id), 174 render_frame_id_(render_frame_id) { 175 } 176 177 CookieGetterTask::~CookieGetterTask() {} 178 179 void CookieGetterTask::RequestCookies( 180 const GURL& url, const GURL& first_party_for_cookies, 181 const media::MediaResourceGetter::GetCookieCB& callback) { 182 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 183 ChildProcessSecurityPolicyImpl* policy = 184 ChildProcessSecurityPolicyImpl::GetInstance(); 185 if (!policy->CanAccessCookiesForOrigin(render_process_id_, url)) { 186 callback.Run(std::string()); 187 return; 188 } 189 190 net::CookieStore* cookie_store = 191 context_getter_->GetURLRequestContext()->cookie_store(); 192 if (!cookie_store) { 193 callback.Run(std::string()); 194 return; 195 } 196 197 net::CookieMonster* cookie_monster = cookie_store->GetCookieMonster(); 198 if (cookie_monster) { 199 cookie_monster->GetAllCookiesForURLAsync(url, base::Bind( 200 &CookieGetterTask::CheckPolicyForCookies, this, 201 url, first_party_for_cookies, callback)); 202 } else { 203 callback.Run(std::string()); 204 } 205 } 206 207 void CookieGetterTask::CheckPolicyForCookies( 208 const GURL& url, const GURL& first_party_for_cookies, 209 const media::MediaResourceGetter::GetCookieCB& callback, 210 const net::CookieList& cookie_list) { 211 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 212 if (GetContentClient()->browser()->AllowGetCookie( 213 url, first_party_for_cookies, cookie_list, 214 resource_context_, render_process_id_, render_frame_id_)) { 215 net::CookieStore* cookie_store = 216 context_getter_->GetURLRequestContext()->cookie_store(); 217 cookie_store->GetCookiesWithOptionsAsync( 218 url, net::CookieOptions(), callback); 219 } else { 220 callback.Run(std::string()); 221 } 222 } 223 224 MediaResourceGetterImpl::MediaResourceGetterImpl( 225 BrowserContext* browser_context, 226 fileapi::FileSystemContext* file_system_context, 227 int render_process_id, 228 int render_frame_id) 229 : browser_context_(browser_context), 230 file_system_context_(file_system_context), 231 render_process_id_(render_process_id), 232 render_frame_id_(render_frame_id), 233 weak_factory_(this) {} 234 235 MediaResourceGetterImpl::~MediaResourceGetterImpl() {} 236 237 void MediaResourceGetterImpl::GetCookies( 238 const GURL& url, const GURL& first_party_for_cookies, 239 const GetCookieCB& callback) { 240 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 241 scoped_refptr<CookieGetterTask> task = new CookieGetterTask( 242 browser_context_, render_process_id_, render_frame_id_); 243 244 GetCookieCB cb = base::Bind(&MediaResourceGetterImpl::GetCookiesCallback, 245 weak_factory_.GetWeakPtr(), 246 callback); 247 BrowserThread::PostTask( 248 BrowserThread::IO, 249 FROM_HERE, 250 base::Bind(&CookieGetterTask::RequestCookies, 251 task, url, first_party_for_cookies, 252 base::Bind(&ReturnResultOnUIThread, cb))); 253 } 254 255 void MediaResourceGetterImpl::GetCookiesCallback( 256 const GetCookieCB& callback, const std::string& cookies) { 257 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 258 callback.Run(cookies); 259 } 260 261 void MediaResourceGetterImpl::GetPlatformPathFromURL( 262 const GURL& url, const GetPlatformPathCB& callback) { 263 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 264 DCHECK(url.SchemeIsFileSystem() || url.SchemeIs(url::kBlobScheme)); 265 266 GetPlatformPathCB cb = 267 base::Bind(&MediaResourceGetterImpl::GetPlatformPathCallback, 268 weak_factory_.GetWeakPtr(), 269 callback); 270 271 if (url.SchemeIs(url::kBlobScheme)) { 272 BrowserThread::PostTask( 273 BrowserThread::IO, 274 FROM_HERE, 275 base::Bind(&RequestPlatformPathFromBlobURL, url, browser_context_, cb)); 276 return; 277 } 278 279 scoped_refptr<fileapi::FileSystemContext> context(file_system_context_); 280 BrowserThread::PostTask( 281 BrowserThread::FILE, 282 FROM_HERE, 283 base::Bind(&RequestPlaformPathFromFileSystemURL, url, render_process_id_, 284 context, cb)); 285 } 286 287 void MediaResourceGetterImpl::GetPlatformPathCallback( 288 const GetPlatformPathCB& callback, const std::string& platform_path) { 289 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 290 callback.Run(platform_path); 291 } 292 293 void MediaResourceGetterImpl::ExtractMediaMetadata( 294 const std::string& url, const std::string& cookies, 295 const std::string& user_agent, const ExtractMediaMetadataCB& callback) { 296 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 297 base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool(); 298 pool->PostWorkerTask( 299 FROM_HERE, 300 base::Bind(&GetMediaMetadata, url, cookies, user_agent, callback)); 301 } 302 303 void MediaResourceGetterImpl::ExtractMediaMetadata( 304 const int fd, const int64 offset, const int64 size, 305 const ExtractMediaMetadataCB& callback) { 306 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 307 base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool(); 308 pool->PostWorkerTask( 309 FROM_HERE, 310 base::Bind(&GetMediaMetadataFromFd, fd, offset, size, callback)); 311 } 312 313 // static 314 bool MediaResourceGetterImpl::RegisterMediaResourceGetter(JNIEnv* env) { 315 return RegisterNativesImpl(env); 316 } 317 318 } // namespace content 319