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