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 43using base::Lock; 44using base::AutoLock; 45 46namespace android { 47 48base::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 76base::Lock* WebUrlLoaderClient::syncLock() { 77 static Lock s_syncLock; 78 return &s_syncLock; 79} 80 81base::ConditionVariable* WebUrlLoaderClient::syncCondition() { 82 static base::ConditionVariable s_syncCondition(syncLock()); 83 return &s_syncCondition; 84} 85 86WebUrlLoaderClient::~WebUrlLoaderClient() 87{ 88} 89 90bool 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 104WebUrlLoaderClient::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 171bool 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 227namespace { 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. 231bool 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 243void 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 264void 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 276void 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 286void 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 300void 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 312void 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 320void 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 328void 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 339namespace { 340// Trampoline to wrap a Chromium Task* in a WebKit-style static function + void*. 341static 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. 348void 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 363void 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 387void 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 402void 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 412void 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 421void 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 430void 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 451void 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 464void 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 475void 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 486void 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 495void 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