| Index: chrome_frame/urlmon_url_request.cc
|
| ===================================================================
|
| --- chrome_frame/urlmon_url_request.cc (revision 37418)
|
| +++ chrome_frame/urlmon_url_request.cc (working copy)
|
| @@ -11,9 +11,9 @@
|
| #include "base/string_util.h"
|
| #include "base/logging.h"
|
| #include "base/message_loop.h"
|
| -#include "chrome_frame/chrome_frame_activex_base.h"
|
| #include "chrome_frame/extra_system_apis.h"
|
| #include "chrome_frame/html_utils.h"
|
| +#include "chrome_frame/urlmon_url_request_private.h"
|
| #include "chrome_frame/urlmon_upload_data_stream.h"
|
| #include "chrome_frame/utils.h"
|
| #include "net/http/http_util.h"
|
| @@ -21,7 +21,6 @@
|
|
|
| static const LARGE_INTEGER kZero = {0};
|
| static const ULARGE_INTEGER kUnsignedZero = {0};
|
| -int UrlmonUrlRequest::instance_count_ = 0;
|
|
|
| // This class wraps the IBindCtx interface which is passed in when our active
|
| // document object is instantiated. The IBindCtx interface is created on
|
| @@ -222,14 +221,25 @@
|
| ScopedComPtr<IMarshal> standard_marshal_;
|
| };
|
|
|
| +STDMETHODIMP UrlmonUrlRequest::SendStream::Write(const void * buffer,
|
| + ULONG size,
|
| + ULONG* size_written) {
|
| + DCHECK(request_);
|
| + int size_to_write = static_cast<int>(
|
| + std::min(static_cast<ULONG>(MAXINT), size));
|
| + request_->delegate_->OnReadComplete(request_->id(), buffer,
|
| + size_to_write);
|
| + if (size_written)
|
| + *size_written = size_to_write;
|
| + return S_OK;
|
| +}
|
| +
|
| +int UrlmonUrlRequest::instance_count_ = 0;
|
| +
|
| UrlmonUrlRequest::UrlmonUrlRequest()
|
| : pending_read_size_(0),
|
| - status_(URLRequestStatus::FAILED, net::ERR_FAILED),
|
| - thread_(PlatformThread::CurrentId()),
|
| - redirect_status_(0),
|
| - parent_window_(NULL),
|
| - worker_thread_(NULL),
|
| - ignore_redirect_stop_binding_error_(false) {
|
| + thread_(NULL),
|
| + parent_window_(NULL) {
|
| DLOG(INFO) << StringPrintf("Created request. Obj: %X", this)
|
| << " Count: " << ++instance_count_;
|
| }
|
| @@ -240,141 +250,100 @@
|
| }
|
|
|
| bool UrlmonUrlRequest::Start() {
|
| - DCHECK_EQ(PlatformThread::CurrentId(), thread_);
|
| -
|
| - if (!worker_thread_ || !worker_thread_->message_loop()) {
|
| - NOTREACHED() << __FUNCTION__ << " Urlmon request thread not initialized";
|
| - return false;
|
| + thread_ = PlatformThread::CurrentId();
|
| + status_.Start();
|
| + HRESULT hr = StartAsyncDownload();
|
| + if (FAILED(hr)) {
|
| + status_.set_result(URLRequestStatus::FAILED, HresultToNetError(hr));
|
| + NotifyDelegateAndDie();
|
| }
|
| -
|
| - Create(HWND_MESSAGE);
|
| - if (!IsWindow()) {
|
| - NOTREACHED() << "Failed to create urlmon message window: "
|
| - << GetLastError();
|
| - return false;
|
| - }
|
| -
|
| - // Take a self reference to maintain COM lifetime. This will be released
|
| - // in OnFinalMessage
|
| - AddRef();
|
| - request_handler()->AddRequest(this);
|
| -
|
| - worker_thread_->message_loop()->PostTask(
|
| - FROM_HERE, NewRunnableMethod(this, &UrlmonUrlRequest::StartAsync));
|
| -
|
| return true;
|
| }
|
|
|
| void UrlmonUrlRequest::Stop() {
|
| - DCHECK_EQ(PlatformThread::CurrentId(), thread_);
|
| + DCHECK_EQ(thread_, PlatformThread::CurrentId());
|
| + DCHECK((status_.get_state() != Status::DONE) == (binding_ != NULL));
|
| + Status::State state = status_.get_state();
|
| + switch (state) {
|
| + case Status::WORKING:
|
| + status_.Cancel();
|
| + binding_->Abort();
|
| + break;
|
|
|
| - if (!worker_thread_ || !worker_thread_->message_loop()) {
|
| - NOTREACHED() << __FUNCTION__ << " Urlmon request thread not initialized";
|
| - return;
|
| - }
|
| + case Status::ABORTING:
|
| + status_.Cancel();
|
| + break;
|
|
|
| - // We can remove the request from the map safely here if it is still valid.
|
| - // There is an additional reference on the UrlmonUrlRequest instance which
|
| - // is released when the task scheduled by the EndRequest function executes.
|
| - request_handler()->RemoveRequest(this);
|
| -
|
| - worker_thread_->message_loop()->PostTask(
|
| - FROM_HERE, NewRunnableMethod(this, &UrlmonUrlRequest::StopAsync));
|
| -}
|
| -
|
| -void UrlmonUrlRequest::StartAsync() {
|
| - DCHECK(worker_thread_ != NULL);
|
| -
|
| - status_.set_status(URLRequestStatus::IO_PENDING);
|
| - HRESULT hr = StartAsyncDownload();
|
| - if (FAILED(hr)) {
|
| - // Do not call EndRequest() here since it will attempt to free references
|
| - // that have not been established.
|
| - status_.set_os_error(HresultToNetError(hr));
|
| - status_.set_status(URLRequestStatus::FAILED);
|
| - DLOG(ERROR) << "StartAsyncDownload failed";
|
| - EndRequest();
|
| - return;
|
| + case Status::DONE:
|
| + status_.Cancel();
|
| + NotifyDelegateAndDie();
|
| + break;
|
| }
|
| }
|
|
|
| -void UrlmonUrlRequest::StopAsync() {
|
| - DCHECK(worker_thread_ != NULL);
|
| -
|
| - if (binding_) {
|
| - binding_->Abort();
|
| - } else {
|
| - status_.set_status(URLRequestStatus::CANCELED);
|
| - status_.set_os_error(net::ERR_FAILED);
|
| - EndRequest();
|
| - }
|
| -}
|
| -
|
| -void UrlmonUrlRequest::OnFinalMessage(HWND window) {
|
| - m_hWnd = NULL;
|
| - // Release the outstanding reference in the context of the UI thread to
|
| - // ensure that our instance gets deleted in the same thread which created it.
|
| - Release();
|
| -}
|
| -
|
| bool UrlmonUrlRequest::Read(int bytes_to_read) {
|
| - DCHECK_EQ(PlatformThread::CurrentId(), thread_);
|
| -
|
| - DLOG(INFO) << StringPrintf("URL: %s Obj: %X", url().c_str(), this);
|
| -
|
| - if (!worker_thread_ || !worker_thread_->message_loop()) {
|
| - NOTREACHED() << __FUNCTION__ << " Urlmon request thread not initialized";
|
| + DCHECK_EQ(thread_, PlatformThread::CurrentId());
|
| + // Re-entrancy check. Thou shall not call Read() while processOnReadComplete!!
|
| + DCHECK_EQ(0, pending_read_size_);
|
| + if (pending_read_size_ != 0)
|
| return false;
|
| - }
|
|
|
| - worker_thread_->message_loop()->PostTask(
|
| - FROM_HERE, NewRunnableMethod(this, &UrlmonUrlRequest::ReadAsync,
|
| - bytes_to_read));
|
| - return true;
|
| -}
|
| -
|
| -void UrlmonUrlRequest::TransferToHost(IUnknown* host) {
|
| - DCHECK_EQ(PlatformThread::CurrentId(), thread_);
|
| - DCHECK(host);
|
| - DCHECK(moniker_);
|
| - if (moniker_) {
|
| - ScopedComPtr<IBindCtx> bind_context;
|
| - CreateBindCtx(0, bind_context.Receive());
|
| - DCHECK(bind_context);
|
| - NavigateBrowserToMoniker(host, moniker_, NULL, bind_context, NULL);
|
| - moniker_.Release();
|
| + DCHECK((status_.get_state() != Status::DONE) == (binding_ != NULL));
|
| + if (status_.get_state() == Status::ABORTING) {
|
| + return true;
|
| }
|
| -}
|
|
|
| -void UrlmonUrlRequest::ReadAsync(int bytes_to_read) {
|
| // Send cached data if available.
|
| CComObjectStackEx<SendStream> send_stream;
|
| send_stream.Initialize(this);
|
|
|
| size_t bytes_copied = 0;
|
| - if (cached_data_.is_valid() && cached_data_.Read(&send_stream, bytes_to_read,
|
| - &bytes_copied)) {
|
| + if (delegate_ && cached_data_.is_valid() &&
|
| + cached_data_.Read(&send_stream, bytes_to_read, &bytes_copied)) {
|
| DLOG(INFO) << StringPrintf("URL: %s Obj: %X - bytes read from cache: %d",
|
| - url().c_str(), this, bytes_copied);
|
| - return;
|
| + url().c_str(), this, bytes_copied);
|
| + return true;
|
| }
|
|
|
| - // if the request is finished or there's nothing more to read
|
| - // then end the request
|
| - if (!status_.is_io_pending() || !binding_) {
|
| - DLOG(INFO) << StringPrintf("URL: %s Obj: %X. Response finished. Status: %d",
|
| - url().c_str(), this, status_.status());
|
| - EndRequest();
|
| - return;
|
| + if (status_.get_state() == Status::WORKING) {
|
| + DLOG(INFO) << StringPrintf("URL: %s Obj: %X", url().c_str(), this) <<
|
| + "- Read pending for: " << bytes_to_read;
|
| + pending_read_size_ = bytes_to_read;
|
| + } else {
|
| + DLOG(INFO) << StringPrintf("URL: %s Obj: %X. Response finished.",
|
| + url().c_str(), this);
|
| + NotifyDelegateAndDie();
|
| }
|
|
|
| - pending_read_size_ = bytes_to_read;
|
| - DLOG(INFO) << StringPrintf("URL: %s Obj: %X", url().c_str(), this) <<
|
| - "- Read pending for: " << bytes_to_read;
|
| + return true;
|
| }
|
|
|
| -STDMETHODIMP UrlmonUrlRequest::OnStartBinding(
|
| - DWORD reserved, IBinding *binding) {
|
| +HRESULT UrlmonUrlRequest::ConnectToExistingMoniker(IMoniker* moniker,
|
| + IBindCtx* context,
|
| + const std::wstring& url) {
|
| + if (!moniker || url.empty()) {
|
| + NOTREACHED() << "Invalid arguments";
|
| + return E_INVALIDARG;
|
| + }
|
| +
|
| + DCHECK(moniker_.get() == NULL);
|
| + DCHECK(bind_context_.get() == NULL);
|
| +
|
| + bind_context_ = context;
|
| + moniker_ = moniker;
|
| + set_url(WideToUTF8(url));
|
| + return S_OK;
|
| +}
|
| +
|
| +void UrlmonUrlRequest::StealMoniker(IMoniker** moniker) {
|
| + // Could be called in any thread. There should be no race
|
| + // since moniker_ is not released while we are in manager's request map.
|
| + *moniker = moniker_.Detach();
|
| +}
|
| +
|
| +STDMETHODIMP UrlmonUrlRequest::OnStartBinding(DWORD reserved,
|
| + IBinding *binding) {
|
| + DCHECK_EQ(thread_, PlatformThread::CurrentId());
|
| binding_ = binding;
|
| return S_OK;
|
| }
|
| @@ -392,35 +361,16 @@
|
|
|
| STDMETHODIMP UrlmonUrlRequest::OnProgress(ULONG progress, ULONG max_progress,
|
| ULONG status_code, LPCWSTR status_text) {
|
| - static const int kDefaultHttpRedirectCode = 302;
|
| -
|
| + DCHECK_EQ(thread_, PlatformThread::CurrentId());
|
| switch (status_code) {
|
| case BINDSTATUS_REDIRECTING: {
|
| + DLOG(INFO) << "URL: " << url() << " redirected to " << status_text;
|
| // Fetch the redirect status as they aren't all equal (307 in particular
|
| // retains the HTTP request verb).
|
| - // We assume that valid redirect codes are 301, 302, 303 and 307. If we
|
| - // receive anything else we would abort the request which would
|
| - // eventually result in the request getting cancelled in Chrome.
|
| - int redirect_status = GetHttpResponseStatus();
|
| - DCHECK(status_text != NULL);
|
| - DLOG(INFO) << "URL: " << url() << " redirected to "
|
| - << status_text;
|
| - redirect_url_ = status_text;
|
| - // At times we receive invalid redirect codes like 0, 200, etc. We
|
| - // default to 302 in this case.
|
| - if (!net::HttpResponseHeaders::IsRedirectResponseCode(redirect_status))
|
| - redirect_status = kDefaultHttpRedirectCode;
|
| - redirect_status_ = redirect_status;
|
| - // Chrome should decide whether a redirect has to be followed. To achieve
|
| - // this we send over a fake response to Chrome and abort the redirect.
|
| - std::string headers = GetHttpHeaders();
|
| - OnResponse(0, UTF8ToWide(headers).c_str(), NULL, NULL);
|
| - ignore_redirect_stop_binding_error_ = true;
|
| - DCHECK(binding_ != NULL);
|
| - if (binding_) {
|
| - binding_->Abort();
|
| - binding_ = NULL;
|
| - }
|
| + int http_code = GetHttpResponseStatus();
|
| + status_.SetRedirected(http_code, WideToUTF8(status_text));
|
| + // Abort. We will inform Chrome in OnStopBinding callback.
|
| + binding_->Abort();
|
| return E_ABORT;
|
| }
|
|
|
| @@ -434,39 +384,51 @@
|
| }
|
|
|
| STDMETHODIMP UrlmonUrlRequest::OnStopBinding(HRESULT result, LPCWSTR error) {
|
| - DCHECK(worker_thread_ != NULL);
|
| - DCHECK_EQ(PlatformThread::CurrentId(), worker_thread_->thread_id());
|
| -
|
| + DCHECK_EQ(thread_, PlatformThread::CurrentId());
|
| DLOG(INFO) << StringPrintf("URL: %s Obj: %X", url().c_str(), this) <<
|
| - " - Request stopped, Result: " << std::hex << result <<
|
| - " Status: " << status_.status();
|
| + " - Request stopped, Result: " << std::hex << result;
|
| + DCHECK(status_.get_state() == Status::WORKING ||
|
| + status_.get_state() == Status::ABORTING);
|
| + Status::State state = status_.get_state();
|
|
|
| - if (FAILED(result)) {
|
| - status_.set_status(URLRequestStatus::FAILED);
|
| - status_.set_os_error(HresultToNetError(result));
|
| - EndRequest();
|
| - } else {
|
| - status_.set_status(URLRequestStatus::SUCCESS);
|
| - status_.set_os_error(0);
|
| - ReleaseBindings();
|
| - // In most cases we receive the end request notification from Chrome.
|
| - // However at times requests can complete without us receiving any
|
| - // data. In this case we need to inform Chrome that this request has been
|
| - // completed to prevent Chrome from waiting forever for data for this
|
| - // request.
|
| - if (pending_read_size_) {
|
| - pending_read_size_ = 0;
|
| - OnResponseEnd(status_);
|
| + // Mark we a are done.
|
| + status_.Done();
|
| +
|
| + if (state == Status::WORKING) {
|
| + status_.set_result(result);
|
| +
|
| + // The code below seems easy but it is not. :)
|
| + // we cannot have pending read and data_avail at the same time.
|
| + DCHECK(!(pending_read_size_ > 0 && cached_data_.is_valid()));
|
| +
|
| + // We have some data, but Chrome has not yet read it. Wait until Chrome
|
| + // read the remaining of the data and then send the error/success code.
|
| + if (cached_data_.is_valid()) {
|
| + ReleaseBindings();
|
| + return S_OK;
|
| }
|
| +
|
| + NotifyDelegateAndDie();
|
| + return S_OK;
|
| }
|
|
|
| + // Status::ABORTING
|
| + if (status_.was_redirected()) {
|
| + // Just release bindings here. Chrome will issue EndRequest(request_id)
|
| + // after processing headers we had provided.
|
| + std::string headers = GetHttpHeaders();
|
| + OnResponse(0, UTF8ToWide(headers).c_str(), NULL, NULL);
|
| + ReleaseBindings();
|
| + return S_OK;
|
| + }
|
| +
|
| + // Stop invoked.
|
| + NotifyDelegateAndDie();
|
| return S_OK;
|
| }
|
|
|
| STDMETHODIMP UrlmonUrlRequest::GetBindInfo(DWORD* bind_flags,
|
| BINDINFO *bind_info) {
|
| - DCHECK(worker_thread_ != NULL);
|
| - DCHECK_EQ(PlatformThread::CurrentId(), worker_thread_->thread_id());
|
|
|
| if ((bind_info == NULL) || (bind_info->cbSize == 0) || (bind_flags == NULL))
|
| return E_INVALIDARG;
|
| @@ -485,9 +447,8 @@
|
| upload_data = true;
|
| } else {
|
| NOTREACHED() << "Unknown HTTP method.";
|
| - status_.set_status(URLRequestStatus::FAILED);
|
| - status_.set_os_error(net::ERR_METHOD_NOT_SUPPORTED);
|
| - EndRequest();
|
| + status_.set_result(URLRequestStatus::FAILED, net::ERR_METHOD_NOT_SUPPORTED);
|
| + NotifyDelegateAndDie();
|
| return E_FAIL;
|
| }
|
|
|
| @@ -519,9 +480,6 @@
|
| STDMETHODIMP UrlmonUrlRequest::OnDataAvailable(DWORD flags, DWORD size,
|
| FORMATETC* formatetc,
|
| STGMEDIUM* storage) {
|
| - DCHECK(worker_thread_ != NULL);
|
| - DCHECK_EQ(PlatformThread::CurrentId(), worker_thread_->thread_id());
|
| -
|
| DLOG(INFO) << StringPrintf("URL: %s Obj: %X - Bytes available: %d",
|
| url().c_str(), this, size);
|
|
|
| @@ -563,7 +521,6 @@
|
| }
|
|
|
| if (BSCF_LASTDATANOTIFICATION & flags) {
|
| - status_.set_status(URLRequestStatus::SUCCESS);
|
| DLOG(INFO) << StringPrintf("URL: %s Obj: %X", url().c_str(), this) <<
|
| " - end of data.";
|
| }
|
| @@ -581,9 +538,7 @@
|
| STDMETHODIMP UrlmonUrlRequest::BeginningTransaction(const wchar_t* url,
|
| const wchar_t* current_headers, DWORD reserved,
|
| wchar_t** additional_headers) {
|
| - DCHECK(worker_thread_ != NULL);
|
| - DCHECK_EQ(PlatformThread::CurrentId(), worker_thread_->thread_id());
|
| -
|
| + DCHECK_EQ(thread_, PlatformThread::CurrentId());
|
| if (!additional_headers) {
|
| NOTREACHED();
|
| return E_POINTER;
|
| @@ -592,7 +547,7 @@
|
| DLOG(INFO) << "URL: " << url << " Obj: " << std::hex << this <<
|
| " - Request headers: \n" << current_headers;
|
|
|
| - if (!binding_) {
|
| + if (status_.get_state() == Status::ABORTING) {
|
| // At times the BINDSTATUS_REDIRECTING notification which is sent to the
|
| // IBindStatusCallback interface does not have an accompanying HTTP
|
| // redirect status code, i.e. the attempt to query the HTTP status code
|
| @@ -602,7 +557,6 @@
|
| // However urlmon still tries to establish a transaction with the
|
| // redirected URL which confuses the web server.
|
| // Fix is to abort the attempted transaction.
|
| - DCHECK(ignore_redirect_stop_binding_error_);
|
| DLOG(WARNING) << __FUNCTION__
|
| << ": Aborting connection to URL:"
|
| << url
|
| @@ -649,13 +603,10 @@
|
| STDMETHODIMP UrlmonUrlRequest::OnResponse(DWORD dwResponseCode,
|
| const wchar_t* response_headers, const wchar_t* request_headers,
|
| wchar_t** additional_headers) {
|
| - DCHECK(worker_thread_ != NULL);
|
| DLOG(INFO) << __FUNCTION__ << " " << url() << std::endl << " headers: " <<
|
| std::endl << response_headers;
|
| - DCHECK_EQ(PlatformThread::CurrentId(), worker_thread_->thread_id());
|
| -
|
| + DCHECK_EQ(thread_, PlatformThread::CurrentId());
|
| if (!binding_) {
|
| - DCHECK(redirect_url_.empty() == false);
|
| DLOG(WARNING) << __FUNCTION__
|
| << ": Ignoring as the binding was aborted due to a redirect";
|
| return S_OK;
|
| @@ -679,7 +630,7 @@
|
|
|
| // NOTE(slightlyoff): We don't use net::HttpResponseHeaders here because
|
| // of lingering ICU/base_noicu issues.
|
| - if (frame_busting_enabled_) {
|
| + if (enable_frame_busting_) {
|
| std::string http_headers = net::HttpUtil::AssembleRawHeaders(
|
| raw_headers.c_str(), raw_headers.length());
|
| if (http_utils::HasFrameBustingHeader(http_headers)) {
|
| @@ -689,36 +640,44 @@
|
| }
|
| }
|
|
|
| - std::wstring url_for_persistent_cookies =
|
| - redirect_url_.empty() ? UTF8ToWide(url()) : redirect_url_;
|
|
|
| + std::string url_for_persistent_cookies;
|
| std::string persistent_cookies;
|
|
|
| - DWORD cookie_size = 0; // NOLINT
|
| - // Note that there's really no way for us here to distinguish session cookies
|
| - // from persistent cookies here. Session cookies should get filtered
|
| - // out on the chrome side as to not be added again.
|
| - InternetGetCookie(url_for_persistent_cookies.c_str(), NULL, NULL,
|
| - &cookie_size);
|
| - if (cookie_size) {
|
| - scoped_array<wchar_t> cookies(new wchar_t[cookie_size + 1]);
|
| - if (!InternetGetCookie(url_for_persistent_cookies.c_str(), NULL,
|
| - cookies.get(), &cookie_size)) {
|
| - NOTREACHED() << "InternetGetCookie failed. Error: " << GetLastError();
|
| - } else {
|
| - persistent_cookies = WideToUTF8(cookies.get());
|
| + if (status_.was_redirected())
|
| + url_for_persistent_cookies = status_.get_redirection().utf8_url;
|
| +
|
| + if (url_for_persistent_cookies.empty())
|
| + url_for_persistent_cookies = url();
|
| +
|
| + // Grab cookies for the specific Url from WININET.
|
| + {
|
| + DWORD cookie_size = 0; // NOLINT
|
| + std::wstring url = UTF8ToWide(url_for_persistent_cookies);
|
| +
|
| + // Note that there's really no way for us here to distinguish session
|
| + // cookies from persistent cookies here. Session cookies should get
|
| + // filtered out on the chrome side as to not be added again.
|
| + InternetGetCookie(url.c_str(), NULL, NULL, &cookie_size);
|
| + if (cookie_size) {
|
| + scoped_array<wchar_t> cookies(new wchar_t[cookie_size + 1]);
|
| + if (!InternetGetCookie(url.c_str(), NULL, cookies.get(), &cookie_size)) {
|
| + NOTREACHED() << "InternetGetCookie failed. Error: " << GetLastError();
|
| + } else {
|
| + persistent_cookies = WideToUTF8(cookies.get());
|
| + }
|
| }
|
| }
|
|
|
| - OnResponseStarted("",
|
| - raw_headers.c_str(),
|
| - 0,
|
| - base::Time(),
|
| + // Inform the delegate.
|
| + delegate_->OnResponseStarted(id(),
|
| + "", // mime_type
|
| + raw_headers.c_str(), // headers
|
| + 0, // size
|
| + base::Time(), // last_modified
|
| persistent_cookies,
|
| - redirect_url_.empty() ? std::string() :
|
| - WideToUTF8(redirect_url_),
|
| - redirect_status_);
|
| -
|
| + status_.get_redirection().utf8_url,
|
| + status_.get_redirection().http_code);
|
| return S_OK;
|
| }
|
|
|
| @@ -818,41 +777,6 @@
|
| return hr;
|
| }
|
|
|
| -HRESULT UrlmonUrlRequest::ConnectToExistingMoniker(IMoniker* moniker,
|
| - IBindCtx* context,
|
| - const std::wstring& url) {
|
| - if (!moniker || url.empty()) {
|
| - NOTREACHED() << "Invalid arguments";
|
| - return E_INVALIDARG;
|
| - }
|
| -
|
| - DCHECK(moniker_.get() == NULL);
|
| - DCHECK(bind_context_.get() == NULL);
|
| -
|
| - CComObject<WrappedBindContext>* bind_context = NULL;
|
| - HRESULT hr = CComObject<WrappedBindContext>::CreateInstance(&bind_context);
|
| - if (FAILED(hr)) {
|
| - NOTREACHED() << "Failed to instantiate wrapped bind context. Error:" << hr;
|
| - return hr;
|
| - }
|
| -
|
| - bind_context->AddRef();
|
| - hr = bind_context->Initialize(context);
|
| - DCHECK(SUCCEEDED(hr));
|
| -
|
| - hr = bind_context->QueryInterface(bind_context_.Receive());
|
| - bind_context->Release();
|
| -
|
| - if (FAILED(hr)) {
|
| - NOTREACHED() << "Failed to QI for IBindCtx on wrapper. Error:" << hr;
|
| - return hr;
|
| - }
|
| -
|
| - moniker_ = moniker;
|
| - set_url(WideToUTF8(url));
|
| - return S_OK;
|
| -}
|
| -
|
| HRESULT UrlmonUrlRequest::StartAsyncDownload() {
|
| HRESULT hr = E_FAIL;
|
| if (moniker_.get() == NULL) {
|
| @@ -874,6 +798,12 @@
|
| ScopedComPtr<IStream> stream;
|
| hr = moniker_->BindToStorage(bind_context_, NULL, __uuidof(IStream),
|
| reinterpret_cast<void**>(stream.Receive()));
|
| + // Even if hr == S_OK, binding_ could be NULL if the entire request
|
| + // finish synchronously but then we still get all the callbacks etc.
|
| + if (hr == S_OK) {
|
| + DCHECK(binding_ != NULL || status_.get_state() == Status::DONE);
|
| + }
|
| +
|
| if (FAILED(hr)) {
|
| // TODO(joshia): Look into. This currently fails for:
|
| // http://user2:secret@localhost:1337/auth-basic?set-cookie-if-challenged
|
| @@ -891,48 +821,17 @@
|
| return hr;
|
| }
|
|
|
| -void UrlmonUrlRequest::EndRequest() {
|
| +void UrlmonUrlRequest::NotifyDelegateAndDie() {
|
| + DCHECK_EQ(thread_, PlatformThread::CurrentId());
|
| DLOG(INFO) << __FUNCTION__;
|
| -
|
| - // In case of a redirect notification we prevent urlmon from following the
|
| - // redirect and rely on Chrome, in which case AutomationMsg_RequestEnd
|
| - // IPC will be sent over by Chrome to end this request.
|
| - if (!ignore_redirect_stop_binding_error_) {
|
| - // Special case. If the last request was a redirect and the current OS
|
| - // error value is E_ACCESSDENIED, that means an unsafe redirect was
|
| - // attempted. In that case, correct the OS error value to be the more
|
| - // specific ERR_UNSAFE_REDIRECT error value.
|
| - if (!status_.is_success() && status_.os_error() == net::ERR_ACCESS_DENIED) {
|
| - int status = GetHttpResponseStatus();
|
| - if (status >= 300 && status < 400) {
|
| - redirect_status_ = status; // store the latest redirect status value.
|
| - status_.set_os_error(net::ERR_UNSAFE_REDIRECT);
|
| - }
|
| - }
|
| - OnResponseEnd(status_);
|
| - } else {
|
| - ignore_redirect_stop_binding_error_ = false;
|
| + PluginUrlRequestDelegate* delegate = delegate_;
|
| + delegate_ = NULL;
|
| + ReleaseBindings();
|
| + if (delegate) {
|
| + delegate->OnResponseEnd(id(), status_.get_result());
|
| }
|
| -
|
| - ReleaseBindings();
|
| - // Remove the request mapping and release the outstanding reference to us in
|
| - // the context of the UI thread.
|
| - // We should not access any members of the UrlmonUrlRequest object after this
|
| - // as the object would be deleted.
|
| - PostTask(FROM_HERE,
|
| - NewRunnableMethod(this, &UrlmonUrlRequest::EndRequestInternal));
|
| }
|
|
|
| -void UrlmonUrlRequest::EndRequestInternal() {
|
| - // The request object could have been removed from the map in the
|
| - // OnRequestEnd callback which executes on receiving the
|
| - // AutomationMsg_RequestEnd IPC from Chrome.
|
| - request_handler()->RemoveRequest(this);
|
| - // The current instance could get destroyed in the context of DestroyWindow.
|
| - // We should not access the object after this.
|
| - DestroyWindow();
|
| -}
|
| -
|
| int UrlmonUrlRequest::GetHttpResponseStatus() const {
|
| if (binding_ == NULL) {
|
| DLOG(WARNING) << "GetHttpResponseStatus - no binding_";
|
| @@ -1096,3 +995,236 @@
|
| }
|
| return ret;
|
| }
|
| +
|
| +
|
| +bool UrlmonUrlRequestManager::IsThreadSafe() {
|
| + return true;
|
| +}
|
| +
|
| +void UrlmonUrlRequestManager::UseMonikerForUrl(IMoniker* moniker,
|
| + IBindCtx* bind_ctx,
|
| + const std::wstring& url) {
|
| + DCHECK(NULL == moniker_for_url_.get());
|
| + moniker_for_url_.reset(new MonikerForUrl());
|
| + moniker_for_url_->moniker = moniker;
|
| + moniker_for_url_->url = url;
|
| +
|
| + CComObject<WrappedBindContext>* ctx = NULL;
|
| + CComObject<WrappedBindContext>::CreateInstance(&ctx);
|
| + ctx->Initialize(bind_ctx);
|
| + ctx->QueryInterface(moniker_for_url_->bind_ctx.Receive());
|
| + DCHECK(moniker_for_url_->bind_ctx.get());
|
| +}
|
| +
|
| +void UrlmonUrlRequestManager::StartRequest(int request_id,
|
| + const IPC::AutomationURLRequest& request_info) {
|
| + if (stopping_) {
|
| + return;
|
| + }
|
| +
|
| + if (!worker_thread_.IsRunning())
|
| + worker_thread_.Start();
|
| +
|
| + MonikerForUrl* use_moniker = NULL;
|
| + if (moniker_for_url_.get()) {
|
| + if (GURL(moniker_for_url_->url) == GURL(request_info.url)) {
|
| + use_moniker = moniker_for_url_.release();
|
| + }
|
| + }
|
| +
|
| + worker_thread_.message_loop()->PostTask(FROM_HERE,
|
| + NewRunnableMethod(this, &UrlmonUrlRequestManager::StartRequestWorker,
|
| + request_id, request_info, use_moniker));
|
| +}
|
| +
|
| +void UrlmonUrlRequestManager::StartRequestWorker(int request_id,
|
| + const IPC::AutomationURLRequest& request_info,
|
| + MonikerForUrl* use_moniker) {
|
| + DCHECK_EQ(worker_thread_.thread_id(), PlatformThread::CurrentId());
|
| + scoped_ptr<MonikerForUrl> moniker_for_url(use_moniker);
|
| +
|
| + if (stopping_)
|
| + return;
|
| +
|
| + DCHECK(LookupRequest(request_id).get() == NULL);
|
| +
|
| + CComObject<UrlmonUrlRequest>* new_request = NULL;
|
| + CComObject<UrlmonUrlRequest>::CreateInstance(&new_request);
|
| +
|
| + new_request->Initialize(static_cast<PluginUrlRequestDelegate*>(this),
|
| + request_id,
|
| + request_info.url,
|
| + request_info.method,
|
| + request_info.referrer,
|
| + request_info.extra_request_headers,
|
| + request_info.upload_data,
|
| + enable_frame_busting_);
|
| +
|
| + // Shall we use an existing moniker?
|
| + if (moniker_for_url.get()) {
|
| + new_request->ConnectToExistingMoniker(moniker_for_url->moniker,
|
| + moniker_for_url->bind_ctx,
|
| + moniker_for_url->url);
|
| + }
|
| +
|
| + DCHECK(LookupRequest(request_id).get() == NULL);
|
| + request_map_[request_id] = new_request;
|
| + map_empty_.Reset();
|
| +
|
| + new_request->Start();
|
| +}
|
| +
|
| +void UrlmonUrlRequestManager::ReadRequest(int request_id, int bytes_to_read) {
|
| + if (stopping_)
|
| + return;
|
| +
|
| + worker_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this,
|
| + &UrlmonUrlRequestManager::ReadRequestWorker, request_id, bytes_to_read));
|
| +}
|
| +
|
| +void UrlmonUrlRequestManager::ReadRequestWorker(int request_id,
|
| + int bytes_to_read) {
|
| + DCHECK_EQ(worker_thread_.thread_id(), PlatformThread::CurrentId());
|
| + scoped_refptr<UrlmonUrlRequest> request = LookupRequest(request_id);
|
| + // if zero, it may just have had network error.
|
| + if (request) {
|
| + request->Read(bytes_to_read);
|
| + }
|
| +}
|
| +
|
| +void UrlmonUrlRequestManager::EndRequest(int request_id) {
|
| + if (stopping_)
|
| + return;
|
| +
|
| + worker_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this,
|
| + &UrlmonUrlRequestManager::EndRequestWorker, request_id));
|
| +}
|
| +
|
| +void UrlmonUrlRequestManager::EndRequestWorker(int request_id) {
|
| + DCHECK_EQ(worker_thread_.thread_id(), PlatformThread::CurrentId());
|
| + scoped_refptr<UrlmonUrlRequest> request = LookupRequest(request_id);
|
| + if (request) {
|
| + request->Stop();
|
| + }
|
| +}
|
| +
|
| +void UrlmonUrlRequestManager::StopAll() {
|
| + if (stopping_)
|
| + return;
|
| +
|
| + stopping_ = true;
|
| +
|
| + if (!worker_thread_.IsRunning())
|
| + return;
|
| +
|
| + worker_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this,
|
| + &UrlmonUrlRequestManager::StopAllWorker));
|
| +
|
| + // Note we may not call worker_thread_.Stop() here. The MessageLoop's quit
|
| + // task will be serialized after request::Stop tasks, but requests may
|
| + // not quit immediately. CoUninitialize has a modal message loop, but it
|
| + // does not help in this case.
|
| + // Normally we call binding->Abort() and expect OnStopBinding() callback
|
| + // where we inform UrlmonUrlRequestManager that request is dead.
|
| + // The problem is that while waiting for OnStopBinding(), Quit Task may be
|
| + // picked up and executed, thus exiting the thread.
|
| + map_empty_.Wait();
|
| + worker_thread_.Stop();
|
| + DCHECK_EQ(0, UrlmonUrlRequest::instance_count_);
|
| +}
|
| +
|
| +void UrlmonUrlRequestManager::StopAllWorker() {
|
| + DCHECK_EQ(worker_thread_.thread_id(), PlatformThread::CurrentId());
|
| + DCHECK_EQ(true, stopping_);
|
| +
|
| + std::vector<scoped_refptr<UrlmonUrlRequest> > request_list;
|
| + // We copy the pending requests into a temporary vector as the Stop
|
| + // function in the request could also try to delete the request from
|
| + // the request map and the iterator could end up being invalid.
|
| + for (RequestMap::iterator it = request_map_.begin();
|
| + it != request_map_.end(); ++it) {
|
| + DCHECK(it->second != NULL);
|
| + request_list.push_back(it->second);
|
| + }
|
| +
|
| + for (std::vector<scoped_refptr<UrlmonUrlRequest> >::size_type index = 0;
|
| + index < request_list.size(); ++index) {
|
| + request_list[index]->Stop();
|
| + }
|
| +}
|
| +
|
| +void UrlmonUrlRequestManager::OnResponseStarted(int request_id,
|
| + const char* mime_type, const char* headers, int size,
|
| + base::Time last_modified, const std::string& peristent_cookies,
|
| + const std::string& redirect_url, int redirect_status) {
|
| + DCHECK_EQ(worker_thread_.thread_id(), PlatformThread::CurrentId());
|
| + DCHECK(LookupRequest(request_id).get() != NULL);
|
| + delegate_->OnResponseStarted(request_id, mime_type, headers, size,
|
| + last_modified, peristent_cookies, redirect_url, redirect_status);
|
| +}
|
| +
|
| +void UrlmonUrlRequestManager::OnReadComplete(int request_id, const void* buffer,
|
| + int len) {
|
| + DCHECK_EQ(worker_thread_.thread_id(), PlatformThread::CurrentId());
|
| + DCHECK(LookupRequest(request_id).get() != NULL);
|
| + delegate_->OnReadComplete(request_id, buffer, len);
|
| +}
|
| +
|
| +void UrlmonUrlRequestManager::OnResponseEnd(int request_id,
|
| + const URLRequestStatus& status) {
|
| + DCHECK_EQ(worker_thread_.thread_id(), PlatformThread::CurrentId());
|
| + RequestMap::size_type n = request_map_.erase(request_id);
|
| + DCHECK_EQ(1, n);
|
| +
|
| + if (request_map_.size() == 0)
|
| + map_empty_.Signal();
|
| +
|
| + // Inform delegate unless the request has been explicitly cancelled.
|
| + if (status.status() != URLRequestStatus::CANCELED)
|
| + delegate_->OnResponseEnd(request_id, status);
|
| +}
|
| +
|
| +scoped_refptr<UrlmonUrlRequest> UrlmonUrlRequestManager::LookupRequest(
|
| + int request_id) {
|
| + RequestMap::iterator it = request_map_.find(request_id);
|
| + if (request_map_.end() != it)
|
| + return it->second;
|
| + return NULL;
|
| +}
|
| +
|
| +UrlmonUrlRequestManager::UrlmonUrlRequestManager()
|
| + : stopping_(false), worker_thread_("UrlMon fetch thread"),
|
| + map_empty_(true, true) {
|
| +}
|
| +
|
| +UrlmonUrlRequestManager::~UrlmonUrlRequestManager() {
|
| + StopAll();
|
| +}
|
| +
|
| +// Called from UI thread.
|
| +void UrlmonUrlRequestManager::StealMonikerFromRequest(int request_id,
|
| + IMoniker** moniker) {
|
| + if (stopping_)
|
| + return;
|
| +
|
| + base::WaitableEvent done(true, false);
|
| + worker_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this,
|
| + &UrlmonUrlRequestManager::StealMonikerFromRequestWorker,
|
| + request_id, moniker, &done));
|
| +
|
| + // Wait until moniker is grabbed from a request in the worker thread.
|
| + done.Wait();
|
| +}
|
| +
|
| +void UrlmonUrlRequestManager::StealMonikerFromRequestWorker(int request_id,
|
| + IMoniker** moniker, base::WaitableEvent* done) {
|
| + if (!stopping_) {
|
| + scoped_refptr<UrlmonUrlRequest> request = LookupRequest(request_id);
|
| + if (request) {
|
| + request->StealMoniker(moniker);
|
| + request->Stop();
|
| + }
|
| + }
|
| +
|
| + done->Signal();
|
| +}
|
|
|