diff options
Diffstat (limited to 'Source/WebKit/android/WebCoreSupport/WebUrlLoaderClient.cpp')
-rw-r--r-- | Source/WebKit/android/WebCoreSupport/WebUrlLoaderClient.cpp | 482 |
1 files changed, 482 insertions, 0 deletions
diff --git a/Source/WebKit/android/WebCoreSupport/WebUrlLoaderClient.cpp b/Source/WebKit/android/WebCoreSupport/WebUrlLoaderClient.cpp new file mode 100644 index 0000000..cf218e7 --- /dev/null +++ b/Source/WebKit/android/WebCoreSupport/WebUrlLoaderClient.cpp @@ -0,0 +1,482 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "WebUrlLoaderClient" + +#include "config.h" +#include "WebUrlLoaderClient.h" + +#include "ChromiumIncludes.h" +#include "OwnPtr.h" +#include "ResourceHandle.h" +#include "ResourceHandleClient.h" +#include "ResourceResponse.h" +#include "WebCoreFrameBridge.h" +#include "WebRequest.h" +#include "WebResourceRequest.h" + +#include <wtf/text/CString.h> + +namespace android { + +base::Thread* WebUrlLoaderClient::ioThread() +{ + static base::Thread* networkThread = 0; + static Lock networkThreadLock; + + // Multiple threads appear to access the ioThread so we must ensure the + // critical section ordering. + AutoLock lock(networkThreadLock); + + if (!networkThread) + networkThread = new base::Thread("network"); + + if (!networkThread) + return 0; + + if (networkThread->IsRunning()) + return networkThread; + + base::Thread::Options options; + options.message_loop_type = MessageLoop::TYPE_IO; + if (!networkThread->StartWithOptions(options)) { + delete networkThread; + networkThread = 0; + } + + return networkThread; +} + +Lock* WebUrlLoaderClient::syncLock() { + static Lock s_syncLock; + return &s_syncLock; +} + +ConditionVariable* WebUrlLoaderClient::syncCondition() { + static ConditionVariable s_syncCondition(syncLock()); + return &s_syncCondition; +} + +WebUrlLoaderClient::~WebUrlLoaderClient() +{ +} + +bool WebUrlLoaderClient::isActive() const +{ + if (m_cancelling) + return false; + if (!m_resourceHandle) + return false; + if (!m_resourceHandle->client()) + return false; + if (m_finished) + return false; + + return true; +} + +WebUrlLoaderClient::WebUrlLoaderClient(WebFrame* webFrame, WebCore::ResourceHandle* resourceHandle, const WebCore::ResourceRequest& resourceRequest) + : m_webFrame(webFrame) + , m_resourceHandle(resourceHandle) + , m_isMainResource(false) + , m_isMainFrame(false) + , m_isCertMimeType(false) + , m_cancelling(false) + , m_sync(false) + , m_finished(false) +{ + WebResourceRequest webResourceRequest(resourceRequest); + UrlInterceptResponse* intercept = webFrame->shouldInterceptRequest(resourceRequest.url().string()); + if (intercept) { + m_request = new WebRequest(this, webResourceRequest, intercept); + return; + } + + m_request = new WebRequest(this, webResourceRequest); + + // Set uploads before start is called on the request + if (resourceRequest.httpBody() && !(webResourceRequest.method() == "GET" || webResourceRequest.method() == "HEAD")) { + Vector<FormDataElement>::iterator iter; + Vector<FormDataElement> elements = resourceRequest.httpBody()->elements(); + for (iter = elements.begin(); iter != elements.end(); iter++) { + FormDataElement element = *iter; + + switch (element.m_type) { + case FormDataElement::data: + if (!element.m_data.isEmpty()) { + // WebKit sometimes gives up empty data to append. These aren't + // necessary so we just optimize those out here. + base::Thread* thread = ioThread(); + if (thread) { + Vector<char>* data = new Vector<char>(element.m_data); + thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::appendBytesToUpload, data)); + } + } + break; + case FormDataElement::encodedFile: + { + // Chromium check if it is a directory by checking + // element.m_fileLength, that doesn't work in Android + std::string filename = element.m_filename.utf8().data(); + if (filename.size()) { + // Change from a url string to a filename + if (filename.find("file://") == 0) // Found at pos 0 + filename.erase(0, 7); + base::Thread* thread = ioThread(); + if (thread) + thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::appendFileToUpload, filename)); + } + } + break; +#if ENABLE(BLOB) + case FormDataElement::encodedBlob: + LOG_ASSERT(false, "Unexpected use of FormDataElement::encodedBlob"); + break; +#endif // ENABLE(BLOB) + default: + LOG_ASSERT(false, "Unexpected default case in WebUrlLoaderClient.cpp"); + break; + } + } + } +} + +bool WebUrlLoaderClient::start(bool isMainResource, bool isMainFrame, bool sync, WebRequestContext* context) +{ + base::Thread* thread = ioThread(); + if (!thread) { + return false; + } + + m_isMainResource = isMainResource; + m_isMainFrame = isMainFrame; + m_sync = sync; + if (m_sync) { + AutoLock autoLock(*syncLock()); + m_request->setSync(sync); + m_request->setRequestContext(context); + thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::start)); + + // Run callbacks until the queue is exhausted and m_finished is true. + // Sometimes, a sync load can wait forever and lock up the WebCore thread, + // here we use TimedWait() with multiple tries to avoid locking. + const int kMaxNumTimeout = 3; + const int kCallbackWaitingTime = 10; + int num_timeout = 0; + while(!m_finished) { + while (!m_queue.empty()) { + OwnPtr<Task> task(m_queue.front()); + m_queue.pop_front(); + task->Run(); + } + if (m_finished) break; + + syncCondition()->TimedWait(base::TimeDelta::FromSeconds(kCallbackWaitingTime)); + if (m_queue.empty()) { + LOGE("Synchronous request timed out after %d seconds for the %dth try, URL: %s", + kCallbackWaitingTime, num_timeout, m_request->getUrl().c_str()); + num_timeout++; + if (num_timeout >= kMaxNumTimeout) { + cancel(); + m_resourceHandle = 0; + return false; + } + } + } + + // This may be the last reference to us, so we may be deleted now. + // Don't access any more member variables after releasing this reference. + m_resourceHandle = 0; + } else { + // Asynchronous start. + // Important to set this before the thread starts so it has a reference and can't be deleted + // before the task starts running on the IO thread. + m_request->setRequestContext(context); + thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::start)); + } + return true; +} + +namespace { +// Check if the mime type is for certificate installation. +// The items must be consistent with the sCertificateTypeMap +// in frameworks/base/core/java/android/webkit/CertTool.java. +bool isMimeTypeForCert(const std::string& mimeType) +{ + static std::hash_set<std::string> sCertificateTypeSet; + if (sCertificateTypeSet.empty()) { + sCertificateTypeSet.insert("application/x-x509-ca-cert"); + sCertificateTypeSet.insert("application/x-x509-user-cert"); + sCertificateTypeSet.insert("application/x-pkcs12"); + } + return sCertificateTypeSet.find(mimeType) != sCertificateTypeSet.end(); +} +} + +void WebUrlLoaderClient::downloadFile() +{ + if (m_response) { + std::string contentDisposition; + m_response->getHeader("content-disposition", &contentDisposition); + m_webFrame->downloadStart(m_response->getUrl(), m_request->getUserAgent(), contentDisposition, m_response->getMimeType(), m_response->getExpectedSize()); + + m_isCertMimeType = isMimeTypeForCert(m_response->getMimeType()); + // Currently, only certificate mime type needs to receive the data. + // Other mime type, e.g. wav, will send the url to other application + // which will load the data by url. + if (!m_isCertMimeType) + cancel(); + } else { + LOGE("Unexpected call to downloadFile() before didReceiveResponse(). URL: %s", m_request->getUrl().c_str()); + // TODO: Turn off asserts crashing before release + // http://b/issue?id=2951985 + CRASH(); + } +} + +void WebUrlLoaderClient::cancel() +{ + if (!isActive()) + return; + + m_cancelling = true; + + base::Thread* thread = ioThread(); + if (thread) + thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::cancel)); +} + +void WebUrlLoaderClient::pauseLoad(bool pause) +{ + if (!isActive()) + return; + + base::Thread* thread = ioThread(); + if (thread) + thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::pauseLoad, pause)); +} + +void WebUrlLoaderClient::setAuth(const std::string& username, const std::string& password) +{ + if (!isActive()) + return; + + base::Thread* thread = ioThread(); + if (!thread) { + return; + } + string16 username16 = ASCIIToUTF16(username); + string16 password16 = ASCIIToUTF16(password); + thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::setAuth, username16, password16)); +} + +void WebUrlLoaderClient::cancelAuth() +{ + if (!isActive()) + return; + + base::Thread* thread = ioThread(); + if (!thread) { + return; + } + thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::cancelAuth)); +} + +void WebUrlLoaderClient::proceedSslCertError() +{ + base::Thread* thread = ioThread(); + if (isActive() && thread) + thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::proceedSslCertError)); + this->Release(); +} + +void WebUrlLoaderClient::cancelSslCertError(int cert_error) +{ + base::Thread* thread = ioThread(); + if (isActive() && thread) + thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::cancelSslCertError, cert_error)); + this->Release(); +} + + +void WebUrlLoaderClient::finish() +{ + m_finished = true; + if (!m_sync) { + // This is the last reference to us, so we will be deleted now. + // We only release the reference here if start() was called asynchronously! + m_resourceHandle = 0; + } + m_request = 0; +} + +namespace { +// Trampoline to wrap a Chromium Task* in a WebKit-style static function + void*. +static void RunTask(void* v) { + OwnPtr<Task> task(static_cast<Task*>(v)); + task->Run(); +} +} + +// This is called from the IO thread, and dispatches the callback to the main thread. +void WebUrlLoaderClient::maybeCallOnMainThread(Task* task) +{ + if (m_sync) { + AutoLock autoLock(*syncLock()); + if (m_queue.empty()) { + syncCondition()->Broadcast(); + } + m_queue.push_back(task); + } else { + // Let WebKit handle it. + callOnMainThread(RunTask, task); + } +} + +// Response methods +void WebUrlLoaderClient::didReceiveResponse(PassOwnPtr<WebResponse> webResponse) +{ + if (!isActive()) + return; + + m_response = webResponse; + m_resourceHandle->client()->didReceiveResponse(m_resourceHandle.get(), m_response->createResourceResponse()); + + // Set the main page's certificate to WebView. + if (m_isMainResource && m_isMainFrame) { + const net::SSLInfo& ssl_info = m_response->getSslInfo(); + if (ssl_info.is_valid()) { + std::vector<std::string> chain_bytes; + ssl_info.cert->GetChainDEREncodedBytes(&chain_bytes); + m_webFrame->setCertificate(chain_bytes[0]); + } + + // Look for X-Auto-Login on the main resource to log in the user. + std::string login; + if (m_response->getHeader("x-auto-login", &login)) + m_webFrame->autoLogin(login); + } +} + +void WebUrlLoaderClient::didReceiveData(scoped_refptr<net::IOBuffer> buf, int size) +{ + if (m_isMainResource && m_isCertMimeType) { + m_webFrame->didReceiveData(buf->data(), size); + } + + if (!isActive() || !size) + return; + + // didReceiveData will take a copy of the data + if (m_resourceHandle && m_resourceHandle->client()) + m_resourceHandle->client()->didReceiveData(m_resourceHandle.get(), buf->data(), size, size); +} + +// For data url's +void WebUrlLoaderClient::didReceiveDataUrl(PassOwnPtr<std::string> str) +{ + if (!isActive() || !str->size()) + return; + + // didReceiveData will take a copy of the data + m_resourceHandle->client()->didReceiveData(m_resourceHandle.get(), str->data(), str->size(), str->size()); +} + +// For special android files +void WebUrlLoaderClient::didReceiveAndroidFileData(PassOwnPtr<std::vector<char> > vector) +{ + if (!isActive() || !vector->size()) + return; + + // didReceiveData will take a copy of the data + m_resourceHandle->client()->didReceiveData(m_resourceHandle.get(), vector->begin(), vector->size(), vector->size()); +} + +void WebUrlLoaderClient::didFail(PassOwnPtr<WebResponse> webResponse) +{ + if (isActive()) + m_resourceHandle->client()->didFail(m_resourceHandle.get(), webResponse->createResourceError()); + + // Always finish a request, if not it will leak + finish(); +} + +void WebUrlLoaderClient::willSendRequest(PassOwnPtr<WebResponse> webResponse) +{ + if (!isActive()) + return; + + KURL url = webResponse->createKurl(); + OwnPtr<WebCore::ResourceRequest> resourceRequest(new WebCore::ResourceRequest(url)); + m_resourceHandle->client()->willSendRequest(m_resourceHandle.get(), *resourceRequest, webResponse->createResourceResponse()); + + // WebKit may have killed the request. + if (!isActive()) + return; + + // Like Chrome, we only follow the redirect if WebKit left the URL unmodified. + if (url == resourceRequest->url()) { + ioThread()->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::followDeferredRedirect)); + } else { + cancel(); + } +} + +void WebUrlLoaderClient::didFinishLoading() +{ + if (isActive()) + m_resourceHandle->client()->didFinishLoading(m_resourceHandle.get(), 0); + + if (m_isMainResource && m_isCertMimeType) { + m_webFrame->didFinishLoading(); + } + + // Always finish a request, if not it will leak + finish(); +} + +void WebUrlLoaderClient::authRequired(scoped_refptr<net::AuthChallengeInfo> authChallengeInfo, bool firstTime) +{ + if (!isActive()) + return; + + std::string host = base::SysWideToUTF8(authChallengeInfo->host_and_port); + std::string realm = base::SysWideToUTF8(authChallengeInfo->realm); + + m_webFrame->didReceiveAuthenticationChallenge(this, host, realm, firstTime); +} + +void WebUrlLoaderClient::reportSslCertError(int cert_error, net::X509Certificate* cert) +{ + if (!isActive()) + return; + + std::vector<std::string> chain_bytes; + cert->GetChainDEREncodedBytes(&chain_bytes); + this->AddRef(); + m_webFrame->reportSslCertError(this, cert_error, chain_bytes[0]); +} + +} // namespace android |