OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 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/android/download_controller.h" | |
6 | |
7 #include "base/android/jni_android.h" | |
8 #include "base/android/jni_string.h" | |
9 #include "base/bind.h" | |
10 #include "base/logging.h" | |
11 #include "base/memory/scoped_ptr.h" | |
12 #include "content/browser/android/content_view_core_impl.h" | |
13 #include "content/browser/download/download_item_impl.h" | |
14 #include "content/browser/renderer_host/render_process_host_impl.h" | |
15 #include "content/browser/renderer_host/render_view_host_delegate.h" | |
16 #include "content/browser/renderer_host/render_view_host_impl.h" | |
17 #include "content/browser/renderer_host/resource_dispatcher_host_impl.h" | |
18 #include "content/browser/web_contents/web_contents_impl.h" | |
19 #include "content/public/browser/browser_thread.h" | |
20 #include "content/public/browser/global_request_id.h" | |
21 #include "jni/DownloadController_jni.h" | |
22 #include "net/cookies/cookie_options.h" | |
23 #include "net/cookies/cookie_store.h" | |
24 #include "net/http/http_request_headers.h" | |
25 #include "net/url_request/url_request.h" | |
26 #include "net/url_request/url_request_context.h" | |
27 | |
28 using base::android::AttachCurrentThread; | |
29 using base::android::CheckException; | |
30 using base::android::ConvertUTF8ToJavaString; | |
31 using base::android::GetClass; | |
32 using base::android::ScopedJavaLocalRef; | |
33 | |
34 namespace { | |
35 const char kDownloadControllerClassPathName[] = | |
36 "org/chromium/content/browser/DownloadController"; | |
37 } // namespace | |
38 | |
39 namespace content { | |
40 | |
41 // JNI methods | |
42 static void Init(JNIEnv* env, jobject obj) { | |
43 DownloadController::GetInstance()->Init(env, obj); | |
44 } | |
45 | |
46 struct DownloadController::JavaObject { | |
47 ScopedJavaLocalRef<jobject> Controller(JNIEnv* env) { | |
48 return GetRealObject(env, obj); | |
49 } | |
50 jweak obj; | |
51 }; | |
52 | |
53 // static | |
54 bool DownloadController::RegisterDownloadController(JNIEnv* env) { | |
55 return RegisterNativesImpl(env); | |
56 } | |
57 | |
58 DownloadController* DownloadController::GetInstance() { | |
59 return Singleton<DownloadController>::get(); | |
60 } | |
61 | |
62 DownloadController::DownloadController() | |
63 : java_object_(NULL) { | |
64 } | |
65 | |
66 DownloadController::~DownloadController() { | |
67 if (java_object_) { | |
68 JNIEnv* env = AttachCurrentThread(); | |
69 env->DeleteWeakGlobalRef(java_object_->obj); | |
70 delete java_object_; | |
71 CheckException(env); | |
72 } | |
73 } | |
74 | |
75 // Initialize references to Java object. | |
76 void DownloadController::Init(JNIEnv* env, jobject obj) { | |
77 java_object_ = new JavaObject; | |
78 java_object_->obj = env->NewWeakGlobalRef(obj); | |
79 } | |
80 | |
81 void DownloadController::CreateGETDownload( | |
82 RenderViewHost* render_view_host, | |
83 int request_id) { | |
84 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
85 int render_process_id = render_view_host->GetProcess()->GetID(); | |
86 GlobalRequestID global_id(render_process_id, request_id); | |
87 | |
88 // We are yielding the UI thread and render_view_host may go away by | |
89 // the time we come back. Pass along render_process_id and render_view_id | |
90 // to retrieve it later (if it still exists). | |
91 BrowserThread::PostTask( | |
92 BrowserThread::IO, FROM_HERE, | |
93 base::Bind(&DownloadController::PrepareDownloadInfo, | |
94 base::Unretained(this), global_id, | |
95 render_process_id, | |
96 render_view_host->GetRoutingID())); | |
97 } | |
98 | |
99 void DownloadController::PrepareDownloadInfo( | |
100 const GlobalRequestID& global_id, | |
101 int render_process_id, int render_view_id) { | |
102 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
103 | |
104 net::URLRequest* request = | |
105 ResourceDispatcherHostImpl::Get()->GetURLRequest(global_id); | |
106 DCHECK(request) << "Request to download not found."; | |
107 | |
108 DownloadInfoAndroid info_android(request); | |
109 | |
110 net::CookieStore* cookie_store = request->context()->cookie_store(); | |
111 if (cookie_store) { | |
112 net::CookieMonster* cookie_monster = cookie_store->GetCookieMonster(); | |
113 if (cookie_monster) { | |
114 cookie_monster->GetAllCookiesForURLAsync( | |
115 request->url(), | |
116 base::Bind(&DownloadController::CheckPolicyAndLoadCookies, | |
117 base::Unretained(this), info_android, render_process_id, | |
118 render_view_id, global_id)); | |
119 } else { | |
120 DoLoadCookies( | |
121 info_android, render_process_id, render_view_id, global_id); | |
122 } | |
123 } else { | |
124 // Can't get any cookies, start android download. | |
125 StartAndroidDownload(info_android, render_process_id, render_view_id); | |
126 } | |
127 } | |
128 | |
129 void DownloadController::CheckPolicyAndLoadCookies( | |
130 const DownloadInfoAndroid& info, int render_process_id, | |
131 int render_view_id, const GlobalRequestID& global_id, | |
132 const net::CookieList& cookie_list) { | |
133 net::URLRequest* request = | |
134 ResourceDispatcherHostImpl::Get()->GetURLRequest(global_id); | |
135 DCHECK(request) << "Request to download not found."; | |
136 | |
137 if (request->context()->network_delegate()->CanGetCookies( | |
138 *request, cookie_list)) { | |
139 DoLoadCookies(info, render_process_id, render_view_id, global_id); | |
140 } else { | |
141 StartAndroidDownload(info, render_process_id, render_view_id); | |
142 } | |
143 } | |
144 | |
145 void DownloadController::DoLoadCookies( | |
146 const DownloadInfoAndroid& info, int render_process_id, | |
147 int render_view_id, const GlobalRequestID& global_id) { | |
148 net::CookieOptions options; | |
149 options.set_include_httponly(); | |
150 | |
151 net::URLRequest* request = | |
152 ResourceDispatcherHostImpl::Get()->GetURLRequest(global_id); | |
153 DCHECK(request) << "Request to download not found."; | |
154 | |
155 request->context()->cookie_store()->GetCookiesWithOptionsAsync( | |
156 info.url, options, | |
157 base::Bind(&DownloadController::OnCookieResponse, | |
158 base::Unretained(this), info, render_process_id, | |
159 render_view_id)); | |
160 } | |
161 | |
162 void DownloadController::OnCookieResponse(DownloadInfoAndroid download_info, | |
163 int render_process_id, | |
164 int render_view_id, | |
165 const std::string& cookie) { | |
166 download_info.cookie = cookie; | |
167 | |
168 // We have everything we need, start Android download. | |
169 StartAndroidDownload(download_info, render_process_id, render_view_id); | |
170 } | |
171 | |
172 void DownloadController::StartAndroidDownload( | |
173 const DownloadInfoAndroid& info, | |
174 int render_process_id, | |
175 int render_view_id) { | |
176 // Call ourself on the UI thread if not already on it. | |
177 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { | |
178 BrowserThread::PostTask( | |
179 BrowserThread::UI, FROM_HERE, | |
180 base::Bind(&DownloadController::StartAndroidDownload, | |
181 base::Unretained(this), info, render_process_id, | |
182 render_view_id)); | |
183 return; | |
184 } | |
185 | |
186 JNIEnv* env = AttachCurrentThread(); | |
187 | |
188 // Call newHttpGetDownload | |
189 ScopedJavaLocalRef<jobject> view = GetContentView(render_process_id, | |
190 render_view_id); | |
191 if (view.is_null()) { | |
192 // The view went away. Can't proceed. | |
193 LOG(ERROR) << "Download failed on URL:" << info.url.spec(); | |
194 return; | |
195 } | |
196 | |
197 ScopedJavaLocalRef<jstring> jurl = | |
198 ConvertUTF8ToJavaString(env, info.url.spec()); | |
199 ScopedJavaLocalRef<jstring> juser_agent = | |
200 ConvertUTF8ToJavaString(env, info.user_agent); | |
201 ScopedJavaLocalRef<jstring> jcontent_disposition = | |
202 ConvertUTF8ToJavaString(env, info.content_disposition); | |
203 ScopedJavaLocalRef<jstring> jmime_type = | |
204 ConvertUTF8ToJavaString(env, info.original_mime_type); | |
205 ScopedJavaLocalRef<jstring> jcookie = | |
206 ConvertUTF8ToJavaString(env, info.cookie); | |
207 ScopedJavaLocalRef<jstring> jreferer = | |
208 ConvertUTF8ToJavaString(env, info.referer); | |
209 | |
210 Java_DownloadController_newHttpGetDownload( | |
211 env, GetJavaObject()->Controller(env).obj(), view.obj(), jurl.obj(), | |
212 juser_agent.obj(), jcontent_disposition.obj(), jmime_type.obj(), | |
213 jcookie.obj(), jreferer.obj(), info.total_bytes); | |
214 } | |
215 | |
216 void DownloadController::OnPostDownloadStarted( | |
217 WebContents* web_contents, | |
218 DownloadItem* download_item) { | |
219 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
220 JNIEnv* env = AttachCurrentThread(); | |
221 | |
222 // Register for updates to the DownloadItem. | |
223 download_item->AddObserver(this); | |
224 | |
225 ScopedJavaLocalRef<jobject> view = | |
226 GetContentViewCoreFromWebContents(web_contents); | |
227 if(view.is_null()) { | |
228 // The view went away. Can't proceed. | |
229 return; | |
230 } | |
231 | |
232 Java_DownloadController_onHttpPostDownloadStarted( | |
233 env, GetJavaObject()->Controller(env).obj(), view.obj()); | |
234 } | |
235 | |
236 void DownloadController::OnDownloadUpdated(DownloadItem* item) { | |
237 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
238 | |
239 if (item->GetState() != DownloadItem::COMPLETE) | |
240 return; | |
241 | |
242 // Call onHttpPostDownloadCompleted | |
243 JNIEnv* env = AttachCurrentThread(); | |
244 ScopedJavaLocalRef<jstring> jurl = | |
245 ConvertUTF8ToJavaString(env, item->GetURL().spec()); | |
246 ScopedJavaLocalRef<jstring> jcontent_disposition = | |
247 ConvertUTF8ToJavaString(env, item->GetContentDisposition()); | |
248 ScopedJavaLocalRef<jstring> jmime_type = | |
249 ConvertUTF8ToJavaString(env, item->GetMimeType()); | |
250 ScopedJavaLocalRef<jstring> jpath = | |
251 ConvertUTF8ToJavaString(env, item->GetFullPath().value()); | |
252 | |
253 ScopedJavaLocalRef<jobject> view_core = GetContentViewCoreFromWebContents( | |
254 item->GetWebContents()); | |
255 if (view_core.is_null()) { | |
256 // We can get NULL WebContents from the DownloadItem. | |
257 return; | |
258 } | |
259 | |
260 Java_DownloadController_onHttpPostDownloadCompleted(env, | |
261 GetJavaObject()->Controller(env).obj(), view_core.obj(), jurl.obj(), | |
262 jcontent_disposition.obj(), jmime_type.obj(), jpath.obj(), | |
263 item->GetReceivedBytes(), true); | |
264 } | |
265 | |
266 void DownloadController::OnDownloadOpened(DownloadItem* item) { | |
267 } | |
268 | |
269 ScopedJavaLocalRef<jobject> DownloadController::GetContentView( | |
270 int render_process_id, int render_view_id) { | |
271 RenderViewHost* render_view_host = | |
272 RenderViewHost::FromID(render_process_id, render_view_id); | |
273 | |
274 if (!render_view_host) | |
275 return ScopedJavaLocalRef<jobject>(); | |
276 | |
277 WebContents* web_contents = | |
278 render_view_host->GetDelegate()->GetAsWebContents(); | |
279 | |
280 if (!web_contents) | |
281 return ScopedJavaLocalRef<jobject>(); | |
282 | |
283 return GetContentViewCoreFromWebContents(web_contents); | |
284 } | |
285 | |
286 ScopedJavaLocalRef<jobject> | |
287 DownloadController::GetContentViewCoreFromWebContents( | |
288 WebContents* web_contents) { | |
289 if (!web_contents) | |
290 return ScopedJavaLocalRef<jobject>(); | |
291 | |
292 ContentViewCore* view_core = web_contents->GetContentNativeView(); | |
293 return view_core ? view_core->GetJavaObject() : | |
294 ScopedJavaLocalRef<jobject>(); | |
295 } | |
296 | |
297 DownloadController::JavaObject* DownloadController::GetJavaObject() { | |
298 if (!java_object_) { | |
299 // Initialize Java DownloadController by calling | |
300 // DownloadController.getInstance(), which will call Init() | |
301 // if Java DownloadController is not instantiated already. | |
302 JNIEnv* env = AttachCurrentThread(); | |
303 ScopedJavaLocalRef<jclass> clazz = | |
304 GetClass(env, kDownloadControllerClassPathName); | |
305 jmethodID get_instance = GetStaticMethodID(env, clazz, "getInstance", | |
306 "()Lorg/chromium/content/browser/DownloadController;"); | |
307 ScopedJavaLocalRef<jobject> jobj(env, | |
308 env->CallStaticObjectMethod(clazz.obj(), get_instance)); | |
309 CheckException(env); | |
310 } | |
311 | |
312 DCHECK(java_object_); | |
313 return java_object_; | |
314 } | |
315 | |
316 DownloadController::DownloadInfoAndroid::DownloadInfoAndroid( | |
317 net::URLRequest* request) { | |
318 request->GetResponseHeaderByName("content-disposition", &content_disposition); | |
319 request->GetMimeType(&original_mime_type); | |
320 request->extra_request_headers().GetHeader( | |
321 net::HttpRequestHeaders::kUserAgent, &user_agent); | |
322 GURL referer_url(request->GetSanitizedReferrer()); | |
323 if (referer_url.is_valid()) | |
324 referer = referer_url.spec(); | |
325 if (!request->url_chain().empty()) { | |
326 original_url = request->url_chain().front(); | |
327 url = request->url_chain().back(); | |
328 } | |
329 } | |
330 | |
331 DownloadController::DownloadInfoAndroid::~DownloadInfoAndroid() {} | |
332 | |
333 } // namespace content | |
OLD | NEW |