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"
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),
37 privileged_mode_(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();
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
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
) {
68 status_
.set_result(net::URLRequestStatus::FAILED
, HresultToNetError(hr
));
69 NotifyDelegateAndDie();
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();
80 // If DownloadInHost is already requested, we will quit soon anyway.
81 if (terminate_requested())
91 case Status::ABORTING
:
97 NotifyDelegateAndDie();
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)
115 DCHECK((status_
.get_state() != Status::DONE
) == (binding_
!= NULL
));
116 if (status_
.get_state() == Status::ABORTING
)
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
;
126 if (status_
.get_state() == Status::WORKING
) {
127 DVLOG(1) << __FUNCTION__
<< me() << " pending: " << bytes_to_read
;
128 pending_read_size_
= bytes_to_read
;
130 DVLOG(1) << __FUNCTION__
<< me() << " Response finished.";
131 NotifyDelegateAndDie();
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
,
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
;
151 enable_frame_busting_
= enable_frame_busting
;
152 privileged_mode_
= privileged_mode
;
153 parent_window_
= notification_window
;
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.
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());
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
179 terminate_bind_callback_
= callback
;
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.
189 base::win::ScopedComPtr
<IStream
> read_stream(pending_data_
);
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());
203 DCHECK_GT(bytes_to_read
, 0U);
204 size_t bytes_copied
= 0;
206 std::string read_data
;
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";
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.
231 pending_data_
.Release();
234 bytes_copied
= read_data
.length();
239 // The delegate can go away in the middle of ReadStream
241 delegate_
->OnReadComplete(id(), read_data
);
245 DLOG(ERROR
) << __FUNCTION__
<< me() << "no delegate";
251 STDMETHODIMP
UrlmonUrlRequest::OnStartBinding(DWORD reserved
,
253 DCHECK_EQ(thread_
, base::PlatformThread::CurrentId());
256 response_headers_
= GetHttpHeadersFromBinding(binding_
);
257 DCHECK(!response_headers_
.empty());
262 STDMETHODIMP
UrlmonUrlRequest::GetPriority(LONG
*priority
) {
265 *priority
= THREAD_PRIORITY_NORMAL
;
269 STDMETHODIMP
UrlmonUrlRequest::OnLowResource(DWORD reserved
) {
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
)
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
)
286 DVLOG(1) << "Invalid delegate";
290 switch (status_code
) {
291 case BINDSTATUS_CONNECTING
: {
293 socket_address_
.set_host(WideToUTF8(status_text
));
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());
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.
320 case BINDSTATUS_COOKIE_SENT
:
321 delegate_
->AddPrivacyDataForUrl(url(), "", COOKIEACTION_READ
);
324 case BINDSTATUS_COOKIE_SUPPRESSED
:
325 delegate_
->AddPrivacyDataForUrl(url(), "", COOKIEACTION_SUPPRESS
);
328 case BINDSTATUS_COOKIE_STATE_ACCEPT
:
329 delegate_
->AddPrivacyDataForUrl(url(), "", COOKIEACTION_ACCEPT
);
332 case BINDSTATUS_COOKIE_STATE_REJECT
:
333 delegate_
->AddPrivacyDataForUrl(url(), "", COOKIEACTION_REJECT
);
336 case BINDSTATUS_COOKIE_STATE_LEASH
:
337 delegate_
->AddPrivacyDataForUrl(url(), "", COOKIEACTION_LEASH
);
340 case BINDSTATUS_COOKIE_STATE_DOWNGRADE
:
341 delegate_
->AddPrivacyDataForUrl(url(), "", COOKIEACTION_DOWNGRADE
);
344 case BINDSTATUS_COOKIE_STATE_UNKNOWN
:
345 NOTREACHED() << L
"Unknown cookie state received";
349 DVLOG(1) << __FUNCTION__
<< me()
350 << base::StringPrintf(L
"code: %i status: %ls", status_code
,
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.
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());
375 cleanup_transaction_
= true;
377 // We may have returned INET_E_TERMINATED_BIND from OnDataAvailable.
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!!
407 DCHECK_EQ(pending_read_size_
, 0UL);
412 if (headers_received_
&& pending_read_size_
== 0) {
417 // No headers or there is a pending read from Chrome.
418 NotifyDelegateAndDie();
423 if (status_
.was_redirected()) {
424 // Just release bindings here. Chrome will issue EndRequest(request_id)
425 // after processing headers we had provided.
427 std::string headers
= GetHttpHeadersFromBinding(binding_
);
428 OnResponse(0, UTF8ToWide(headers
).c_str(), NULL
, NULL
);
435 NotifyDelegateAndDie();
439 STDMETHODIMP
UrlmonUrlRequest::GetBindInfo(DWORD
* bind_flags
,
440 BINDINFO
* bind_info
) {
441 if ((bind_info
== NULL
) || (bind_info
->cbSize
== 0) || (bind_flags
== NULL
))
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
;
450 // Look into whether the other load flags need to be supported in chrome
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
;
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();
501 DVLOG(1) << __FUNCTION__
<< me() << "POST request with no data!";
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
)) {
523 IStream
* read_stream
= storage
->pstm
;
529 // Some requests such as HEAD have zero data.
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
;
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";
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
;
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
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
) {
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:"
584 << " as the binding has been aborted";
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
) {
626 lstrcpynW(*additional_headers
, ASCIIToWide(new_headers
).c_str(),
630 request_headers_
= new_headers
;
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
;
641 DLOG(WARNING
) << "Invalid delegate";
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";
673 DVLOG(1) << __FUNCTION__
<< me() << "Calling OnResponseStarted";
675 // Inform the delegate.
676 headers_received_
= true;
678 delegate_
->OnResponseStarted(id(),
680 raw_headers
.c_str(), // headers
682 base::Time(), // last_modified
683 status_
.get_redirection().utf8_url
,
684 status_
.get_redirection().http_code
,
689 STDMETHODIMP
UrlmonUrlRequest::GetWindow(const GUID
& guid_reason
,
690 HWND
* parent_window
) {
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
;
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_
;
714 STDMETHODIMP
UrlmonUrlRequest::Authenticate(HWND
* parent_window
,
720 if (privileged_mode_
)
721 return E_ACCESSDENIED
;
723 DCHECK(::IsWindow(parent_window_
));
724 *parent_window
= parent_window_
;
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
)
765 HRESULT hr
= E_ABORT
;
768 case ERROR_INTERNET_SEC_CERT_REV_FAILED
: {
773 case ERROR_INTERNET_SEC_CERT_DATE_INVALID
:
774 case ERROR_INTERNET_SEC_CERT_CN_INVALID
:
775 case ERROR_INTERNET_INVALID_CA
: {
781 NOTREACHED() << "Unhandled security problem : " << problem
;
788 HRESULT
UrlmonUrlRequest::StartAsyncDownload() {
789 DVLOG(1) << __FUNCTION__
<< me() << url();
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(),
798 NOTREACHED() << "CreateURLMonikerEx failed. Error: " << 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
;
808 // Use existing bind context.
809 hr
= ::RegisterBindStatusCallback(bind_context_
, this, NULL
, 0);
810 DCHECK(SUCCEEDED(hr
)) << "RegisterBindStatusCallback failed. Error: " << 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());
827 info
->set_chrome_request(true);
829 hr
= moniker_
->BindToStorage(bind_context_
, NULL
, __uuidof(IStream
),
830 reinterpret_cast<void**>(stream
.Receive()));
832 DCHECK(binding_
!= NULL
|| status_
.get_state() == Status::DONE
);
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
);
852 void UrlmonUrlRequest::NotifyDelegateAndDie() {
853 DCHECK_EQ(thread_
, base::PlatformThread::CurrentId());
854 DVLOG(1) << __FUNCTION__
<< me();
856 PluginUrlRequestDelegate
* delegate
= delegate_
;
859 TerminateTransaction();
860 if (delegate
&& id() != -1) {
861 net::URLRequestStatus result
= status_
.get_result();
862 delegate
->OnResponseEnd(id(), result
);
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
879 static const int kUrlmonTerminateTransactionFlags
= 0x2000000;
880 base::win::ScopedComPtr
<BindContextInfo
> info
;
881 BindContextInfo::FromBindContext(bind_context_
, info
.Receive());
883 if (info
&& info
->protocol()) {
884 info
->protocol()->Terminate(kUrlmonTerminateTransactionFlags
);
887 bind_context_
.Release();
890 void UrlmonUrlRequest::ReleaseBindings() {
892 // Do not release bind_context here!
893 // We may get DownloadToHost request and therefore we want the bind_context
896 ::RevokeBindStatusCallback(bind_context_
, this);
899 net::Error
UrlmonUrlRequest::HresultToNetError(HRESULT hr
) {
900 const int kInvalidHostName
= 0x8007007b;
902 // http://msdn.microsoft.com/en-us/library/ms775145(VS.85).aspx
904 net::Error ret
= net::ERR_UNEXPECTED
;
912 ret
= net::ERR_INVALID_URL
;
915 case INET_E_CANNOT_CONNECT
:
916 ret
= net::ERR_CONNECTION_FAILED
;
919 case INET_E_DOWNLOAD_FAILURE
:
920 case INET_E_CONNECTION_TIMEOUT
:
922 ret
= net::ERR_CONNECTION_ABORTED
;
925 case INET_E_DATA_NOT_AVAILABLE
:
926 ret
= net::ERR_EMPTY_RESPONSE
;
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
;
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
:
945 ret
= net::ERR_INVALID_URL
;
948 case INET_E_INVALID_CERTIFICATE
:
949 ret
= net::ERR_CERT_INVALID
;
953 ret
= net::ERR_ACCESS_DENIED
;
958 << base::StringPrintf("TODO: translate HRESULT 0x%08X to net::Error",
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
);
977 DCHECK(start_url
.is_valid());
978 DCHECK(pending_request_
== NULL
);
980 base::win::ScopedComPtr
<BindContextInfo
> info
;
981 BindContextInfo::FromBindContext(bind_ctx
, info
.Receive());
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
);
989 bool is_started
= pending_request_
->Start();
994 void UrlmonUrlRequestManager::StartRequest(int request_id
,
995 const AutomationURLRequest
& request_info
) {
996 DVLOG(1) << __FUNCTION__
<< " id: " << request_id
;
999 DLOG(WARNING
) << __FUNCTION__
<< " request not started (stopping)";
1003 DCHECK(request_map_
.find(request_id
) == request_map_
.end());
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());
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(
1023 base::Bind(&UrlmonUrlRequestManager::StartRequestHelper
,
1024 base::Unretained(this), request_id
, request_info
,
1025 &background_request_map_
, &background_resource_map_lock_
));
1028 StartRequestHelper(request_id
, request_info
, &request_map_
, NULL
);
1031 void UrlmonUrlRequestManager::StartRequestHelper(
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:"
1044 << ". Stopping pending url request for url:"
1045 << pending_request_
->url();
1046 pending_request_
->Stop();
1047 pending_request_
= NULL
;
1049 new_request
.swap(pending_request_
);
1051 DVLOG(1) << __FUNCTION__
<< new_request
->me()
1052 << " assigned id " << request_id
;
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),
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();
1084 // Freshly created, start now.
1085 new_request
->Start();
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
1100 scoped_refptr
<UrlmonUrlRequest
> request
= LookupRequest(request_id
,
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_
);
1108 background_thread_
->message_loop()->PostTask(
1109 FROM_HERE
, base::IgnoreReturn
<bool>(base::Bind(
1110 &UrlmonUrlRequest::Read
, request
.get(), bytes_to_read
)));
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 "
1125 scoped_refptr
<UrlmonUrlRequest
> request(LookupRequest(request_id
,
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_
);
1133 background_thread_
->message_loop()->PostTask(
1135 base::Bind(&UrlmonUrlRequestManager::DownloadRequestInHostHelper
,
1136 base::Unretained(this), request
.get()));
1140 DLOG(ERROR
) << __FUNCTION__
<< " no request found for " << request_id
;
1143 void UrlmonUrlRequestManager::DownloadRequestInHostHelper(
1144 UrlmonUrlRequest
* request
) {
1146 UrlmonUrlRequest::TerminateBindCallback callback
=
1147 base::Bind(&UrlmonUrlRequestManager::BindTerminated
,
1148 base::Unretained(this));
1149 request
->TerminateBind(callback
);
1152 void UrlmonUrlRequestManager::BindTerminated(IMoniker
* moniker
,
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
,
1177 scoped_array
<char> cookies(new char[cookie_size
+ 1]);
1178 if (!InternetGetCookieA(url
.spec().c_str(), NULL
, cookies
.get(),
1181 error
= GetLastError();
1182 NOTREACHED() << "InternetGetCookie failed. Error: " << error
;
1184 cookie_string
= cookies
.get();
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
) {
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.
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
);
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
,
1226 request_map_
.erase(request_id
);
1228 } else if (background_worker_thread_enabled_
) {
1229 base::AutoLock
lock(background_resource_map_lock_
);
1230 request
= LookupRequest(request_id
, &background_request_map_
);
1232 background_request_map_
.erase(request_id
);
1233 background_thread_
->message_loop()->PostTask(
1234 FROM_HERE
, base::Bind(&UrlmonUrlRequest::Stop
, request
.get()));
1238 DLOG(ERROR
) << __FUNCTION__
<< " no request found for " << request_id
;
1241 void UrlmonUrlRequestManager::StopAll() {
1242 DVLOG(1) << __FUNCTION__
;
1248 DVLOG(1) << __FUNCTION__
<< " stopping " << request_map_
.size()
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()
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
);
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__
;
1294 scoped_refptr
<UrlmonUrlRequest
> request
= LookupRequest(request_id
,
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
);
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
;
1311 scoped_refptr
<UrlmonUrlRequest
> request
= LookupRequest(request_id
,
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
);
1319 delegate_
->OnReadComplete(request_id
, data
);
1320 DVLOG(1) << __FUNCTION__
<< " done id: " << request_id
;
1323 void UrlmonUrlRequestManager::OnResponseEnd(
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:"
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
)
1356 UrlmonUrlRequestManager::UrlmonUrlRequestManager()
1357 : stopping_(false), notification_window_(NULL
),
1358 privileged_mode_(false),
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() {
1376 void UrlmonUrlRequestManager::AddPrivacyDataForUrl(
1377 const std::string
& url
, const std::string
& policy_ref
,
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() {
1414 void UrlmonUrlRequestManager::ResourceFetcherThread::Init() {
1418 void UrlmonUrlRequestManager::ResourceFetcherThread::CleanUp() {