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