Home | History | Annotate | Download | only in WebCoreSupport
      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 
     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 
     76 base::Lock* WebUrlLoaderClient::syncLock() {
     77     static Lock s_syncLock;
     78     return &s_syncLock;
     79 }
     80 
     81 base::ConditionVariable* WebUrlLoaderClient::syncCondition() {
     82     static base::ConditionVariable s_syncCondition(syncLock());
     83     return &s_syncCondition;
     84 }
     85 
     86 WebUrlLoaderClient::~WebUrlLoaderClient()
     87 {
     88 }
     89 
     90 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 
    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 
    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.
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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*.
    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.
    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
    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 
    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
    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
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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