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