1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome_frame/urlmon_url_request.h" 6 7 #include <urlmon.h> 8 #include <wininet.h> 9 10 #include "base/bind.h" 11 #include "base/bind_helpers.h" 12 #include "base/logging.h" 13 #include "base/memory/scoped_ptr.h" 14 #include "base/message_loop/message_loop.h" 15 #include "base/strings/string_number_conversions.h" 16 #include "base/strings/stringprintf.h" 17 #include "base/strings/utf_string_conversions.h" 18 #include "base/threading/platform_thread.h" 19 #include "base/threading/thread.h" 20 #include "chrome/common/automation_messages.h" 21 #include "chrome_frame/bind_context_info.h" 22 #include "chrome_frame/chrome_frame_activex_base.h" 23 #include "chrome_frame/extra_system_apis.h" 24 #include "chrome_frame/html_utils.h" 25 #include "chrome_frame/urlmon_upload_data_stream.h" 26 #include "chrome_frame/urlmon_url_request_private.h" 27 #include "chrome_frame/utils.h" 28 #include "net/base/load_flags.h" 29 #include "net/http/http_response_headers.h" 30 #include "net/http/http_util.h" 31 32 #define IS_HTTP_SUCCESS_CODE(code) (code >= 200 && code <= 299) 33 34 UrlmonUrlRequest::UrlmonUrlRequest() 35 : pending_read_size_(0), 36 headers_received_(false), 37 calling_delegate_(0), 38 thread_(NULL), 39 parent_window_(NULL), 40 privileged_mode_(false), 41 pending_(false), 42 is_expecting_download_(true), 43 cleanup_transaction_(false) { 44 DVLOG(1) << __FUNCTION__ << me(); 45 } 46 47 UrlmonUrlRequest::~UrlmonUrlRequest() { 48 DVLOG(1) << __FUNCTION__ << me(); 49 } 50 51 std::string UrlmonUrlRequest::me() const { 52 return base::StringPrintf(" id: %i Obj: %X ", id(), this); 53 } 54 55 bool UrlmonUrlRequest::Start() { 56 DVLOG(1) << __FUNCTION__ << me() << url(); 57 DCHECK(thread_ == 0 || thread_ == base::PlatformThread::CurrentId()); 58 thread_ = base::PlatformThread::CurrentId(); 59 status_.Start(); 60 // Initialize the net::HostPortPair structure from the url initially. We may 61 // not receive the ip address of the host if the request is satisfied from 62 // the cache. 63 socket_address_ = net::HostPortPair::FromURL(GURL(url())); 64 // The UrlmonUrlRequest instance can get destroyed in the context of 65 // StartAsyncDownload if BindToStorage finishes synchronously with an error. 66 // Grab a reference to protect against this. 67 scoped_refptr<UrlmonUrlRequest> ref(this); 68 HRESULT hr = StartAsyncDownload(); 69 if (FAILED(hr) && status_.get_state() != UrlmonUrlRequest::Status::DONE) { 70 status_.Done(); 71 status_.set_result(net::URLRequestStatus::FAILED, HresultToNetError(hr)); 72 NotifyDelegateAndDie(); 73 } 74 return true; 75 } 76 77 void UrlmonUrlRequest::Stop() { 78 DCHECK_EQ(thread_, base::PlatformThread::CurrentId()); 79 DCHECK((status_.get_state() != Status::DONE) == (binding_ != NULL)); 80 Status::State state = status_.get_state(); 81 delegate_ = NULL; 82 83 // If DownloadInHost is already requested, we will quit soon anyway. 84 if (terminate_requested()) 85 return; 86 87 switch (state) { 88 case Status::WORKING: 89 status_.Cancel(); 90 if (binding_) 91 binding_->Abort(); 92 break; 93 94 case Status::ABORTING: 95 status_.Cancel(); 96 break; 97 98 case Status::DONE: 99 status_.Cancel(); 100 NotifyDelegateAndDie(); 101 break; 102 } 103 } 104 105 bool UrlmonUrlRequest::Read(int bytes_to_read) { 106 DCHECK_EQ(thread_, base::PlatformThread::CurrentId()); 107 DCHECK_GE(bytes_to_read, 0); 108 DCHECK_EQ(0, calling_delegate_); 109 DVLOG(1) << __FUNCTION__ << me(); 110 111 is_expecting_download_ = false; 112 113 // Re-entrancy check. Thou shall not call Read() while process OnReadComplete! 114 DCHECK_EQ(0u, pending_read_size_); 115 if (pending_read_size_ != 0) 116 return false; 117 118 DCHECK((status_.get_state() != Status::DONE) == (binding_ != NULL)); 119 if (status_.get_state() == Status::ABORTING) 120 return true; 121 122 // Send data if available. 123 size_t bytes_copied = 0; 124 if ((bytes_copied = SendDataToDelegate(bytes_to_read))) { 125 DVLOG(1) << __FUNCTION__ << me() << " bytes read: " << bytes_copied; 126 return true; 127 } 128 129 if (status_.get_state() == Status::WORKING) { 130 DVLOG(1) << __FUNCTION__ << me() << " pending: " << bytes_to_read; 131 pending_read_size_ = bytes_to_read; 132 } else { 133 DVLOG(1) << __FUNCTION__ << me() << " Response finished."; 134 NotifyDelegateAndDie(); 135 } 136 137 return true; 138 } 139 140 HRESULT UrlmonUrlRequest::InitPending(const GURL& url, IMoniker* moniker, 141 IBindCtx* bind_context, 142 bool enable_frame_busting, 143 bool privileged_mode, 144 HWND notification_window, 145 IStream* cache) { 146 DVLOG(1) << __FUNCTION__ << me() << url.spec(); 147 DCHECK(bind_context_ == NULL); 148 DCHECK(moniker_ == NULL); 149 DCHECK(cache_ == NULL); 150 DCHECK(thread_ == 0 || thread_ == base::PlatformThread::CurrentId()); 151 thread_ = base::PlatformThread::CurrentId(); 152 bind_context_ = bind_context; 153 moniker_ = moniker; 154 enable_frame_busting_ = enable_frame_busting; 155 privileged_mode_ = privileged_mode; 156 parent_window_ = notification_window; 157 cache_ = cache; 158 set_url(url.spec()); 159 set_pending(true); 160 161 // Request has already started and data is fetched. We will get the 162 // GetBindInfo call as per contract but the return values are 163 // ignored. So just set "get" as a method to make our GetBindInfo 164 // implementation happy. 165 method_ = "get"; 166 return S_OK; 167 } 168 169 void UrlmonUrlRequest::TerminateBind(const TerminateBindCallback& callback) { 170 DCHECK_EQ(thread_, base::PlatformThread::CurrentId()); 171 DVLOG(1) << __FUNCTION__ << me(); 172 cleanup_transaction_ = false; 173 if (status_.get_state() == Status::DONE) { 174 // Binding is stopped. Note result could be an error. 175 callback.Run(moniker_, bind_context_, upload_data_, 176 request_headers_.c_str()); 177 } else { 178 // WORKING (ABORTING?). Save the callback. 179 // Now we will return INET_TERMINATE_BIND from ::OnDataAvailable() and in 180 // ::OnStopBinding will invoke the callback passing our moniker and 181 // bind context. 182 terminate_bind_callback_ = callback; 183 if (pending_data_) { 184 // For downloads to work correctly, we must induce a call to 185 // OnDataAvailable so that we can download INET_E_TERMINATED_BIND and 186 // get IE into the correct state. 187 // To accomplish this we read everything that's readily available in 188 // the current stream. Once we've reached the end of the stream we 189 // should get E_PENDING back and then later we'll get that call 190 // to OnDataAvailable. 191 std::string data; 192 base::win::ScopedComPtr<IStream> read_stream(pending_data_); 193 HRESULT hr; 194 while ((hr = ReadStream(read_stream, 0xffff, &data)) == S_OK) { 195 // Just drop the data. 196 } 197 DLOG_IF(WARNING, hr != E_PENDING) << __FUNCTION__ << 198 base::StringPrintf(" expected E_PENDING but got 0x%08X", hr); 199 } 200 } 201 } 202 203 size_t UrlmonUrlRequest::SendDataToDelegate(size_t bytes_to_read) { 204 DCHECK_EQ(thread_, base::PlatformThread::CurrentId()); 205 DCHECK_NE(id(), -1); 206 DCHECK_GT(bytes_to_read, 0U); 207 size_t bytes_copied = 0; 208 if (delegate_) { 209 std::string read_data; 210 if (cache_) { 211 HRESULT hr = ReadStream(cache_, bytes_to_read, &read_data); 212 if (hr == S_FALSE || read_data.length() < bytes_to_read) { 213 DVLOG(1) << __FUNCTION__ << me() << "all cached data read"; 214 cache_.Release(); 215 } 216 } 217 218 if (read_data.empty() && pending_data_) { 219 size_t pending_data_read_save = pending_read_size_; 220 pending_read_size_ = 0; 221 222 // AddRef the stream while we call Read to avoid a potential issue 223 // where we can get a call to OnDataAvailable while inside Read and 224 // in our OnDataAvailable call, we can release the stream object 225 // while still using it. 226 base::win::ScopedComPtr<IStream> pending(pending_data_); 227 HRESULT hr = ReadStream(pending, bytes_to_read, &read_data); 228 if (read_data.empty()) 229 pending_read_size_ = pending_data_read_save; 230 // If we received S_FALSE it indicates that there is no more data in the 231 // stream. Clear it to ensure that OnStopBinding correctly sends over the 232 // response end notification to chrome. 233 if (hr == S_FALSE) 234 pending_data_.Release(); 235 } 236 237 bytes_copied = read_data.length(); 238 239 if (bytes_copied) { 240 ++calling_delegate_; 241 DCHECK_NE(id(), -1); 242 // The delegate can go away in the middle of ReadStream 243 if (delegate_) 244 delegate_->OnReadComplete(id(), read_data); 245 --calling_delegate_; 246 } 247 } else { 248 DLOG(ERROR) << __FUNCTION__ << me() << "no delegate"; 249 } 250 251 return bytes_copied; 252 } 253 254 STDMETHODIMP UrlmonUrlRequest::OnStartBinding(DWORD reserved, 255 IBinding* binding) { 256 DCHECK_EQ(thread_, base::PlatformThread::CurrentId()); 257 binding_ = binding; 258 if (pending_) { 259 response_headers_ = GetHttpHeadersFromBinding(binding_); 260 DCHECK(!response_headers_.empty()); 261 } 262 return S_OK; 263 } 264 265 STDMETHODIMP UrlmonUrlRequest::GetPriority(LONG *priority) { 266 if (!priority) 267 return E_POINTER; 268 *priority = THREAD_PRIORITY_NORMAL; 269 return S_OK; 270 } 271 272 STDMETHODIMP UrlmonUrlRequest::OnLowResource(DWORD reserved) { 273 return S_OK; 274 } 275 276 STDMETHODIMP UrlmonUrlRequest::OnProgress(ULONG progress, ULONG max_progress, 277 ULONG status_code, LPCWSTR status_text) { 278 DCHECK_EQ(thread_, base::PlatformThread::CurrentId()); 279 280 if (status_.get_state() != Status::WORKING) 281 return S_OK; 282 283 // Ignore any notifications received while we are in the pending state 284 // waiting for the request to be initiated by Chrome. 285 if (pending_ && status_code != BINDSTATUS_REDIRECTING) 286 return S_OK; 287 288 if (!delegate_) { 289 DVLOG(1) << "Invalid delegate"; 290 return S_OK; 291 } 292 293 switch (status_code) { 294 case BINDSTATUS_CONNECTING: { 295 if (status_text) { 296 socket_address_.set_host(WideToUTF8(status_text)); 297 } 298 break; 299 } 300 301 case BINDSTATUS_REDIRECTING: { 302 // If we receive a redirect for the initial pending request initiated 303 // when our document loads we should stash it away and inform Chrome 304 // accordingly when it requests data for the original URL. 305 base::win::ScopedComPtr<BindContextInfo> info; 306 BindContextInfo::FromBindContext(bind_context_, info.Receive()); 307 DCHECK(info); 308 GURL previously_redirected(info ? info->GetUrl() : std::wstring()); 309 if (GURL(status_text) != previously_redirected) { 310 DVLOG(1) << __FUNCTION__ << me() << "redirect from " << url() 311 << " to " << status_text; 312 // Fetch the redirect status as they aren't all equal (307 in particular 313 // retains the HTTP request verb). 314 int http_code = GetHttpResponseStatusFromBinding(binding_); 315 status_.SetRedirected(http_code, WideToUTF8(status_text)); 316 // Abort. We will inform Chrome in OnStopBinding callback. 317 binding_->Abort(); 318 return E_ABORT; 319 } 320 break; 321 } 322 323 case BINDSTATUS_COOKIE_SENT: 324 delegate_->AddPrivacyDataForUrl(url(), "", COOKIEACTION_READ); 325 break; 326 327 case BINDSTATUS_COOKIE_SUPPRESSED: 328 delegate_->AddPrivacyDataForUrl(url(), "", COOKIEACTION_SUPPRESS); 329 break; 330 331 case BINDSTATUS_COOKIE_STATE_ACCEPT: 332 delegate_->AddPrivacyDataForUrl(url(), "", COOKIEACTION_ACCEPT); 333 break; 334 335 case BINDSTATUS_COOKIE_STATE_REJECT: 336 delegate_->AddPrivacyDataForUrl(url(), "", COOKIEACTION_REJECT); 337 break; 338 339 case BINDSTATUS_COOKIE_STATE_LEASH: 340 delegate_->AddPrivacyDataForUrl(url(), "", COOKIEACTION_LEASH); 341 break; 342 343 case BINDSTATUS_COOKIE_STATE_DOWNGRADE: 344 delegate_->AddPrivacyDataForUrl(url(), "", COOKIEACTION_DOWNGRADE); 345 break; 346 347 case BINDSTATUS_COOKIE_STATE_UNKNOWN: 348 NOTREACHED() << L"Unknown cookie state received"; 349 break; 350 351 default: 352 DVLOG(1) << __FUNCTION__ << me() 353 << base::StringPrintf(L"code: %i status: %ls", status_code, 354 status_text); 355 break; 356 } 357 358 return S_OK; 359 } 360 361 STDMETHODIMP UrlmonUrlRequest::OnStopBinding(HRESULT result, LPCWSTR error) { 362 DCHECK_EQ(thread_, base::PlatformThread::CurrentId()); 363 DVLOG(1) << __FUNCTION__ << me() 364 << "- Request stopped, Result: " << std::hex << result; 365 DCHECK(status_.get_state() == Status::WORKING || 366 status_.get_state() == Status::ABORTING); 367 368 Status::State state = status_.get_state(); 369 370 // Mark we a are done. 371 status_.Done(); 372 373 if (result == INET_E_TERMINATED_BIND) { 374 if (terminate_requested()) { 375 terminate_bind_callback_.Run(moniker_, bind_context_, upload_data_, 376 request_headers_.c_str()); 377 } else { 378 cleanup_transaction_ = true; 379 } 380 // We may have returned INET_E_TERMINATED_BIND from OnDataAvailable. 381 result = S_OK; 382 } 383 384 if (state == Status::WORKING) { 385 status_.set_result(result); 386 387 if (FAILED(result)) { 388 int http_code = GetHttpResponseStatusFromBinding(binding_); 389 // For certain requests like empty POST requests the server can return 390 // back a HTTP success code in the range 200 to 299. We need to flag 391 // these requests as succeeded. 392 if (IS_HTTP_SUCCESS_CODE(http_code)) { 393 // If this DCHECK fires it means that the server returned a HTTP 394 // success code outside the standard range 200-206. We need to confirm 395 // if the following code path is correct. 396 DCHECK_LE(http_code, 206); 397 status_.set_result(S_OK); 398 std::string headers = GetHttpHeadersFromBinding(binding_); 399 OnResponse(0, UTF8ToWide(headers).c_str(), NULL, NULL); 400 } else if (net::HttpResponseHeaders::IsRedirectResponseCode(http_code) && 401 result == E_ACCESSDENIED) { 402 // Special case. If the last request was a redirect and the current OS 403 // error value is E_ACCESSDENIED, that means an unsafe redirect was 404 // attempted. In that case, correct the OS error value to be the more 405 // specific ERR_UNSAFE_REDIRECT error value. 406 status_.set_result(net::URLRequestStatus::FAILED, 407 net::ERR_UNSAFE_REDIRECT); 408 } 409 } 410 411 // The code below seems easy but it is not. :) 412 // The network policy in Chrome network is that error code/end_of_stream 413 // should be returned only as a result of read (or start) request. 414 // Here are the possible cases: 415 // pending_data_|pending_read 416 // FALSE |FALSE => EndRequest if no headers, otherwise wait for Read. 417 // FALSE |TRUE => EndRequest. 418 // TRUE |FALSE => Wait for Read. 419 // TRUE |TRUE => Something went wrong!! 420 421 if (pending_data_) { 422 DCHECK_EQ(pending_read_size_, 0UL); 423 ReleaseBindings(); 424 return S_OK; 425 } 426 427 if (headers_received_ && pending_read_size_ == 0) { 428 ReleaseBindings(); 429 return S_OK; 430 } 431 432 // No headers or there is a pending read from Chrome. 433 NotifyDelegateAndDie(); 434 return S_OK; 435 } 436 437 // Status::ABORTING 438 if (status_.was_redirected()) { 439 // Just release bindings here. Chrome will issue EndRequest(request_id) 440 // after processing headers we had provided. 441 if (!pending_) { 442 std::string headers = GetHttpHeadersFromBinding(binding_); 443 OnResponse(0, UTF8ToWide(headers).c_str(), NULL, NULL); 444 } 445 ReleaseBindings(); 446 return S_OK; 447 } 448 449 // Stop invoked. 450 NotifyDelegateAndDie(); 451 return S_OK; 452 } 453 454 STDMETHODIMP UrlmonUrlRequest::GetBindInfo(DWORD* bind_flags, 455 BINDINFO* bind_info) { 456 if ((bind_info == NULL) || (bind_info->cbSize == 0) || (bind_flags == NULL)) 457 return E_INVALIDARG; 458 459 *bind_flags = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA; 460 461 bind_info->dwOptionsFlags = INTERNET_FLAG_NO_AUTO_REDIRECT; 462 bind_info->dwOptions = BINDINFO_OPTIONS_WININETFLAG; 463 464 // TODO(ananta) 465 // Look into whether the other load flags need to be supported in chrome 466 // frame. 467 if (load_flags_ & net::LOAD_VALIDATE_CACHE) 468 *bind_flags |= BINDF_RESYNCHRONIZE; 469 470 if (load_flags_ & net::LOAD_BYPASS_CACHE) 471 *bind_flags |= BINDF_GETNEWESTVERSION; 472 473 if (LowerCaseEqualsASCII(method(), "get")) { 474 bind_info->dwBindVerb = BINDVERB_GET; 475 } else if (LowerCaseEqualsASCII(method(), "post")) { 476 bind_info->dwBindVerb = BINDVERB_POST; 477 } else if (LowerCaseEqualsASCII(method(), "put")) { 478 bind_info->dwBindVerb = BINDVERB_PUT; 479 } else { 480 std::wstring verb(ASCIIToWide(StringToUpperASCII(method()))); 481 bind_info->dwBindVerb = BINDVERB_CUSTOM; 482 bind_info->szCustomVerb = reinterpret_cast<wchar_t*>( 483 ::CoTaskMemAlloc((verb.length() + 1) * sizeof(wchar_t))); 484 lstrcpyW(bind_info->szCustomVerb, verb.c_str()); 485 } 486 487 if (bind_info->dwBindVerb == BINDVERB_POST || 488 bind_info->dwBindVerb == BINDVERB_PUT || 489 post_data_len() > 0) { 490 // Bypass caching proxies on upload requests and avoid writing responses to 491 // the browser's cache. 492 *bind_flags |= BINDF_GETNEWESTVERSION | BINDF_PRAGMA_NO_CACHE; 493 494 // Attempt to avoid storing the response for upload requests. 495 // See http://crbug.com/55918 496 if (resource_type_ != ResourceType::MAIN_FRAME) 497 *bind_flags |= BINDF_NOWRITECACHE; 498 499 // Initialize the STGMEDIUM. 500 memset(&bind_info->stgmedData, 0, sizeof(STGMEDIUM)); 501 bind_info->grfBindInfoF = 0; 502 503 if (bind_info->dwBindVerb != BINDVERB_CUSTOM) 504 bind_info->szCustomVerb = NULL; 505 506 if ((post_data_len() || is_chunked_upload()) && 507 get_upload_data(&bind_info->stgmedData.pstm) == S_OK) { 508 bind_info->stgmedData.tymed = TYMED_ISTREAM; 509 if (!is_chunked_upload()) { 510 bind_info->cbstgmedData = static_cast<DWORD>(post_data_len()); 511 } 512 DVLOG(1) << __FUNCTION__ << me() << method() 513 << " request with " << base::Int64ToString(post_data_len()) 514 << " bytes. url=" << url(); 515 } else { 516 DVLOG(1) << __FUNCTION__ << me() << "POST request with no data!"; 517 } 518 } 519 return S_OK; 520 } 521 522 STDMETHODIMP UrlmonUrlRequest::OnDataAvailable(DWORD flags, DWORD size, 523 FORMATETC* formatetc, 524 STGMEDIUM* storage) { 525 DCHECK_EQ(thread_, base::PlatformThread::CurrentId()); 526 DVLOG(1) << __FUNCTION__ << me() << "bytes available: " << size; 527 528 if (terminate_requested()) { 529 DVLOG(1) << " Download requested. INET_E_TERMINATED_BIND returned"; 530 return INET_E_TERMINATED_BIND; 531 } 532 533 if (!storage || (storage->tymed != TYMED_ISTREAM)) { 534 NOTREACHED(); 535 return E_INVALIDARG; 536 } 537 538 IStream* read_stream = storage->pstm; 539 if (!read_stream) { 540 NOTREACHED(); 541 return E_UNEXPECTED; 542 } 543 544 // Some requests such as HEAD have zero data. 545 if (size > 0) 546 pending_data_ = read_stream; 547 548 if (pending_read_size_) { 549 size_t bytes_copied = SendDataToDelegate(pending_read_size_); 550 DVLOG(1) << __FUNCTION__ << me() << "size read: " << bytes_copied; 551 } else { 552 DVLOG(1) << __FUNCTION__ << me() << "- waiting for remote read"; 553 } 554 555 if (BSCF_LASTDATANOTIFICATION & flags) { 556 if (!is_expecting_download_ || pending()) { 557 DVLOG(1) << __FUNCTION__ << me() << "EOF"; 558 return S_OK; 559 } 560 // Always return INET_E_TERMINATED_BIND to allow bind context reuse 561 // if DownloadToHost is suddenly requested. 562 DVLOG(1) << __FUNCTION__ << " EOF: INET_E_TERMINATED_BIND returned"; 563 return INET_E_TERMINATED_BIND; 564 } 565 return S_OK; 566 } 567 568 STDMETHODIMP UrlmonUrlRequest::OnObjectAvailable(REFIID iid, IUnknown* object) { 569 // We are calling BindToStorage on the moniker we should always get called 570 // back on OnDataAvailable and should never get OnObjectAvailable 571 NOTREACHED(); 572 return E_NOTIMPL; 573 } 574 575 STDMETHODIMP UrlmonUrlRequest::BeginningTransaction(const wchar_t* url, 576 const wchar_t* current_headers, DWORD reserved, 577 wchar_t** additional_headers) { 578 DCHECK_EQ(thread_, base::PlatformThread::CurrentId()); 579 if (!additional_headers) { 580 NOTREACHED(); 581 return E_POINTER; 582 } 583 584 DVLOG(1) << __FUNCTION__ << me() << "headers: \n" << current_headers; 585 586 if (status_.get_state() == Status::ABORTING) { 587 // At times the BINDSTATUS_REDIRECTING notification which is sent to the 588 // IBindStatusCallback interface does not have an accompanying HTTP 589 // redirect status code, i.e. the attempt to query the HTTP status code 590 // from the binding returns 0, 200, etc which are invalid redirect codes. 591 // We don't want urlmon to follow redirects. We return E_ABORT in our 592 // IBindStatusCallback::OnProgress function and also abort the binding. 593 // However urlmon still tries to establish a transaction with the 594 // redirected URL which confuses the web server. 595 // Fix is to abort the attempted transaction. 596 DLOG(WARNING) << __FUNCTION__ << me() 597 << ": Aborting connection to URL:" 598 << url 599 << " as the binding has been aborted"; 600 return E_ABORT; 601 } 602 603 HRESULT hr = S_OK; 604 605 std::string new_headers; 606 if (is_chunked_upload()) { 607 new_headers = base::StringPrintf("Transfer-Encoding: chunked\r\n"); 608 } 609 610 if (!extra_headers().empty()) { 611 // TODO(robertshield): We may need to sanitize headers on POST here. 612 new_headers += extra_headers(); 613 } 614 615 if (!referrer().empty()) { 616 // Referrer is famously misspelled in HTTP: 617 new_headers += base::StringPrintf("Referer: %s\r\n", referrer().c_str()); 618 } 619 620 // In the rare case if "User-Agent" string is already in |current_headers|. 621 // We send Chrome's user agent in requests initiated within ChromeFrame to 622 // enable third party content in pages rendered in ChromeFrame to correctly 623 // send content for Chrome as the third party content may not be equipped to 624 // identify chromeframe as the user agent. This also ensures that the user 625 // agent reported in scripts in chrome frame is consistent with that sent 626 // in outgoing requests. 627 std::string user_agent = http_utils::AddChromeFrameToUserAgentValue( 628 http_utils::GetChromeUserAgent()); 629 new_headers += ReplaceOrAddUserAgent(current_headers, user_agent); 630 631 if (!new_headers.empty()) { 632 *additional_headers = reinterpret_cast<wchar_t*>( 633 CoTaskMemAlloc((new_headers.size() + 1) * sizeof(wchar_t))); 634 635 if (*additional_headers == NULL) { 636 NOTREACHED(); 637 hr = E_OUTOFMEMORY; 638 } else { 639 lstrcpynW(*additional_headers, ASCIIToWide(new_headers).c_str(), 640 new_headers.size()); 641 } 642 } 643 request_headers_ = new_headers; 644 return hr; 645 } 646 647 STDMETHODIMP UrlmonUrlRequest::OnResponse(DWORD dwResponseCode, 648 const wchar_t* response_headers, const wchar_t* request_headers, 649 wchar_t** additional_headers) { 650 DCHECK_EQ(thread_, base::PlatformThread::CurrentId()); 651 DVLOG(1) << __FUNCTION__ << me() << "headers: \n" 652 << (response_headers == NULL ? L"EMPTY" : response_headers); 653 654 if (!delegate_) { 655 DLOG(WARNING) << "Invalid delegate"; 656 return S_OK; 657 } 658 659 delegate_->AddPrivacyDataForUrl(url(), "", 0); 660 661 std::string raw_headers; 662 if (response_headers) 663 raw_headers = WideToUTF8(response_headers); 664 665 // Security check for frame busting headers. We don't honor the headers 666 // as-such, but instead simply kill requests which we've been asked to 667 // look for if they specify a value for "X-Frame-Options" other than 668 // "ALLOWALL" (the others are "deny" and "sameorigin"). This puts the onus 669 // on the user of the UrlRequest to specify whether or not requests should 670 // be inspected. For ActiveDocuments, the answer is "no", since WebKit's 671 // detection/handling is sufficient and since ActiveDocuments cannot be 672 // hosted as iframes. For NPAPI and ActiveX documents, the Initialize() 673 // function of the PluginUrlRequest object allows them to specify how they'd 674 // like requests handled. Both should set enable_frame_busting_ to true to 675 // avoid CSRF attacks. Should WebKit's handling of this ever change, we will 676 // need to re-visit how and when frames are killed to better mirror a policy 677 // which may do something other than kill the sub-document outright. 678 679 // NOTE(slightlyoff): We don't use net::HttpResponseHeaders here because 680 // of lingering ICU/base_noicu issues. 681 if (enable_frame_busting_) { 682 if (http_utils::HasFrameBustingHeader(raw_headers)) { 683 DLOG(ERROR) << "X-Frame-Options header other than ALLOWALL " << 684 "detected, navigation canceled"; 685 return E_FAIL; 686 } 687 } 688 689 DVLOG(1) << __FUNCTION__ << me() << "Calling OnResponseStarted"; 690 691 // Inform the delegate. 692 headers_received_ = true; 693 DCHECK_NE(id(), -1); 694 delegate_->OnResponseStarted(id(), 695 "", // mime_type 696 raw_headers.c_str(), // headers 697 0, // size 698 base::Time(), // last_modified 699 status_.get_redirection().utf8_url, 700 status_.get_redirection().http_code, 701 socket_address_, 702 post_data_len()); 703 return S_OK; 704 } 705 706 STDMETHODIMP UrlmonUrlRequest::GetWindow(const GUID& guid_reason, 707 HWND* parent_window) { 708 if (!parent_window) 709 return E_INVALIDARG; 710 711 #ifndef NDEBUG 712 wchar_t guid[40] = {0}; 713 ::StringFromGUID2(guid_reason, guid, arraysize(guid)); 714 const wchar_t* str = guid; 715 if (guid_reason == IID_IAuthenticate) 716 str = L"IAuthenticate"; 717 else if (guid_reason == IID_IHttpSecurity) 718 str = L"IHttpSecurity"; 719 else if (guid_reason == IID_IWindowForBindingUI) 720 str = L"IWindowForBindingUI"; 721 DVLOG(1) << __FUNCTION__ << me() << "GetWindow: " << str; 722 #endif 723 // We should return a non-NULL HWND as parent. Otherwise no dialog is shown. 724 // TODO(iyengar): This hits when running the URL request tests. 725 DLOG_IF(WARNING, !::IsWindow(parent_window_)) 726 << "UrlmonUrlRequest::GetWindow - no window!"; 727 *parent_window = parent_window_; 728 return S_OK; 729 } 730 731 STDMETHODIMP UrlmonUrlRequest::Authenticate(HWND* parent_window, 732 LPWSTR* user_name, 733 LPWSTR* password) { 734 if (!parent_window) 735 return E_INVALIDARG; 736 737 if (privileged_mode_) 738 return E_ACCESSDENIED; 739 740 DCHECK(::IsWindow(parent_window_)); 741 *parent_window = parent_window_; 742 return S_OK; 743 } 744 745 STDMETHODIMP UrlmonUrlRequest::OnSecurityProblem(DWORD problem) { 746 // Urlmon notifies the client of authentication problems, certificate 747 // errors, etc by querying the object implementing the IBindStatusCallback 748 // interface for the IHttpSecurity interface. If this interface is not 749 // implemented then Urlmon checks for the problem codes defined below 750 // and performs actions as defined below:- 751 // It invokes the ReportProgress method of the protocol sink with 752 // these problem codes and eventually invokes the ReportResult method 753 // on the protocol sink which ends up in a call to the OnStopBinding 754 // method of the IBindStatusCallBack interface. 755 756 // MSHTML's implementation of the IBindStatusCallback interface does not 757 // implement the IHttpSecurity interface. However it handles the 758 // OnStopBinding call with a HRESULT of 0x800c0019 and navigates to 759 // an interstitial page which presents the user with a choice of whether 760 // to abort the navigation. 761 762 // In our OnStopBinding implementation we stop the navigation and inform 763 // Chrome about the result. Ideally Chrome should behave in a manner similar 764 // to IE, i.e. display the SSL error interstitial page and if the user 765 // decides to proceed anyway we would turn off SSL warnings for that 766 // particular navigation and allow IE to download the content. 767 // We would need to return the certificate information to Chrome for display 768 // purposes. Currently we only return a dummy certificate to Chrome. 769 // At this point we decided that it is a lot of work at this point and 770 // decided to go with the easier option of implementing the IHttpSecurity 771 // interface and replicating the checks performed by Urlmon. This 772 // causes Urlmon to display a dialog box on the same lines as IE6. 773 DVLOG(1) << __FUNCTION__ << me() << "Security problem : " << problem; 774 775 // On IE6 the default IBindStatusCallback interface does not implement the 776 // IHttpSecurity interface and thus causes IE to put up a certificate error 777 // dialog box. We need to emulate this behavior for sites with mismatched 778 // certificates to work. 779 if (GetIEVersion() == IE_6) 780 return S_FALSE; 781 782 HRESULT hr = E_ABORT; 783 784 switch (problem) { 785 case ERROR_INTERNET_SEC_CERT_REV_FAILED: { 786 hr = RPC_E_RETRY; 787 break; 788 } 789 790 case ERROR_INTERNET_SEC_CERT_DATE_INVALID: 791 case ERROR_INTERNET_SEC_CERT_CN_INVALID: 792 case ERROR_INTERNET_INVALID_CA: { 793 hr = S_FALSE; 794 break; 795 } 796 797 default: { 798 NOTREACHED() << "Unhandled security problem : " << problem; 799 break; 800 } 801 } 802 return hr; 803 } 804 805 HRESULT UrlmonUrlRequest::StartAsyncDownload() { 806 DVLOG(1) << __FUNCTION__ << me() << url(); 807 HRESULT hr = E_FAIL; 808 DCHECK((moniker_ && bind_context_) || (!moniker_ && !bind_context_)); 809 810 if (!moniker_.get()) { 811 std::wstring wide_url = UTF8ToWide(url()); 812 hr = CreateURLMonikerEx(NULL, wide_url.c_str(), moniker_.Receive(), 813 URL_MK_UNIFORM); 814 if (FAILED(hr)) { 815 NOTREACHED() << "CreateURLMonikerEx failed. Error: " << hr; 816 return hr; 817 } 818 } 819 820 if (bind_context_.get() == NULL) { 821 hr = ::CreateAsyncBindCtxEx(NULL, 0, this, NULL, 822 bind_context_.Receive(), 0); 823 DCHECK(SUCCEEDED(hr)) << "CreateAsyncBindCtxEx failed. Error: " << hr; 824 } else { 825 // Use existing bind context. 826 hr = ::RegisterBindStatusCallback(bind_context_, this, NULL, 0); 827 DCHECK(SUCCEEDED(hr)) << "RegisterBindStatusCallback failed. Error: " << hr; 828 } 829 830 if (SUCCEEDED(hr)) { 831 base::win::ScopedComPtr<IStream> stream; 832 833 // BindToStorage may complete synchronously. 834 // We still get all the callbacks - OnStart/StopBinding, this may result 835 // in destruction of our object. It's fine but we access some members 836 // below for debug info. :) 837 base::win::ScopedComPtr<IHttpSecurity> self(this); 838 839 // Inform our moniker patch this binding should not be tortured. 840 base::win::ScopedComPtr<BindContextInfo> info; 841 BindContextInfo::FromBindContext(bind_context_, info.Receive()); 842 DCHECK(info); 843 if (info) 844 info->set_chrome_request(true); 845 846 hr = moniker_->BindToStorage(bind_context_, NULL, __uuidof(IStream), 847 reinterpret_cast<void**>(stream.Receive())); 848 if (hr == S_OK) 849 DCHECK(binding_ != NULL || status_.get_state() == Status::DONE); 850 851 if (FAILED(hr)) { 852 // TODO(joshia): Look into. This currently fails for: 853 // http://user2:secret@localhost:1337/auth-basic?set-cookie-if-challenged 854 // when running the UrlRequest unit tests. 855 DLOG(ERROR) << __FUNCTION__ << me() << 856 base::StringPrintf("IUrlMoniker::BindToStorage failed 0x%08X.", hr); 857 // In most cases we'll get a MK_E_SYNTAX error here but if we abort 858 // the navigation ourselves such as in the case of seeing something 859 // else than ALLOWALL in X-Frame-Options. 860 } 861 } 862 863 DLOG_IF(ERROR, FAILED(hr)) << me() << 864 base::StringPrintf(L"StartAsyncDownload failed: 0x%08X", hr); 865 866 return hr; 867 } 868 869 void UrlmonUrlRequest::NotifyDelegateAndDie() { 870 DCHECK_EQ(thread_, base::PlatformThread::CurrentId()); 871 DVLOG(1) << __FUNCTION__ << me(); 872 873 PluginUrlRequestDelegate* delegate = delegate_; 874 delegate_ = NULL; 875 ReleaseBindings(); 876 TerminateTransaction(); 877 if (delegate && id() != -1) { 878 net::URLRequestStatus result = status_.get_result(); 879 delegate->OnResponseEnd(id(), result); 880 } else { 881 DLOG(WARNING) << __FUNCTION__ << me() << "no delegate"; 882 } 883 } 884 885 void UrlmonUrlRequest::TerminateTransaction() { 886 if (cleanup_transaction_ && bind_context_ && moniker_) { 887 // We return INET_E_TERMINATED_BIND from our OnDataAvailable implementation 888 // to ensure that the transaction stays around if Chrome decides to issue 889 // a download request when it finishes inspecting the headers received in 890 // OnResponse. However this causes the urlmon transaction object to leak. 891 // To workaround this we save away the IInternetProtocol interface which is 892 // implemented by the urlmon CTransaction object in our BindContextInfo 893 // instance which is maintained per bind context. Invoking Terminate 894 // on this with the special flags 0x2000000 cleanly releases the 895 // transaction. 896 static const int kUrlmonTerminateTransactionFlags = 0x2000000; 897 base::win::ScopedComPtr<BindContextInfo> info; 898 BindContextInfo::FromBindContext(bind_context_, info.Receive()); 899 DCHECK(info); 900 if (info && info->protocol()) { 901 info->protocol()->Terminate(kUrlmonTerminateTransactionFlags); 902 } 903 } 904 bind_context_.Release(); 905 } 906 907 void UrlmonUrlRequest::ReleaseBindings() { 908 binding_.Release(); 909 // Do not release bind_context here! 910 // We may get DownloadToHost request and therefore we want the bind_context 911 // to be available. 912 if (bind_context_) 913 ::RevokeBindStatusCallback(bind_context_, this); 914 } 915 916 net::Error UrlmonUrlRequest::HresultToNetError(HRESULT hr) { 917 const int kInvalidHostName = 0x8007007b; 918 // Useful reference: 919 // http://msdn.microsoft.com/en-us/library/ms775145(VS.85).aspx 920 921 net::Error ret = net::ERR_UNEXPECTED; 922 923 switch (hr) { 924 case S_OK: 925 ret = net::OK; 926 break; 927 928 case MK_E_SYNTAX: 929 ret = net::ERR_INVALID_URL; 930 break; 931 932 case INET_E_CANNOT_CONNECT: 933 ret = net::ERR_CONNECTION_FAILED; 934 break; 935 936 case INET_E_DOWNLOAD_FAILURE: 937 case INET_E_CONNECTION_TIMEOUT: 938 case E_ABORT: 939 ret = net::ERR_CONNECTION_ABORTED; 940 break; 941 942 case INET_E_DATA_NOT_AVAILABLE: 943 ret = net::ERR_EMPTY_RESPONSE; 944 break; 945 946 case INET_E_RESOURCE_NOT_FOUND: 947 // To behave more closely to the chrome network stack, we translate this 948 // error value as tunnel connection failed. This error value is tested 949 // in the ProxyTunnelRedirectTest and UnexpectedServerAuthTest tests. 950 ret = net::ERR_TUNNEL_CONNECTION_FAILED; 951 break; 952 953 // The following error codes can be returned while processing an invalid 954 // url. http://msdn.microsoft.com/en-us/library/bb250493(v=vs.85).aspx 955 case INET_E_INVALID_URL: 956 case INET_E_UNKNOWN_PROTOCOL: 957 case INET_E_REDIRECT_FAILED: 958 case INET_E_SECURITY_PROBLEM: 959 case kInvalidHostName: 960 case E_INVALIDARG: 961 case E_OUTOFMEMORY: 962 ret = net::ERR_INVALID_URL; 963 break; 964 965 case INET_E_INVALID_CERTIFICATE: 966 ret = net::ERR_CERT_INVALID; 967 break; 968 969 case E_ACCESSDENIED: 970 ret = net::ERR_ACCESS_DENIED; 971 break; 972 973 default: 974 DLOG(WARNING) 975 << base::StringPrintf("TODO: translate HRESULT 0x%08X to net::Error", 976 hr); 977 break; 978 } 979 return ret; 980 } 981 982 983 PluginUrlRequestManager::ThreadSafeFlags 984 UrlmonUrlRequestManager::GetThreadSafeFlags() { 985 return PluginUrlRequestManager::NOT_THREADSAFE; 986 } 987 988 void UrlmonUrlRequestManager::SetInfoForUrl(const std::wstring& url, 989 IMoniker* moniker, LPBC bind_ctx) { 990 CComObject<UrlmonUrlRequest>* new_request = NULL; 991 CComObject<UrlmonUrlRequest>::CreateInstance(&new_request); 992 if (new_request) { 993 GURL start_url(url); 994 DCHECK(start_url.is_valid()); 995 DCHECK(pending_request_ == NULL); 996 997 base::win::ScopedComPtr<BindContextInfo> info; 998 BindContextInfo::FromBindContext(bind_ctx, info.Receive()); 999 DCHECK(info); 1000 IStream* cache = info ? info->cache() : NULL; 1001 pending_request_ = new_request; 1002 pending_request_->InitPending(start_url, moniker, bind_ctx, 1003 enable_frame_busting_, privileged_mode_, 1004 notification_window_, cache); 1005 // Start the request 1006 bool is_started = pending_request_->Start(); 1007 DCHECK(is_started); 1008 } 1009 } 1010 1011 void UrlmonUrlRequestManager::StartRequest(int request_id, 1012 const AutomationURLRequest& request_info) { 1013 DVLOG(1) << __FUNCTION__ << " id: " << request_id; 1014 1015 if (stopping_) { 1016 DLOG(WARNING) << __FUNCTION__ << " request not started (stopping)"; 1017 return; 1018 } 1019 1020 DCHECK(request_map_.find(request_id) == request_map_.end()); 1021 #ifndef NDEBUG 1022 if (background_worker_thread_enabled_) { 1023 base::AutoLock lock(background_resource_map_lock_); 1024 DCHECK(background_request_map_.find(request_id) == 1025 background_request_map_.end()); 1026 } 1027 #endif // NDEBUG 1028 DCHECK(GURL(request_info.url).is_valid()); 1029 1030 // Non frame requests like sub resources, images, etc are handled on the 1031 // background thread. 1032 if (background_worker_thread_enabled_ && 1033 !ResourceType::IsFrame( 1034 static_cast<ResourceType::Type>(request_info.resource_type))) { 1035 DLOG(INFO) << "Downloading resource type " 1036 << request_info.resource_type 1037 << " on background thread"; 1038 background_thread_->message_loop()->PostTask( 1039 FROM_HERE, 1040 base::Bind(&UrlmonUrlRequestManager::StartRequestHelper, 1041 base::Unretained(this), request_id, request_info, 1042 &background_request_map_, &background_resource_map_lock_)); 1043 return; 1044 } 1045 StartRequestHelper(request_id, request_info, &request_map_, NULL); 1046 } 1047 1048 void UrlmonUrlRequestManager::StartRequestHelper( 1049 int request_id, 1050 const AutomationURLRequest& request_info, 1051 RequestMap* request_map, 1052 base::Lock* request_map_lock) { 1053 DCHECK(request_map); 1054 scoped_refptr<UrlmonUrlRequest> new_request; 1055 bool is_started = false; 1056 if (pending_request_) { 1057 if (pending_request_->url() != request_info.url) { 1058 DLOG(INFO) << __FUNCTION__ 1059 << "Received url request for url:" 1060 << request_info.url 1061 << ". Stopping pending url request for url:" 1062 << pending_request_->url(); 1063 pending_request_->Stop(); 1064 pending_request_ = NULL; 1065 } else { 1066 new_request.swap(pending_request_); 1067 is_started = true; 1068 DVLOG(1) << __FUNCTION__ << new_request->me() 1069 << " assigned id " << request_id; 1070 } 1071 } 1072 1073 if (!is_started) { 1074 CComObject<UrlmonUrlRequest>* created_request = NULL; 1075 CComObject<UrlmonUrlRequest>::CreateInstance(&created_request); 1076 new_request = created_request; 1077 } 1078 1079 // Format upload data if it's chunked. 1080 if (request_info.upload_data && request_info.upload_data->is_chunked()) { 1081 ScopedVector<net::UploadElement>* elements = 1082 request_info.upload_data->elements_mutable(); 1083 for (size_t i = 0; i < elements->size(); ++i) { 1084 net::UploadElement* element = (*elements)[i]; 1085 DCHECK(element->type() == net::UploadElement::TYPE_BYTES); 1086 std::string chunk_length = base::StringPrintf( 1087 "%X\r\n", static_cast<unsigned int>(element->bytes_length())); 1088 std::vector<char> bytes; 1089 bytes.insert(bytes.end(), chunk_length.data(), 1090 chunk_length.data() + chunk_length.length()); 1091 const char* data = element->bytes(); 1092 bytes.insert(bytes.end(), data, data + element->bytes_length()); 1093 const char* crlf = "\r\n"; 1094 bytes.insert(bytes.end(), crlf, crlf + strlen(crlf)); 1095 if (i == elements->size() - 1) { 1096 const char* end_of_data = "0\r\n\r\n"; 1097 bytes.insert(bytes.end(), end_of_data, 1098 end_of_data + strlen(end_of_data)); 1099 } 1100 element->SetToBytes(&bytes[0], static_cast<int>(bytes.size())); 1101 } 1102 } 1103 1104 new_request->Initialize(static_cast<PluginUrlRequestDelegate*>(this), 1105 request_id, 1106 request_info.url, 1107 request_info.method, 1108 request_info.referrer, 1109 request_info.extra_request_headers, 1110 request_info.upload_data, 1111 static_cast<ResourceType::Type>(request_info.resource_type), 1112 enable_frame_busting_, 1113 request_info.load_flags); 1114 new_request->set_parent_window(notification_window_); 1115 new_request->set_privileged_mode(privileged_mode_); 1116 1117 if (request_map_lock) 1118 request_map_lock->Acquire(); 1119 1120 (*request_map)[request_id] = new_request; 1121 1122 if (request_map_lock) 1123 request_map_lock->Release(); 1124 1125 if (!is_started) { 1126 // Freshly created, start now. 1127 new_request->Start(); 1128 } else { 1129 // Request is already underway, call OnResponse so that the 1130 // other side can start reading. 1131 DCHECK(!new_request->response_headers().empty()); 1132 new_request->OnResponse( 1133 0, UTF8ToWide(new_request->response_headers()).c_str(), NULL, NULL); 1134 } 1135 } 1136 1137 void UrlmonUrlRequestManager::ReadRequest(int request_id, int bytes_to_read) { 1138 DVLOG(1) << __FUNCTION__ << " id: " << request_id; 1139 // if we fail to find the request in the normal map and the background 1140 // request map, it may mean that the request could have failed with a 1141 // network error. 1142 scoped_refptr<UrlmonUrlRequest> request = LookupRequest(request_id, 1143 &request_map_); 1144 if (request) { 1145 request->Read(bytes_to_read); 1146 } else if (background_worker_thread_enabled_) { 1147 base::AutoLock lock(background_resource_map_lock_); 1148 request = LookupRequest(request_id, &background_request_map_); 1149 if (request) { 1150 background_thread_->message_loop()->PostTask( 1151 FROM_HERE, base::Bind(base::IgnoreResult(&UrlmonUrlRequest::Read), 1152 request.get(), bytes_to_read)); 1153 } 1154 } 1155 if (!request) 1156 DLOG(ERROR) << __FUNCTION__ << " no request found for " << request_id; 1157 } 1158 1159 void UrlmonUrlRequestManager::DownloadRequestInHost(int request_id) { 1160 DVLOG(1) << __FUNCTION__ << " " << request_id; 1161 if (!IsWindow(notification_window_)) { 1162 NOTREACHED() << "Cannot handle download if we don't have anyone to hand it " 1163 "to."; 1164 return; 1165 } 1166 1167 scoped_refptr<UrlmonUrlRequest> request(LookupRequest(request_id, 1168 &request_map_)); 1169 if (request) { 1170 DownloadRequestInHostHelper(request); 1171 } else if (background_worker_thread_enabled_) { 1172 base::AutoLock lock(background_resource_map_lock_); 1173 request = LookupRequest(request_id, &background_request_map_); 1174 if (request) { 1175 background_thread_->message_loop()->PostTask( 1176 FROM_HERE, 1177 base::Bind(&UrlmonUrlRequestManager::DownloadRequestInHostHelper, 1178 base::Unretained(this), request.get())); 1179 } 1180 } 1181 if (!request) 1182 DLOG(ERROR) << __FUNCTION__ << " no request found for " << request_id; 1183 } 1184 1185 void UrlmonUrlRequestManager::DownloadRequestInHostHelper( 1186 UrlmonUrlRequest* request) { 1187 DCHECK(request); 1188 UrlmonUrlRequest::TerminateBindCallback callback = 1189 base::Bind(&UrlmonUrlRequestManager::BindTerminated, 1190 base::Unretained(this)); 1191 request->TerminateBind(callback); 1192 } 1193 1194 void UrlmonUrlRequestManager::BindTerminated(IMoniker* moniker, 1195 IBindCtx* bind_ctx, 1196 IStream* post_data, 1197 const char* request_headers) { 1198 DownloadInHostParams* download_params = new DownloadInHostParams; 1199 download_params->bind_ctx = bind_ctx; 1200 download_params->moniker = moniker; 1201 download_params->post_data = post_data; 1202 if (request_headers) { 1203 download_params->request_headers = request_headers; 1204 } 1205 ::PostMessage(notification_window_, WM_DOWNLOAD_IN_HOST, 1206 reinterpret_cast<WPARAM>(download_params), 0); 1207 } 1208 1209 void UrlmonUrlRequestManager::GetCookiesForUrl(const GURL& url, int cookie_id) { 1210 DWORD cookie_size = 0; 1211 bool success = true; 1212 std::string cookie_string; 1213 1214 int32 cookie_action = COOKIEACTION_READ; 1215 BOOL result = InternetGetCookieA(url.spec().c_str(), NULL, NULL, 1216 &cookie_size); 1217 DWORD error = 0; 1218 if (cookie_size) { 1219 scoped_ptr<char[]> cookies(new char[cookie_size + 1]); 1220 if (!InternetGetCookieA(url.spec().c_str(), NULL, cookies.get(), 1221 &cookie_size)) { 1222 success = false; 1223 error = GetLastError(); 1224 NOTREACHED() << "InternetGetCookie failed. Error: " << error; 1225 } else { 1226 cookie_string = cookies.get(); 1227 } 1228 } else { 1229 success = false; 1230 error = GetLastError(); 1231 DVLOG(1) << "InternetGetCookie failed. Error: " << error; 1232 } 1233 1234 OnCookiesRetrieved(success, url, cookie_string, cookie_id); 1235 if (!success && !error) 1236 cookie_action = COOKIEACTION_SUPPRESS; 1237 1238 AddPrivacyDataForUrl(url.spec(), "", cookie_action); 1239 } 1240 1241 void UrlmonUrlRequestManager::SetCookiesForUrl(const GURL& url, 1242 const std::string& cookie) { 1243 DCHECK(container_); 1244 // Grab a reference on the container to ensure that we don't get destroyed in 1245 // case the InternetSetCookie call below puts up a dialog box, which can 1246 // happen if the cookie policy is set to prompt. 1247 if (container_) { 1248 container_->AddRef(); 1249 } 1250 1251 InternetCookieState cookie_state = static_cast<InternetCookieState>( 1252 InternetSetCookieExA(url.spec().c_str(), NULL, cookie.c_str(), 1253 INTERNET_COOKIE_EVALUATE_P3P, NULL)); 1254 1255 int32 cookie_action = MapCookieStateToCookieAction(cookie_state); 1256 AddPrivacyDataForUrl(url.spec(), "", cookie_action); 1257 1258 if (container_) { 1259 container_->Release(); 1260 } 1261 } 1262 1263 void UrlmonUrlRequestManager::EndRequest(int request_id) { 1264 DVLOG(1) << __FUNCTION__ << " id: " << request_id; 1265 scoped_refptr<UrlmonUrlRequest> request = LookupRequest(request_id, 1266 &request_map_); 1267 if (request) { 1268 request_map_.erase(request_id); 1269 request->Stop(); 1270 } else if (background_worker_thread_enabled_) { 1271 base::AutoLock lock(background_resource_map_lock_); 1272 request = LookupRequest(request_id, &background_request_map_); 1273 if (request) { 1274 background_request_map_.erase(request_id); 1275 background_thread_->message_loop()->PostTask( 1276 FROM_HERE, base::Bind(&UrlmonUrlRequest::Stop, request.get())); 1277 } 1278 } 1279 if (!request) 1280 DLOG(ERROR) << __FUNCTION__ << " no request found for " << request_id; 1281 } 1282 1283 void UrlmonUrlRequestManager::StopAll() { 1284 DVLOG(1) << __FUNCTION__; 1285 if (stopping_) 1286 return; 1287 1288 stopping_ = true; 1289 1290 DVLOG(1) << __FUNCTION__ << " stopping " << request_map_.size() 1291 << " requests"; 1292 1293 StopAllRequestsHelper(&request_map_, NULL); 1294 1295 if (background_worker_thread_enabled_) { 1296 DCHECK(background_thread_.get()); 1297 background_thread_->message_loop()->PostTask( 1298 FROM_HERE, base::Bind(&UrlmonUrlRequestManager::StopAllRequestsHelper, 1299 base::Unretained(this), &background_request_map_, 1300 &background_resource_map_lock_)); 1301 background_thread_->Stop(); 1302 background_thread_.reset(); 1303 } 1304 } 1305 1306 void UrlmonUrlRequestManager::StopAllRequestsHelper( 1307 RequestMap* request_map, 1308 base::Lock* request_map_lock) { 1309 DCHECK(request_map); 1310 1311 DVLOG(1) << __FUNCTION__ << " stopping " << request_map->size() 1312 << " requests"; 1313 1314 if (request_map_lock) 1315 request_map_lock->Acquire(); 1316 1317 for (RequestMap::iterator it = request_map->begin(); 1318 it != request_map->end(); ++it) { 1319 DCHECK(it->second != NULL); 1320 it->second->Stop(); 1321 } 1322 request_map->clear(); 1323 1324 if (request_map_lock) 1325 request_map_lock->Release(); 1326 } 1327 1328 void UrlmonUrlRequestManager::OnResponseStarted( 1329 int request_id, const char* mime_type, const char* headers, int size, 1330 base::Time last_modified, const std::string& redirect_url, 1331 int redirect_status, const net::HostPortPair& socket_address, 1332 uint64 upload_size) { 1333 DCHECK_NE(request_id, -1); 1334 DVLOG(1) << __FUNCTION__; 1335 1336 #ifndef NDEBUG 1337 scoped_refptr<UrlmonUrlRequest> request = LookupRequest(request_id, 1338 &request_map_); 1339 if (request == NULL && background_worker_thread_enabled_) { 1340 base::AutoLock lock(background_resource_map_lock_); 1341 request = LookupRequest(request_id, &background_request_map_); 1342 } 1343 DCHECK(request != NULL); 1344 #endif // NDEBUG 1345 delegate_->OnResponseStarted( 1346 request_id, mime_type, headers, size, last_modified, redirect_url, 1347 redirect_status, socket_address, upload_size); 1348 } 1349 1350 void UrlmonUrlRequestManager::OnReadComplete(int request_id, 1351 const std::string& data) { 1352 DCHECK_NE(request_id, -1); 1353 DVLOG(1) << __FUNCTION__ << " id: " << request_id; 1354 #ifndef NDEBUG 1355 scoped_refptr<UrlmonUrlRequest> request = LookupRequest(request_id, 1356 &request_map_); 1357 if (request == NULL && background_worker_thread_enabled_) { 1358 base::AutoLock lock(background_resource_map_lock_); 1359 request = LookupRequest(request_id, &background_request_map_); 1360 } 1361 DCHECK(request != NULL); 1362 #endif // NDEBUG 1363 delegate_->OnReadComplete(request_id, data); 1364 DVLOG(1) << __FUNCTION__ << " done id: " << request_id; 1365 } 1366 1367 void UrlmonUrlRequestManager::OnResponseEnd( 1368 int request_id, 1369 const net::URLRequestStatus& status) { 1370 DCHECK_NE(request_id, -1); 1371 DVLOG(1) << __FUNCTION__; 1372 DCHECK(status.status() != net::URLRequestStatus::CANCELED); 1373 RequestMap::size_type erased_count = request_map_.erase(request_id); 1374 if (erased_count != 1u && background_worker_thread_enabled_) { 1375 base::AutoLock lock(background_resource_map_lock_); 1376 erased_count = background_request_map_.erase(request_id); 1377 if (erased_count != 1u) { 1378 DLOG(WARNING) << __FUNCTION__ 1379 << " Failed to find request id:" 1380 << request_id; 1381 } 1382 } 1383 delegate_->OnResponseEnd(request_id, status); 1384 } 1385 1386 void UrlmonUrlRequestManager::OnCookiesRetrieved(bool success, const GURL& url, 1387 const std::string& cookie_string, int cookie_id) { 1388 DCHECK(url.is_valid()); 1389 delegate_->OnCookiesRetrieved(success, url, cookie_string, cookie_id); 1390 } 1391 1392 scoped_refptr<UrlmonUrlRequest> UrlmonUrlRequestManager::LookupRequest( 1393 int request_id, RequestMap* request_map) { 1394 RequestMap::iterator it = request_map->find(request_id); 1395 if (request_map->end() != it) 1396 return it->second; 1397 return NULL; 1398 } 1399 1400 UrlmonUrlRequestManager::UrlmonUrlRequestManager() 1401 : stopping_(false), notification_window_(NULL), 1402 privileged_mode_(false), 1403 container_(NULL), 1404 background_worker_thread_enabled_(true) { 1405 background_thread_.reset(new base::Thread("cf_iexplore_background_thread")); 1406 background_thread_->init_com_with_mta(false); 1407 background_worker_thread_enabled_ = 1408 GetConfigBool(true, kUseBackgroundThreadForSubResources); 1409 if (background_worker_thread_enabled_) { 1410 base::Thread::Options options; 1411 options.message_loop_type = base::MessageLoop::TYPE_UI; 1412 background_thread_->StartWithOptions(options); 1413 } 1414 } 1415 1416 UrlmonUrlRequestManager::~UrlmonUrlRequestManager() { 1417 StopAll(); 1418 } 1419 1420 void UrlmonUrlRequestManager::AddPrivacyDataForUrl( 1421 const std::string& url, const std::string& policy_ref, 1422 int32 flags) { 1423 DCHECK(!url.empty()); 1424 1425 bool fire_privacy_event = false; 1426 1427 if (privacy_info_.privacy_records.empty()) 1428 flags |= PRIVACY_URLISTOPLEVEL; 1429 1430 if (!privacy_info_.privacy_impacted) { 1431 if (flags & (COOKIEACTION_ACCEPT | COOKIEACTION_REJECT | 1432 COOKIEACTION_DOWNGRADE)) { 1433 privacy_info_.privacy_impacted = true; 1434 fire_privacy_event = true; 1435 } 1436 } 1437 1438 PrivacyInfo::PrivacyEntry& privacy_entry = 1439 privacy_info_.privacy_records[UTF8ToWide(url)]; 1440 1441 privacy_entry.flags |= flags; 1442 privacy_entry.policy_ref = UTF8ToWide(policy_ref); 1443 1444 if (fire_privacy_event && IsWindow(notification_window_)) { 1445 PostMessage(notification_window_, WM_FIRE_PRIVACY_CHANGE_NOTIFICATION, 1, 1446 0); 1447 } 1448 } 1449