diff options
-rw-r--r-- | chrome_frame/chrome_frame_activex_base.h | 55 | ||||
-rw-r--r-- | chrome_frame/chrome_frame_automation.cc | 2 | ||||
-rw-r--r-- | chrome_frame/chrome_frame_automation.h | 36 | ||||
-rw-r--r-- | chrome_frame/chrome_frame_delegate.h | 45 | ||||
-rw-r--r-- | chrome_frame/plugin_url_request.h | 3 | ||||
-rw-r--r-- | chrome_frame/urlmon_url_request.cc | 110 | ||||
-rw-r--r-- | chrome_frame/urlmon_url_request.h | 31 |
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); |