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