• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2010, The Android Open Source Project
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *  * Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  *  * Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #define LOG_TAG "WebUrlLoaderClient"
27 
28 #include "config.h"
29 #include "WebUrlLoaderClient.h"
30 
31 #include "ChromiumIncludes.h"
32 #include "OwnPtr.h"
33 #include "ResourceHandle.h"
34 #include "ResourceHandleClient.h"
35 #include "ResourceResponse.h"
36 #include "WebCoreFrameBridge.h"
37 #include "WebRequest.h"
38 #include "WebResourceRequest.h"
39 
40 #include <utils/Log.h>
41 #include <wtf/text/CString.h>
42 
43 using base::Lock;
44 using base::AutoLock;
45 
46 namespace android {
47 
ioThread()48 base::Thread* WebUrlLoaderClient::ioThread()
49 {
50     static base::Thread* networkThread = 0;
51     static Lock networkThreadLock;
52 
53     // Multiple threads appear to access the ioThread so we must ensure the
54     // critical section ordering.
55     AutoLock lock(networkThreadLock);
56 
57     if (!networkThread)
58         networkThread = new base::Thread("network");
59 
60     if (!networkThread)
61         return 0;
62 
63     if (networkThread->IsRunning())
64         return networkThread;
65 
66     base::Thread::Options options;
67     options.message_loop_type = MessageLoop::TYPE_IO;
68     if (!networkThread->StartWithOptions(options)) {
69         delete networkThread;
70         networkThread = 0;
71     }
72 
73     return networkThread;
74 }
75 
syncLock()76 base::Lock* WebUrlLoaderClient::syncLock() {
77     static Lock s_syncLock;
78     return &s_syncLock;
79 }
80 
syncCondition()81 base::ConditionVariable* WebUrlLoaderClient::syncCondition() {
82     static base::ConditionVariable s_syncCondition(syncLock());
83     return &s_syncCondition;
84 }
85 
~WebUrlLoaderClient()86 WebUrlLoaderClient::~WebUrlLoaderClient()
87 {
88 }
89 
isActive() const90 bool WebUrlLoaderClient::isActive() const
91 {
92     if (m_cancelling)
93         return false;
94     if (!m_resourceHandle)
95         return false;
96     if (!m_resourceHandle->client())
97         return false;
98     if (m_finished)
99         return false;
100 
101     return true;
102 }
103 
WebUrlLoaderClient(WebFrame * webFrame,WebCore::ResourceHandle * resourceHandle,const WebCore::ResourceRequest & resourceRequest)104 WebUrlLoaderClient::WebUrlLoaderClient(WebFrame* webFrame, WebCore::ResourceHandle* resourceHandle, const WebCore::ResourceRequest& resourceRequest)
105     : m_webFrame(webFrame)
106     , m_resourceHandle(resourceHandle)
107     , m_isMainResource(false)
108     , m_isMainFrame(false)
109     , m_isCertMimeType(false)
110     , m_cancelling(false)
111     , m_sync(false)
112     , m_finished(false)
113 {
114     bool block = webFrame->blockNetworkLoads() && (resourceRequest.url().protocolIs("http") || resourceRequest.url().protocolIs("https"));
115     WebResourceRequest webResourceRequest(resourceRequest, block);
116     UrlInterceptResponse* intercept = webFrame->shouldInterceptRequest(resourceRequest.url().string());
117     if (intercept) {
118         m_request = new WebRequest(this, webResourceRequest, intercept);
119         return;
120     }
121 
122     m_request = new WebRequest(this, webResourceRequest);
123 
124     // Set uploads before start is called on the request
125     if (resourceRequest.httpBody() && !(webResourceRequest.method() == "GET" || webResourceRequest.method() == "HEAD")) {
126         Vector<FormDataElement>::iterator iter;
127         Vector<FormDataElement> elements = resourceRequest.httpBody()->elements();
128         for (iter = elements.begin(); iter != elements.end(); iter++) {
129             FormDataElement element = *iter;
130 
131             switch (element.m_type) {
132             case FormDataElement::data:
133                 if (!element.m_data.isEmpty()) {
134                     // WebKit sometimes gives up empty data to append. These aren't
135                     // necessary so we just optimize those out here.
136                     base::Thread* thread = ioThread();
137                     if (thread) {
138                         Vector<char>* data = new Vector<char>(element.m_data);
139                         thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::appendBytesToUpload, data));
140                     }
141                 }
142                 break;
143             case FormDataElement::encodedFile:
144                 {
145                     // Chromium check if it is a directory by checking
146                     // element.m_fileLength, that doesn't work in Android
147                     std::string filename = element.m_filename.utf8().data();
148                     if (filename.size()) {
149                         // Change from a url string to a filename
150                         if (filename.find("file://") == 0) // Found at pos 0
151                             filename.erase(0, 7);
152                         base::Thread* thread = ioThread();
153                         if (thread)
154                             thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::appendFileToUpload, filename));
155                     }
156                 }
157                 break;
158 #if ENABLE(BLOB)
159             case FormDataElement::encodedBlob:
160                 ALOG_ASSERT(false, "Unexpected use of FormDataElement::encodedBlob");
161                 break;
162 #endif // ENABLE(BLOB)
163             default:
164                 ALOG_ASSERT(false, "Unexpected default case in WebUrlLoaderClient.cpp");
165                 break;
166             }
167         }
168     }
169 }
170 
start(bool isMainResource,bool isMainFrame,bool sync,WebRequestContext * context)171 bool WebUrlLoaderClient::start(bool isMainResource, bool isMainFrame, bool sync, WebRequestContext* context)
172 {
173     base::Thread* thread = ioThread();
174     if (!thread) {
175         return false;
176     }
177 
178     m_isMainResource = isMainResource;
179     m_isMainFrame = isMainFrame;
180     m_sync = sync;
181     if (m_sync) {
182         AutoLock autoLock(*syncLock());
183         m_request->setSync(sync);
184         m_request->setRequestContext(context);
185         thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::start));
186 
187         // Run callbacks until the queue is exhausted and m_finished is true.
188         // Sometimes, a sync load can wait forever and lock up the WebCore thread,
189         // here we use TimedWait() with multiple tries to avoid locking.
190         const int kMaxNumTimeout = 3;
191         const int kCallbackWaitingTime = 10;
192         int num_timeout = 0;
193         while(!m_finished) {
194             while (!m_queue.empty()) {
195                 OwnPtr<Task> task(m_queue.front());
196                 m_queue.pop_front();
197                 task->Run();
198             }
199             if (m_finished) break;
200 
201             syncCondition()->TimedWait(base::TimeDelta::FromSeconds(kCallbackWaitingTime));
202             if (m_queue.empty()) {
203                 ALOGE("Synchronous request timed out after %d seconds for the %dth try, URL: %s",
204                      kCallbackWaitingTime, num_timeout, m_request->getUrl().c_str());
205                 num_timeout++;
206                 if (num_timeout >= kMaxNumTimeout) {
207                     cancel();
208                     m_resourceHandle = 0;
209                     return false;
210                 }
211             }
212         }
213 
214         // This may be the last reference to us, so we may be deleted now.
215         // Don't access any more member variables after releasing this reference.
216         m_resourceHandle = 0;
217     } else {
218         // Asynchronous start.
219         // Important to set this before the thread starts so it has a reference and can't be deleted
220         // before the task starts running on the IO thread.
221         m_request->setRequestContext(context);
222         thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::start));
223     }
224     return true;
225 }
226 
227 namespace {
228 // Check if the mime type is for certificate installation.
229 // The items must be consistent with the sCertificateTypeMap
230 // in frameworks/base/core/java/android/webkit/CertTool.java.
isMimeTypeForCert(const std::string & mimeType)231 bool isMimeTypeForCert(const std::string& mimeType)
232 {
233     static std::hash_set<std::string> sCertificateTypeSet;
234     if (sCertificateTypeSet.empty()) {
235         sCertificateTypeSet.insert("application/x-x509-ca-cert");
236         sCertificateTypeSet.insert("application/x-x509-user-cert");
237         sCertificateTypeSet.insert("application/x-pkcs12");
238     }
239     return sCertificateTypeSet.find(mimeType) != sCertificateTypeSet.end();
240 }
241 }
242 
downloadFile()243 void WebUrlLoaderClient::downloadFile()
244 {
245     if (m_response) {
246         std::string contentDisposition;
247         m_response->getHeader("content-disposition", &contentDisposition);
248         m_webFrame->downloadStart(m_response->getUrl(), m_request->getUserAgent(), contentDisposition, m_response->getMimeType(), m_request->getReferer(), m_response->getExpectedSize());
249 
250         m_isCertMimeType = isMimeTypeForCert(m_response->getMimeType());
251         // Currently, only certificate mime type needs to receive the data.
252         // Other mime type, e.g. wav, will send the url to other application
253         // which will load the data by url.
254         if (!m_isCertMimeType)
255             cancel();
256     } else {
257         ALOGE("Unexpected call to downloadFile() before didReceiveResponse(). URL: %s", m_request->getUrl().c_str());
258         // TODO: Turn off asserts crashing before release
259         // http://b/issue?id=2951985
260         CRASH();
261     }
262 }
263 
cancel()264 void WebUrlLoaderClient::cancel()
265 {
266     if (!isActive())
267         return;
268 
269     m_cancelling = true;
270 
271     base::Thread* thread = ioThread();
272     if (thread)
273         thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::cancel));
274 }
275 
pauseLoad(bool pause)276 void WebUrlLoaderClient::pauseLoad(bool pause)
277 {
278     if (!isActive())
279         return;
280 
281     base::Thread* thread = ioThread();
282     if (thread)
283         thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::pauseLoad, pause));
284 }
285 
setAuth(const std::string & username,const std::string & password)286 void WebUrlLoaderClient::setAuth(const std::string& username, const std::string& password)
287 {
288     if (!isActive())
289         return;
290 
291     base::Thread* thread = ioThread();
292     if (!thread) {
293         return;
294     }
295     string16 username16 = ASCIIToUTF16(username);
296     string16 password16 = ASCIIToUTF16(password);
297     thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::setAuth, username16, password16));
298 }
299 
cancelAuth()300 void WebUrlLoaderClient::cancelAuth()
301 {
302     if (!isActive())
303         return;
304 
305     base::Thread* thread = ioThread();
306     if (!thread) {
307         return;
308     }
309     thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::cancelAuth));
310 }
311 
proceedSslCertError()312 void WebUrlLoaderClient::proceedSslCertError()
313 {
314     base::Thread* thread = ioThread();
315     if (isActive() && thread)
316         thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::proceedSslCertError));
317     this->Release();
318 }
319 
cancelSslCertError(int cert_error)320 void WebUrlLoaderClient::cancelSslCertError(int cert_error)
321 {
322     base::Thread* thread = ioThread();
323     if (isActive() && thread)
324         thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::cancelSslCertError, cert_error));
325     this->Release();
326 }
327 
finish()328 void WebUrlLoaderClient::finish()
329 {
330     m_finished = true;
331     if (!m_sync) {
332         // This is the last reference to us, so we will be deleted now.
333         // We only release the reference here if start() was called asynchronously!
334         m_resourceHandle = 0;
335     }
336     m_request = 0;
337 }
338 
339 namespace {
340 // Trampoline to wrap a Chromium Task* in a WebKit-style static function + void*.
RunTask(void * v)341 static void RunTask(void* v) {
342     OwnPtr<Task> task(static_cast<Task*>(v));
343     task->Run();
344 }
345 }
346 
347 // This is called from the IO thread, and dispatches the callback to the main thread.
maybeCallOnMainThread(Task * task)348 void WebUrlLoaderClient::maybeCallOnMainThread(Task* task)
349 {
350     if (m_sync) {
351         AutoLock autoLock(*syncLock());
352         if (m_queue.empty()) {
353             syncCondition()->Broadcast();
354         }
355         m_queue.push_back(task);
356     } else {
357         // Let WebKit handle it.
358         callOnMainThread(RunTask, task);
359     }
360 }
361 
362 // Response methods
didReceiveResponse(PassOwnPtr<WebResponse> webResponse)363 void WebUrlLoaderClient::didReceiveResponse(PassOwnPtr<WebResponse> webResponse)
364 {
365     if (!isActive())
366         return;
367 
368     m_response = webResponse;
369     m_resourceHandle->client()->didReceiveResponse(m_resourceHandle.get(), m_response->createResourceResponse());
370 
371     // Set the main page's certificate to WebView.
372     if (m_isMainResource && m_isMainFrame) {
373         const net::SSLInfo& ssl_info = m_response->getSslInfo();
374         if (ssl_info.is_valid()) {
375             std::vector<std::string> chain_bytes;
376             ssl_info.cert->GetChainDEREncodedBytes(&chain_bytes);
377             m_webFrame->setCertificate(chain_bytes[0]);
378         }
379 
380         // Look for X-Auto-Login on the main resource to log in the user.
381         std::string login;
382         if (m_response->getHeader("x-auto-login", &login))
383             m_webFrame->autoLogin(login);
384     }
385 }
386 
didReceiveData(scoped_refptr<net::IOBuffer> buf,int size)387 void WebUrlLoaderClient::didReceiveData(scoped_refptr<net::IOBuffer> buf, int size)
388 {
389     if (m_isMainResource && m_isCertMimeType) {
390         m_webFrame->didReceiveData(buf->data(), size);
391     }
392 
393     if (!isActive() || !size)
394         return;
395 
396     // didReceiveData will take a copy of the data
397     if (m_resourceHandle && m_resourceHandle->client())
398         m_resourceHandle->client()->didReceiveData(m_resourceHandle.get(), buf->data(), size, size);
399 }
400 
401 // For data url's
didReceiveDataUrl(PassOwnPtr<std::string> str)402 void WebUrlLoaderClient::didReceiveDataUrl(PassOwnPtr<std::string> str)
403 {
404     if (!isActive() || !str->size())
405         return;
406 
407     // didReceiveData will take a copy of the data
408     m_resourceHandle->client()->didReceiveData(m_resourceHandle.get(), str->data(), str->size(), str->size());
409 }
410 
411 // For special android files
didReceiveAndroidFileData(PassOwnPtr<std::vector<char>> vector)412 void WebUrlLoaderClient::didReceiveAndroidFileData(PassOwnPtr<std::vector<char> > vector)
413 {
414     if (!isActive() || !vector->size())
415         return;
416 
417     // didReceiveData will take a copy of the data
418     m_resourceHandle->client()->didReceiveData(m_resourceHandle.get(), vector->begin(), vector->size(), vector->size());
419 }
420 
didFail(PassOwnPtr<WebResponse> webResponse)421 void WebUrlLoaderClient::didFail(PassOwnPtr<WebResponse> webResponse)
422 {
423     if (isActive())
424         m_resourceHandle->client()->didFail(m_resourceHandle.get(), webResponse->createResourceError());
425 
426     // Always finish a request, if not it will leak
427     finish();
428 }
429 
willSendRequest(PassOwnPtr<WebResponse> webResponse)430 void WebUrlLoaderClient::willSendRequest(PassOwnPtr<WebResponse> webResponse)
431 {
432     if (!isActive())
433         return;
434 
435     KURL url = webResponse->createKurl();
436     OwnPtr<WebCore::ResourceRequest> resourceRequest(new WebCore::ResourceRequest(url));
437     m_resourceHandle->client()->willSendRequest(m_resourceHandle.get(), *resourceRequest, webResponse->createResourceResponse());
438 
439     // WebKit may have killed the request.
440     if (!isActive())
441         return;
442 
443     // Like Chrome, we only follow the redirect if WebKit left the URL unmodified.
444     if (url == resourceRequest->url()) {
445         ioThread()->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::followDeferredRedirect));
446     } else {
447         cancel();
448     }
449 }
450 
didFinishLoading()451 void WebUrlLoaderClient::didFinishLoading()
452 {
453     if (isActive())
454         m_resourceHandle->client()->didFinishLoading(m_resourceHandle.get(), 0);
455 
456     if (m_isMainResource && m_isCertMimeType) {
457         m_webFrame->didFinishLoading();
458     }
459 
460     // Always finish a request, if not it will leak
461     finish();
462 }
463 
authRequired(scoped_refptr<net::AuthChallengeInfo> authChallengeInfo,bool firstTime,bool suppressDialog)464 void WebUrlLoaderClient::authRequired(scoped_refptr<net::AuthChallengeInfo> authChallengeInfo, bool firstTime, bool suppressDialog)
465 {
466     if (!isActive())
467         return;
468 
469     std::string host = base::SysWideToUTF8(authChallengeInfo->host_and_port);
470     std::string realm = base::SysWideToUTF8(authChallengeInfo->realm);
471 
472     m_webFrame->didReceiveAuthenticationChallenge(this, host, realm, firstTime, suppressDialog);
473 }
474 
reportSslCertError(int cert_error,net::X509Certificate * cert)475 void WebUrlLoaderClient::reportSslCertError(int cert_error, net::X509Certificate* cert)
476 {
477     if (!isActive())
478         return;
479 
480     std::vector<std::string> chain_bytes;
481     cert->GetChainDEREncodedBytes(&chain_bytes);
482     this->AddRef();
483     m_webFrame->reportSslCertError(this, cert_error, chain_bytes[0], m_request->getUrl());
484 }
485 
sslClientCert(EVP_PKEY * pkey,net::X509Certificate * chain)486 void WebUrlLoaderClient::sslClientCert(EVP_PKEY* pkey, net::X509Certificate* chain)
487 {
488     base::Thread* thread = ioThread();
489     scoped_refptr<net::X509Certificate> scopedChain(chain);
490     if (isActive() && thread)
491         thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::sslClientCert, pkey, scopedChain));
492     this->Release();
493 }
494 
requestClientCert(net::SSLCertRequestInfo * cert_request_info)495 void WebUrlLoaderClient::requestClientCert(net::SSLCertRequestInfo* cert_request_info)
496 {
497     if (!isActive())
498         return;
499 
500     std::string host_and_port = cert_request_info->host_and_port;
501     this->AddRef();
502     m_webFrame->requestClientCert(this, host_and_port);
503 }
504 
505 } // namespace android
506