Home | History | Annotate | Download | only in chrome_frame
      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::EndRequest(int request_id) {
   1210   DVLOG(1) << __FUNCTION__ << " id: " << request_id;
   1211   scoped_refptr<UrlmonUrlRequest> request = LookupRequest(request_id,
   1212                                                           &request_map_);
   1213   if (request) {
   1214     request_map_.erase(request_id);
   1215     request->Stop();
   1216   } else if (background_worker_thread_enabled_) {
   1217     base::AutoLock lock(background_resource_map_lock_);
   1218     request = LookupRequest(request_id, &background_request_map_);
   1219     if (request) {
   1220       background_request_map_.erase(request_id);
   1221       background_thread_->message_loop()->PostTask(
   1222           FROM_HERE, base::Bind(&UrlmonUrlRequest::Stop, request.get()));
   1223     }
   1224   }
   1225   if (!request)
   1226     DLOG(ERROR) << __FUNCTION__ << " no request found for " << request_id;
   1227 }
   1228 
   1229 void UrlmonUrlRequestManager::StopAll() {
   1230   DVLOG(1) << __FUNCTION__;
   1231   if (stopping_)
   1232     return;
   1233 
   1234   stopping_ = true;
   1235 
   1236   DVLOG(1) << __FUNCTION__ << " stopping " << request_map_.size()
   1237            << " requests";
   1238 
   1239   StopAllRequestsHelper(&request_map_, NULL);
   1240 
   1241   if (background_worker_thread_enabled_) {
   1242     DCHECK(background_thread_.get());
   1243     background_thread_->message_loop()->PostTask(
   1244         FROM_HERE, base::Bind(&UrlmonUrlRequestManager::StopAllRequestsHelper,
   1245                               base::Unretained(this), &background_request_map_,
   1246                               &background_resource_map_lock_));
   1247     background_thread_->Stop();
   1248     background_thread_.reset();
   1249   }
   1250 }
   1251 
   1252 void UrlmonUrlRequestManager::StopAllRequestsHelper(
   1253     RequestMap* request_map,
   1254     base::Lock* request_map_lock) {
   1255   DCHECK(request_map);
   1256 
   1257   DVLOG(1) << __FUNCTION__ << " stopping " << request_map->size()
   1258            << " requests";
   1259 
   1260   if (request_map_lock)
   1261     request_map_lock->Acquire();
   1262 
   1263   for (RequestMap::iterator it = request_map->begin();
   1264        it != request_map->end(); ++it) {
   1265     DCHECK(it->second != NULL);
   1266     it->second->Stop();
   1267   }
   1268   request_map->clear();
   1269 
   1270   if (request_map_lock)
   1271     request_map_lock->Release();
   1272 }
   1273 
   1274 void UrlmonUrlRequestManager::OnResponseStarted(
   1275     int request_id, const char* mime_type, const char* headers, int size,
   1276     base::Time last_modified, const std::string& redirect_url,
   1277     int redirect_status, const net::HostPortPair& socket_address,
   1278     uint64 upload_size) {
   1279   DCHECK_NE(request_id, -1);
   1280   DVLOG(1) << __FUNCTION__;
   1281 
   1282 #ifndef NDEBUG
   1283   scoped_refptr<UrlmonUrlRequest> request = LookupRequest(request_id,
   1284                                                           &request_map_);
   1285   if (request == NULL && background_worker_thread_enabled_) {
   1286     base::AutoLock lock(background_resource_map_lock_);
   1287     request = LookupRequest(request_id, &background_request_map_);
   1288   }
   1289   DCHECK(request != NULL);
   1290 #endif  // NDEBUG
   1291   delegate_->OnResponseStarted(
   1292       request_id, mime_type, headers, size, last_modified, redirect_url,
   1293       redirect_status, socket_address, upload_size);
   1294 }
   1295 
   1296 void UrlmonUrlRequestManager::OnReadComplete(int request_id,
   1297                                              const std::string& data) {
   1298   DCHECK_NE(request_id, -1);
   1299   DVLOG(1) << __FUNCTION__ << " id: " << request_id;
   1300 #ifndef NDEBUG
   1301   scoped_refptr<UrlmonUrlRequest> request = LookupRequest(request_id,
   1302                                                           &request_map_);
   1303   if (request == NULL && background_worker_thread_enabled_) {
   1304     base::AutoLock lock(background_resource_map_lock_);
   1305     request = LookupRequest(request_id, &background_request_map_);
   1306   }
   1307   DCHECK(request != NULL);
   1308 #endif  // NDEBUG
   1309   delegate_->OnReadComplete(request_id, data);
   1310   DVLOG(1) << __FUNCTION__ << " done id: " << request_id;
   1311 }
   1312 
   1313 void UrlmonUrlRequestManager::OnResponseEnd(
   1314     int request_id,
   1315     const net::URLRequestStatus& status) {
   1316   DCHECK_NE(request_id, -1);
   1317   DVLOG(1) << __FUNCTION__;
   1318   DCHECK(status.status() != net::URLRequestStatus::CANCELED);
   1319   RequestMap::size_type erased_count = request_map_.erase(request_id);
   1320   if (erased_count != 1u && background_worker_thread_enabled_) {
   1321     base::AutoLock lock(background_resource_map_lock_);
   1322     erased_count = background_request_map_.erase(request_id);
   1323     if (erased_count != 1u) {
   1324       DLOG(WARNING) << __FUNCTION__
   1325                     << " Failed to find request id:"
   1326                     << request_id;
   1327     }
   1328   }
   1329   delegate_->OnResponseEnd(request_id, status);
   1330 }
   1331 
   1332 scoped_refptr<UrlmonUrlRequest> UrlmonUrlRequestManager::LookupRequest(
   1333     int request_id, RequestMap* request_map) {
   1334   RequestMap::iterator it = request_map->find(request_id);
   1335   if (request_map->end() != it)
   1336     return it->second;
   1337   return NULL;
   1338 }
   1339 
   1340 UrlmonUrlRequestManager::UrlmonUrlRequestManager()
   1341     : stopping_(false), notification_window_(NULL),
   1342       privileged_mode_(false),
   1343       container_(NULL),
   1344       background_worker_thread_enabled_(true) {
   1345   background_thread_.reset(new base::Thread("cf_iexplore_background_thread"));
   1346   background_thread_->init_com_with_mta(false);
   1347   background_worker_thread_enabled_ =
   1348       GetConfigBool(true, kUseBackgroundThreadForSubResources);
   1349   if (background_worker_thread_enabled_) {
   1350     base::Thread::Options options;
   1351     options.message_loop_type = base::MessageLoop::TYPE_UI;
   1352     background_thread_->StartWithOptions(options);
   1353   }
   1354 }
   1355 
   1356 UrlmonUrlRequestManager::~UrlmonUrlRequestManager() {
   1357   StopAll();
   1358 }
   1359 
   1360 void UrlmonUrlRequestManager::AddPrivacyDataForUrl(
   1361     const std::string& url, const std::string& policy_ref,
   1362     int32 flags) {
   1363   DCHECK(!url.empty());
   1364 
   1365   bool fire_privacy_event = false;
   1366 
   1367   if (privacy_info_.privacy_records.empty())
   1368     flags |= PRIVACY_URLISTOPLEVEL;
   1369 
   1370   if (!privacy_info_.privacy_impacted) {
   1371     if (flags & (COOKIEACTION_ACCEPT | COOKIEACTION_REJECT |
   1372                  COOKIEACTION_DOWNGRADE)) {
   1373       privacy_info_.privacy_impacted = true;
   1374       fire_privacy_event = true;
   1375     }
   1376   }
   1377 
   1378   PrivacyInfo::PrivacyEntry& privacy_entry =
   1379       privacy_info_.privacy_records[UTF8ToWide(url)];
   1380 
   1381   privacy_entry.flags |= flags;
   1382   privacy_entry.policy_ref = UTF8ToWide(policy_ref);
   1383 
   1384   if (fire_privacy_event && IsWindow(notification_window_)) {
   1385     PostMessage(notification_window_, WM_FIRE_PRIVACY_CHANGE_NOTIFICATION, 1,
   1386                 0);
   1387   }
   1388 }
   1389