summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome_frame/chrome_frame_activex_base.h55
-rw-r--r--chrome_frame/chrome_frame_automation.cc2
-rw-r--r--chrome_frame/chrome_frame_automation.h36
-rw-r--r--chrome_frame/chrome_frame_delegate.h45
-rw-r--r--chrome_frame/plugin_url_request.h3
-rw-r--r--chrome_frame/urlmon_url_request.cc110
-rw-r--r--chrome_frame/urlmon_url_request.h31
7 files changed, 210 insertions, 72 deletions
diff --git a/chrome_frame/chrome_frame_activex_base.h b/chrome_frame/chrome_frame_activex_base.h
index 651ecbf..bbd88dc 100644
--- a/chrome_frame/chrome_frame_activex_base.h
+++ b/chrome_frame/chrome_frame_activex_base.h
@@ -150,13 +150,19 @@ class ATL_NO_VTABLE ChromeFrameActivexBase :
public IPropertyNotifySinkCP<T>,
public CComCoClass<T, &class_id>,
public CComControl<T>,
- public ChromeFramePlugin<T> {
+ public ChromeFramePlugin<T>,
+ public TaskMarshallerThroughWindowsMessages<
+ ChromeFrameActivexBase<T, class_id> > {
protected:
typedef std::set<ScopedComPtr<IDispatch> > EventHandlers;
+ typedef TaskMarshallerThroughWindowsMessages<
+ ChromeFrameActivexBase<T, class_id> > TaskMarshaller;
+ typedef ChromeFrameActivexBase<T, class_id> Base;
public:
ChromeFrameActivexBase()
- : ready_state_(READYSTATE_UNINITIALIZED) {
+ : ready_state_(READYSTATE_UNINITIALIZED),
+ worker_thread_("ChromeFrameWorker_Thread") {
m_bWindowOnly = TRUE;
}
@@ -197,6 +203,7 @@ BEGIN_MSG_MAP(ChromeFrameActivexBase)
MESSAGE_HANDLER(WM_CREATE, OnCreate)
CHAIN_MSG_MAP(ChromeFramePlugin<T>)
CHAIN_MSG_MAP(CComControl<T>)
+ CHAIN_MSG_MAP(TaskMarshaller)
DEFAULT_REFLECTION_HANDLER()
END_MSG_MAP()
@@ -231,6 +238,12 @@ END_MSG_MAP()
IE_8,
IE_8 + 1);
}
+
+ base::Thread::Options options;
+ options.message_loop_type = MessageLoop::TYPE_UI;
+ worker_thread_.StartWithOptions(options);
+ worker_thread_.message_loop()->PostTask(
+ FROM_HERE, NewRunnableMethod(this, &Base::OnWorkerStart));
return S_OK;
}
@@ -302,10 +315,21 @@ END_MSG_MAP()
// of this template should implement this method based on how
// it "feels" from a security perspective. If it's hosted in another
// scriptable document, return true, else false.
- virtual bool is_frame_busting_enabled() const {
+ bool is_frame_busting_enabled() const {
return true;
}
+ // Needed to support PostTask.
+ static bool ImplementsThreadSafeReferenceCounting() {
+ return true;
+ }
+
+ virtual void OnFinalMessage(HWND) {
+ worker_thread_.message_loop()->PostTask(
+ FROM_HERE, NewRunnableMethod(this, &Base::OnWorkerStop));
+ worker_thread_.Stop();
+ }
+
protected:
virtual void OnTabbedOut(int tab_handle, bool reverse) {
DCHECK(m_bInPlaceActive);
@@ -419,12 +443,13 @@ END_MSG_MAP()
DCHECK(request.get() != NULL);
- if (request->Initialize(automation_client_.get(), tab_handle, request_id,
- request_info.url, request_info.method,
- request_info.referrer,
- request_info.extra_request_headers,
- request_info.upload_data.get(),
- static_cast<T*>(this)->is_frame_busting_enabled())) {
+ if (request->Initialize(
+ automation_client_.get(), tab_handle, request_id, request_info.url,
+ request_info.method, request_info.referrer,
+ request_info.extra_request_headers, request_info.upload_data.get(),
+ static_cast<T*>(this)->is_frame_busting_enabled())) {
+ request->set_worker_thread(&worker_thread_);
+ request->set_task_marshaller(this);
// If Start is successful, it will add a self reference.
request->Start();
request->set_parent_window(m_hWnd);
@@ -941,6 +966,16 @@ END_MSG_MAP()
}
protected:
+ // The following functions are called to initialize and uninitialize the
+ // worker thread.
+ void OnWorkerStart() {
+ CoInitialize(NULL);
+ }
+
+ void OnWorkerStop() {
+ CoUninitialize();
+ }
+
ScopedBstr url_;
ScopedComPtr<IOleDocumentSite> doc_site_;
@@ -963,6 +998,8 @@ END_MSG_MAP()
// The UrlmonUrlRequest instance instantiated for downloading the base URL.
scoped_refptr<CComObject<UrlmonUrlRequest> > base_url_request_;
+
+ base::Thread worker_thread_;
};
#endif // CHROME_FRAME_CHROME_FRAME_ACTIVEX_BASE_H_
diff --git a/chrome_frame/chrome_frame_automation.cc b/chrome_frame/chrome_frame_automation.cc
index 2d22bbd..d84cbd3 100644
--- a/chrome_frame/chrome_frame_automation.cc
+++ b/chrome_frame/chrome_frame_automation.cc
@@ -1000,8 +1000,8 @@ void ChromeFrameAutomationClient::RemoveRequest(
PluginUrlRequest* request = LookupRequest(request_id);
if (request) {
if (abort) {
+ // The request object will get removed asynchronously.
request->Stop();
- DCHECK(request_map_.end() == request_map_.find(request_id));
} else {
request_map_.erase(request_id);
}
diff --git a/chrome_frame/chrome_frame_automation.h b/chrome_frame/chrome_frame_automation.h
index 6eca88d..f2120e2 100644
--- a/chrome_frame/chrome_frame_automation.h
+++ b/chrome_frame/chrome_frame_automation.h
@@ -144,35 +144,6 @@ class ProxyFactory {
int uma_send_interval_;
};
-// T is expected to be something CWindowImpl derived, or at least to have
-// PostMessage(UINT, WPARAM) method. Do not forget to CHAIN_MSG_MAP
-template <class T> class TaskMarshallerThroughWindowsMessages {
- public:
- void PostTask(const tracked_objects::Location& from_here, Task* task) {
- task->SetBirthPlace(from_here);
- T* this_ptr = static_cast<T*>(this);
- if (this_ptr->IsWindow()) {
- this_ptr->PostMessage(MSG_EXECUTE_TASK, reinterpret_cast<WPARAM>(task));
- } else {
- DLOG(INFO) << "Dropping MSG_EXECUTE_TASK message for destroyed window.";
- }
- }
-
- BEGIN_MSG_MAP(PostMessageMarshaller)
- MESSAGE_HANDLER(MSG_EXECUTE_TASK, ExecuteTask)
- END_MSG_MAP()
-
- private:
- enum { MSG_EXECUTE_TASK = WM_APP + 6 };
- inline LRESULT ExecuteTask(UINT, WPARAM wparam, LPARAM,
- BOOL& handled) { // NOLINT
- Task* task = reinterpret_cast<Task*>(wparam);
- task->Run();
- delete task;
- return 0;
- }
-};
-
// Handles all automation requests initiated from the chrome frame objects.
// These include the chrome tab/chrome frame activex/chrome frame npapi
// plugin objects.
@@ -296,6 +267,13 @@ class ChromeFrameAutomationClient
void SetPageFontSize(enum AutomationPageFontSize);
+ // Dummy reference counting functions to enable us to use the
+ // TaskMarshallerThroughWindowsMessages functionality. At this point we don't
+ // need to ensure that any tasks executed on us grab a reference to ensure
+ // that the instance remains valid.
+ void AddRef() {}
+ void Release() {}
+
protected:
// ChromeFrameAutomationProxy::LaunchDelegate implementation.
virtual void LaunchComplete(ChromeFrameAutomationProxy* proxy,
diff --git a/chrome_frame/chrome_frame_delegate.h b/chrome_frame/chrome_frame_delegate.h
index e11f099..bbdc99e 100644
--- a/chrome_frame/chrome_frame_delegate.h
+++ b/chrome_frame/chrome_frame_delegate.h
@@ -5,6 +5,9 @@
#ifndef CHROME_FRAME_CHROME_FRAME_DELEGATE_H_
#define CHROME_FRAME_CHROME_FRAME_DELEGATE_H_
+#include <atlbase.h>
+#include <atlwin.h>
+
#include "chrome/test/automation/automation_messages.h"
#include "ipc/ipc_message.h"
@@ -12,7 +15,6 @@
// implementations.
class ChromeFrameDelegate {
public:
-
typedef HWND WindowType;
virtual WindowType GetWindow() const = 0;
@@ -103,4 +105,45 @@ class ChromeFrameDelegateImpl : public ChromeFrameDelegate {
virtual void OnGoToHistoryEntryOffset(int tab_handle, int offset) {}
};
+// This interface enables tasks to be marshalled to desired threads.
+class TaskMarshaller {
+ public:
+ virtual void PostTask(const tracked_objects::Location& from_here,
+ Task* task) = 0;
+};
+
+// T is expected to be something CWindowImpl derived, or at least to have
+// PostMessage(UINT, WPARAM) method. Do not forget to CHAIN_MSG_MAP
+template <class T> class TaskMarshallerThroughWindowsMessages
+ : public TaskMarshaller {
+ public:
+ virtual void PostTask(const tracked_objects::Location& from_here,
+ Task* task) {
+ task->SetBirthPlace(from_here);
+ T* this_ptr = static_cast<T*>(this);
+ if (this_ptr->IsWindow()) {
+ this_ptr->AddRef();
+ this_ptr->PostMessage(MSG_EXECUTE_TASK, reinterpret_cast<WPARAM>(task));
+ } else {
+ DLOG(INFO) << "Dropping MSG_EXECUTE_TASK message for destroyed window.";
+ }
+ }
+
+ BEGIN_MSG_MAP(PostMessageMarshaller)
+ MESSAGE_HANDLER(MSG_EXECUTE_TASK, ExecuteTask)
+ END_MSG_MAP()
+
+ private:
+ enum { MSG_EXECUTE_TASK = WM_APP + 6 };
+ inline LRESULT ExecuteTask(UINT, WPARAM wparam, LPARAM,
+ BOOL& handled) { // NOLINT
+ Task* task = reinterpret_cast<Task*>(wparam);
+ task->Run();
+ delete task;
+ T* this_ptr = static_cast<T*>(this);
+ this_ptr->Release();
+ return 0;
+ }
+};
+
#endif // CHROME_FRAME_CHROME_FRAME_DELEGATE_H_
diff --git a/chrome_frame/plugin_url_request.h b/chrome_frame/plugin_url_request.h
index fb28c77..8c38b92 100644
--- a/chrome_frame/plugin_url_request.h
+++ b/chrome_frame/plugin_url_request.h
@@ -13,6 +13,7 @@
#include "net/base/upload_data.h"
#include "net/url_request/url_request_status.h"
#include "base/ref_counted.h"
+#include "chrome_frame/chrome_frame_delegate.h"
class PluginUrlRequest;
@@ -52,7 +53,7 @@ class PluginUrlRequest : public UrlRequestReference {
// These cookies are sent when we receive a response for every URL request
// initiated by Chrome. Ideally we should only send cookies for the top level
// URL and any subframes. However we don't receive information from Chrome
- // about the context for a URL, i.e. whether it is a subframe, etc.
+ // about the context for a URL, i.e. whether it is a subframe, etc.
// Additionally cookies for a URL should be sent once for the page. This
// is not done now as it is difficult to track URLs, specifically if they
// are redirected, etc.
diff --git a/chrome_frame/urlmon_url_request.cc b/chrome_frame/urlmon_url_request.cc
index 4d91607..d64cc62 100644
--- a/chrome_frame/urlmon_url_request.cc
+++ b/chrome_frame/urlmon_url_request.cc
@@ -9,6 +9,8 @@
#include "base/scoped_ptr.h"
#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/urlmon_upload_data_stream.h"
#include "chrome_frame/utils.h"
#include "net/http/http_util.h"
@@ -23,10 +25,11 @@ UrlmonUrlRequest::UrlmonUrlRequest()
: pending_read_size_(0),
status_(URLRequestStatus::FAILED, net::ERR_FAILED),
thread_(PlatformThread::CurrentId()),
- is_request_started_(false),
post_data_len_(0),
redirect_status_(0),
- parent_window_(NULL) {
+ parent_window_(NULL),
+ worker_thread_(NULL),
+ task_marshaller_(NULL) {
DLOG(INFO) << StringPrintf("Created request. Obj: %X", this)
<< " Count: " << ++instance_count_;
}
@@ -39,22 +42,16 @@ UrlmonUrlRequest::~UrlmonUrlRequest() {
bool UrlmonUrlRequest::Start() {
DCHECK_EQ(PlatformThread::CurrentId(), thread_);
- 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";
- OnResponseEnd(status_);
+ if (!worker_thread_) {
+ NOTREACHED() << __FUNCTION__ << " Urlmon request thread not initialized";
return false;
}
+ worker_thread_->message_loop()->PostTask(
+ FROM_HERE, NewRunnableMethod(this, &UrlmonUrlRequest::StartAsync));
+
// Take a self reference to maintain COM lifetime. This will be released
- // in EndRequest. Set a flag indicating that we have an additional
- // reference here
- is_request_started_ = true;
+ // in EndRequest
AddRef();
request_handler()->AddRequest(this);
return true;
@@ -63,6 +60,34 @@ bool UrlmonUrlRequest::Start() {
void UrlmonUrlRequest::Stop() {
DCHECK_EQ(PlatformThread::CurrentId(), thread_);
+ if (!worker_thread_) {
+ NOTREACHED() << __FUNCTION__ << " Urlmon request thread not initialized";
+ return;
+ }
+
+ 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;
+ }
+}
+
+void UrlmonUrlRequest::StopAsync() {
+ DCHECK(worker_thread_ != NULL);
+
if (binding_) {
binding_->Abort();
} else {
@@ -77,15 +102,28 @@ bool UrlmonUrlRequest::Read(int bytes_to_read) {
DLOG(INFO) << StringPrintf("URL: %s Obj: %X", url().c_str(), this);
+ if (!worker_thread_) {
+ NOTREACHED() << __FUNCTION__ << " Urlmon request thread not initialized";
+ return false;
+ }
+
+ worker_thread_->message_loop()->PostTask(
+ FROM_HERE, NewRunnableMethod(this, &UrlmonUrlRequest::ReadAsync,
+ bytes_to_read));
+ 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)) {
DLOG(INFO) << StringPrintf("URL: %s Obj: %X - bytes read from cache: %d",
url().c_str(), this, bytes_copied);
- return true;
+ return;
}
// if the request is finished or there's nothing more to read
@@ -94,13 +132,12 @@ bool UrlmonUrlRequest::Read(int bytes_to_read) {
DLOG(INFO) << StringPrintf("URL: %s Obj: %X. Response finished. Status: %d",
url().c_str(), this, status_.status());
EndRequest();
- return true;
+ return;
}
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(
@@ -143,7 +180,8 @@ STDMETHODIMP UrlmonUrlRequest::OnProgress(ULONG progress, ULONG max_progress,
}
STDMETHODIMP UrlmonUrlRequest::OnStopBinding(HRESULT result, LPCWSTR error) {
- DCHECK_EQ(PlatformThread::CurrentId(), thread_);
+ DCHECK(worker_thread_ != NULL);
+ DCHECK_EQ(PlatformThread::CurrentId(), worker_thread_->thread_id());
DLOG(INFO) << StringPrintf("URL: %s Obj: %X", url().c_str(), this) <<
" - Request stopped, Result: " << std::hex << result <<
@@ -167,7 +205,8 @@ STDMETHODIMP UrlmonUrlRequest::OnStopBinding(HRESULT result, LPCWSTR error) {
STDMETHODIMP UrlmonUrlRequest::GetBindInfo(DWORD* bind_flags,
BINDINFO *bind_info) {
- DCHECK_EQ(PlatformThread::CurrentId(), thread_);
+ 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;
@@ -224,7 +263,9 @@ STDMETHODIMP UrlmonUrlRequest::GetBindInfo(DWORD* bind_flags,
STDMETHODIMP UrlmonUrlRequest::OnDataAvailable(DWORD flags, DWORD size,
FORMATETC* formatetc,
STGMEDIUM* storage) {
- DCHECK_EQ(PlatformThread::CurrentId(), thread_);
+ 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);
@@ -285,7 +326,9 @@ STDMETHODIMP UrlmonUrlRequest::OnObjectAvailable(REFIID iid, IUnknown *object) {
STDMETHODIMP UrlmonUrlRequest::BeginningTransaction(const wchar_t* url,
const wchar_t* current_headers, DWORD reserved,
wchar_t** additional_headers) {
- DCHECK_EQ(PlatformThread::CurrentId(), thread_);
+ DCHECK(worker_thread_ != NULL);
+ DCHECK_EQ(PlatformThread::CurrentId(), worker_thread_->thread_id());
+
if (!additional_headers) {
NOTREACHED();
return E_POINTER;
@@ -333,7 +376,8 @@ STDMETHODIMP UrlmonUrlRequest::BeginningTransaction(const wchar_t* url,
STDMETHODIMP UrlmonUrlRequest::OnResponse(DWORD dwResponseCode,
const wchar_t* response_headers, const wchar_t* request_headers,
wchar_t** additional_headers) {
- DCHECK_EQ(PlatformThread::CurrentId(), thread_);
+ DCHECK(worker_thread_ != NULL);
+ DCHECK_EQ(PlatformThread::CurrentId(), worker_thread_->thread_id());
std::string raw_headers = WideToUTF8(response_headers);
@@ -552,15 +596,22 @@ void UrlmonUrlRequest::EndRequest() {
status_.set_os_error(net::ERR_UNSAFE_REDIRECT);
}
}
- request_handler()->RemoveRequest(this);
+
OnResponseEnd(status_);
- // If the request was started then we must have an additional reference on the
- // request.
- if (is_request_started_) {
- is_request_started_ = false;
- Release();
- }
+ DCHECK(task_marshaller_ != NULL);
+
+ // Remove the request mapping and release the outstanding reference to us in
+ // the context of the UI thread.
+ task_marshaller_->PostTask(
+ FROM_HERE, NewRunnableMethod(this, &UrlmonUrlRequest::EndRequestInternal));
+}
+
+void UrlmonUrlRequest::EndRequestInternal() {
+ request_handler()->RemoveRequest(this);
+ // 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();
}
int UrlmonUrlRequest::GetHttpResponseStatus() const {
@@ -677,6 +728,7 @@ bool UrlmonUrlRequest::Cache::Append(IStream* source,
while (SUCCEEDED(hr)) {
DWORD chunk_read = 0; // NOLINT
hr = source->Read(read_buffer_, sizeof(read_buffer_), &chunk_read);
+
if (!chunk_read)
break;
diff --git a/chrome_frame/urlmon_url_request.h b/chrome_frame/urlmon_url_request.h
index 114ee6b..3c64243f 100644
--- a/chrome_frame/urlmon_url_request.h
+++ b/chrome_frame/urlmon_url_request.h
@@ -13,8 +13,10 @@
#include "base/lock.h"
#include "base/platform_thread.h"
+#include "base/thread.h"
#include "base/scoped_comptr_win.h"
#include "chrome_frame/plugin_url_request.h"
+#include "chrome_frame/chrome_frame_delegate.h"
#include "net/base/net_errors.h"
#include "net/base/upload_data.h"
@@ -87,8 +89,32 @@ END_SERVICE_MAP()
parent_window_ = parent_window;
}
+ // Needed to support PostTask.
+ static bool ImplementsThreadSafeReferenceCounting() {
+ return true;
+ }
+
+ // URL requests are handled on this thread.
+ void set_worker_thread(base::Thread* worker_thread) {
+ worker_thread_ = worker_thread;
+ }
+
+ void set_task_marshaller(TaskMarshaller* task_marshaller) {
+ task_marshaller_ = task_marshaller;
+ }
+
protected:
+ // The following functions issue and handle Urlmon requests on the dedicated
+ // Urlmon thread.
+ void StartAsync();
+ void StopAsync();
+ void ReadAsync(int bytes_to_read);
+
static const size_t kCopyChunkSize = 32 * 1024;
+ // URL requests are handled on this thread.
+ base::Thread* worker_thread_;
+
+ TaskMarshaller* task_marshaller_;
// A fake stream class to make it easier to copy received data using
// IStream::CopyTo instead of allocating temporary buffers and keeping
@@ -203,7 +229,9 @@ END_SERVICE_MAP()
HRESULT StartAsyncDownload();
void EndRequest();
-
+ // Executes in the context of the UI thread and releases the outstanding
+ // reference to us. It also deletes the request mapping for this instance.
+ void EndRequestInternal();
int GetHttpResponseStatus() const;
static net::Error HresultToNetError(HRESULT hr);
@@ -221,7 +249,6 @@ END_SERVICE_MAP()
uint64 post_data_len_;
PlatformThreadId thread_;
- bool is_request_started_;
static int instance_count_;
HWND parent_window_;
DISALLOW_COPY_AND_ASSIGN(UrlmonUrlRequest);