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