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