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
34UrlmonUrlRequest::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
47UrlmonUrlRequest::~UrlmonUrlRequest() {
48  DVLOG(1) << __FUNCTION__ << me();
49}
50
51std::string UrlmonUrlRequest::me() const {
52  return base::StringPrintf(" id: %i Obj: %X ", id(), this);
53}
54
55bool 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
77void 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
105bool 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
140HRESULT 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
169void 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
203size_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
254STDMETHODIMP 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
265STDMETHODIMP UrlmonUrlRequest::GetPriority(LONG *priority) {
266  if (!priority)
267    return E_POINTER;
268  *priority = THREAD_PRIORITY_NORMAL;
269  return S_OK;
270}
271
272STDMETHODIMP UrlmonUrlRequest::OnLowResource(DWORD reserved) {
273  return S_OK;
274}
275
276STDMETHODIMP 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
361STDMETHODIMP 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
454STDMETHODIMP 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
522STDMETHODIMP 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
568STDMETHODIMP 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
575STDMETHODIMP 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
647STDMETHODIMP 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
706STDMETHODIMP 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
731STDMETHODIMP 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
745STDMETHODIMP 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
805HRESULT 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
869void 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
885void 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
907void 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
916net::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
983PluginUrlRequestManager::ThreadSafeFlags
984    UrlmonUrlRequestManager::GetThreadSafeFlags() {
985  return PluginUrlRequestManager::NOT_THREADSAFE;
986}
987
988void 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
1011void 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
1048void 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
1137void 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
1159void 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
1185void 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
1194void 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
1209void 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
1241void 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
1263void 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
1283void 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
1306void 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
1328void 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
1350void 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
1367void 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
1386void 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
1392scoped_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
1400UrlmonUrlRequestManager::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
1416UrlmonUrlRequestManager::~UrlmonUrlRequestManager() {
1417  StopAll();
1418}
1419
1420void 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