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