summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorstoyan@chromium.org <stoyan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-02-01 19:48:36 +0000
committerstoyan@chromium.org <stoyan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-02-01 19:48:36 +0000
commit3eb07da6e868e49e28cb5a6d8a57037f2c45a20e (patch)
treeb9e05e275e38b176b1473e0f9e66075706ca238b
parent55750b57e30ecc9f3657d1ebd08853e232ba4e23 (diff)
downloadchromium_src-3eb07da6e868e49e28cb5a6d8a57037f2c45a20e.zip
chromium_src-3eb07da6e868e49e28cb5a6d8a57037f2c45a20e.tar.gz
chromium_src-3eb07da6e868e49e28cb5a6d8a57037f2c45a20e.tar.bz2
TEST=new tests added.
Review URL: http://codereview.chromium.org/545093 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@37728 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome_frame/chrome_active_document.cc35
-rw-r--r--chrome_frame/chrome_active_document.h4
-rw-r--r--chrome_frame/chrome_frame.gyp5
-rw-r--r--chrome_frame/chrome_frame_activex.cc5
-rw-r--r--chrome_frame/chrome_frame_activex_base.h99
-rw-r--r--chrome_frame/chrome_frame_automation.cc207
-rw-r--r--chrome_frame/chrome_frame_automation.h48
-rw-r--r--chrome_frame/chrome_frame_npapi.cc75
-rw-r--r--chrome_frame/chrome_frame_npapi.h18
-rw-r--r--chrome_frame/chrome_frame_npapi_entrypoints.cc47
-rw-r--r--chrome_frame/http_negotiate.cc44
-rw-r--r--chrome_frame/npapi_url_request.cc173
-rw-r--r--chrome_frame/npapi_url_request.h70
-rw-r--r--chrome_frame/plugin_url_request.cc51
-rw-r--r--chrome_frame/plugin_url_request.h137
-rw-r--r--chrome_frame/test/chrome_frame_test_utils.h10
-rw-r--r--chrome_frame/test/chrome_frame_unittests.cc11
-rw-r--r--chrome_frame/test/url_request_test.cc152
-rw-r--r--chrome_frame/urlmon_url_request.cc686
-rw-r--r--chrome_frame/urlmon_url_request.h308
-rw-r--r--chrome_frame/urlmon_url_request_private.h307
-rw-r--r--chrome_frame/utils.h20
22 files changed, 1496 insertions, 1016 deletions
diff --git a/chrome_frame/chrome_active_document.cc b/chrome_frame/chrome_active_document.cc
index 553b46f..202128b 100644
--- a/chrome_frame/chrome_active_document.cc
+++ b/chrome_frame/chrome_active_document.cc
@@ -54,6 +54,7 @@ bool g_first_launch_by_process_ = true;
ChromeActiveDocument::ChromeActiveDocument()
: first_navigation_(true),
is_automation_client_reused_(false) {
+ url_fetcher_.set_frame_busting(false);
memset(&navigation_info_, 0, sizeof(navigation_info_));
}
@@ -68,7 +69,7 @@ HRESULT ChromeActiveDocument::FinalConstruct() {
DLOG(INFO) << "Reusing automation client instance from "
<< cached_document;
DCHECK(automation_client_.get() != NULL);
- automation_client_->Reinitialize(this);
+ automation_client_->Reinitialize(this, &url_fetcher_);
is_automation_client_reused_ = true;
} else {
// The FinalConstruct implementation in the ChromeFrameActivexBase class
@@ -102,6 +103,8 @@ ChromeActiveDocument::~ChromeActiveDocument() {
if (find_dialog_.IsWindow()) {
find_dialog_.DestroyWindow();
}
+ // ChromeFramePlugin
+ Base::Uninitialize();
}
// Override DoVerb
@@ -174,10 +177,6 @@ STDMETHODIMP ChromeActiveDocument::IsDirty() {
return S_FALSE;
}
-bool ChromeActiveDocument::is_frame_busting_enabled() {
- return false;
-}
-
void ChromeActiveDocument::OnAutomationServerReady() {
Base::OnAutomationServerReady();
Base::GiveFocusToChrome();
@@ -231,18 +230,7 @@ STDMETHODIMP ChromeActiveDocument::Load(BOOL fully_avalable,
}
if (!is_chrome_protocol) {
- CComObject<UrlmonUrlRequest>* new_request = NULL;
- CComObject<UrlmonUrlRequest>::CreateInstance(&new_request);
- new_request->AddRef();
-
- if (SUCCEEDED(new_request->ConnectToExistingMoniker(moniker_name,
- bind_context,
- url))) {
- base_url_request_.swap(&new_request);
- DCHECK(new_request == NULL);
- } else {
- new_request->Release();
- }
+ url_fetcher_.UseMonikerForUrl(moniker_name, bind_context, url);
}
UMA_HISTOGRAM_CUSTOM_COUNTS("ChromeFrame.FullTabLaunchType",
@@ -883,12 +871,15 @@ bool ChromeActiveDocument::LaunchUrl(const std::wstring& url,
}
}
- if (!is_automation_client_reused_ &&
- !InitializeAutomation(GetHostProcessName(false), L"", IsIEInPrivate())) {
- return false;
- }
+ if (is_automation_client_reused_)
+ return true;
- return true;
+ automation_client_->SetUrlFetcher(&url_fetcher_);
+
+ if (InitializeAutomation(GetHostProcessName(false), L"", IsIEInPrivate()))
+ return true;
+
+ return false;
}
HRESULT ChromeActiveDocument::SetPageFontSize(const GUID* cmd_group_guid,
diff --git a/chrome_frame/chrome_active_document.h b/chrome_frame/chrome_active_document.h
index 45df8b7..dc6511d 100644
--- a/chrome_frame/chrome_active_document.h
+++ b/chrome_frame/chrome_active_document.h
@@ -238,10 +238,6 @@ END_EXEC_COMMAND_MAP()
bool PreProcessContextMenu(HMENU menu);
bool HandleContextMenuCommand(UINT cmd, const IPC::ContextMenuParams& params);
- // Should connections initiated by this class try to block
- // responses served with the X-Frame-Options header?
- bool is_frame_busting_enabled();
-
// ChromeFramePlugin overrides.
virtual void OnAutomationServerReady();
diff --git a/chrome_frame/chrome_frame.gyp b/chrome_frame/chrome_frame.gyp
index 280c214..7f844cf 100644
--- a/chrome_frame/chrome_frame.gyp
+++ b/chrome_frame/chrome_frame.gyp
@@ -183,8 +183,9 @@
'../testing/gtest.gyp:gtest',
'../third_party/libxml/libxml.gyp:libxml',
'../third_party/libxslt/libxslt.gyp:libxslt',
- 'chrome_frame_strings',
+ 'chrome_frame_ie',
'chrome_frame_npapi',
+ 'chrome_frame_strings',
'npchrome_frame',
'xulrunner_sdk',
],
@@ -203,6 +204,7 @@
'test/test_server.cc',
'test/test_server.h',
'test/test_server_test.cc',
+ 'test/url_request_test.cc',
'chrome_frame_automation.cc',
'chrome_frame_histograms.h',
'chrome_frame_histograms.cc',
@@ -581,6 +583,7 @@
'extra_system_apis.h',
'urlmon_url_request.cc',
'urlmon_url_request.h',
+ 'urlmon_url_request_private.h',
'urlmon_upload_data_stream.cc',
'urlmon_upload_data_stream.h',
'vtable_patch_manager.cc',
diff --git a/chrome_frame/chrome_frame_activex.cc b/chrome_frame/chrome_frame_activex.cc
index e8fa70a..295ceaa 100644
--- a/chrome_frame/chrome_frame_activex.cc
+++ b/chrome_frame/chrome_frame_activex.cc
@@ -42,6 +42,9 @@ ChromeFrameActivex::~ChromeFrameActivex() {
DCHECK(onload_.size() == 0);
DCHECK(onreadystatechanged_.size() == 0);
DCHECK(onextensionready_.size() == 0);
+
+ // ChromeFramePlugin::Uninitialize()
+ Base::Uninitialize();
}
LRESULT ChromeFrameActivex::OnCreate(UINT message, WPARAM wparam, LPARAM lparam,
@@ -315,6 +318,8 @@ HRESULT ChromeFrameActivex::IOleObject_SetClientSite(
profile_name.assign(profile_name_arg, profile_name_arg.Length());
}
+ url_fetcher_.set_frame_busting(!is_privileged_);
+ automation_client_->SetUrlFetcher(&url_fetcher_);
if (!InitializeAutomation(profile_name, chrome_extra_arguments,
IsIEInPrivate())) {
return E_FAIL;
diff --git a/chrome_frame/chrome_frame_activex_base.h b/chrome_frame/chrome_frame_activex_base.h
index d7abc01..af71a11 100644
--- a/chrome_frame/chrome_frame_activex_base.h
+++ b/chrome_frame/chrome_frame_activex_base.h
@@ -165,8 +165,7 @@ class ATL_NO_VTABLE ChromeFrameActivexBase : // NOLINT
public:
ChromeFrameActivexBase()
- : ready_state_(READYSTATE_UNINITIALIZED),
- worker_thread_("ChromeFrameWorker_Thread") {
+ : ready_state_(READYSTATE_UNINITIALIZED) {
m_bWindowOnly = TRUE;
}
@@ -467,59 +466,16 @@ END_MSG_MAP()
web_browser2->put_Visible(VARIANT_TRUE);
}
- virtual void OnRequestStart(int tab_handle, int request_id,
- const IPC::AutomationURLRequest& request_info) {
- // The worker thread may have been stopped. This could happen if the
- // ActiveX instance was reused.
- if (!worker_thread_.message_loop()) {
- 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));
- }
-
- scoped_refptr<CComObject<UrlmonUrlRequest> > request;
- if (base_url_request_.get() &&
- GURL(base_url_request_->url()) == GURL(request_info.url)) {
- request.swap(base_url_request_);
- } else {
- CComObject<UrlmonUrlRequest>* new_request = NULL;
- CComObject<UrlmonUrlRequest>::CreateInstance(&new_request);
- request = new_request;
- }
-
- 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())) {
- request->set_worker_thread(&worker_thread_);
- // If Start is successful, it will add a self reference.
- request->Start();
- request->set_parent_window(m_hWnd);
- }
- }
-
- virtual void OnRequestRead(int tab_handle, int request_id,
- int bytes_to_read) {
- automation_client_->ReadRequest(request_id, bytes_to_read);
- }
-
- virtual void OnRequestEnd(int tab_handle, int request_id,
- const URLRequestStatus& status) {
- automation_client_->RemoveRequest(request_id, true);
- }
-
virtual void OnDownloadRequestInHost(int tab_handle, int request_id) {
DLOG(INFO) << "TODO: Let the host browser handle this download";
- PluginUrlRequest* request = automation_client_->LookupRequest(request_id);
- if (request) {
- static_cast<UrlmonUrlRequest*>(request)->TransferToHost(doc_site_);
+ ScopedComPtr<IBindCtx> bind_context;
+ ScopedComPtr<IMoniker> moniker;
+ url_fetcher_.StealMonikerFromRequest(request_id, moniker.Receive());
+ if (moniker) {
+ ::CreateBindCtx(0, bind_context.Receive());
+ DCHECK(bind_context);
+ NavigateBrowserToMoniker(doc_site_, moniker, NULL, bind_context, NULL);
}
- automation_client_->RemoveRequest(request_id, false);
}
virtual void OnSetCookieAsync(int tab_handle, const GURL& url,
@@ -569,26 +525,7 @@ END_MSG_MAP()
LRESULT OnDestroy(UINT message, WPARAM wparam, LPARAM lparam,
BOOL& handled) { // NO_LINT
- if (worker_thread_.message_loop()) {
- if (automation_client_.get())
- automation_client_->CleanupRequests();
-
- worker_thread_.message_loop()->PostTask(
- FROM_HERE, NewRunnableMethod(this, &Base::OnWorkerStop));
-
- MSG msg = {0};
- while (GetMessage(&msg, NULL, WM_USER,
- WM_WORKER_THREAD_UNINITIALIZED_MSG)) {
- if (msg.hwnd == m_hWnd &&
- msg.message == WM_WORKER_THREAD_UNINITIALIZED_MSG) {
- break;
- }
-
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- worker_thread_.Stop();
- }
+ DLOG(INFO) << __FUNCTION__;
return 0;
}
@@ -1111,17 +1048,6 @@ END_MSG_MAP()
}
protected:
- // The following functions are called to initialize and uninitialize the
- // worker thread.
- void OnWorkerStart() {
- CoInitialize(NULL);
- }
-
- void OnWorkerStop() {
- CoUninitialize();
- PostMessage(WM_WORKER_THREAD_UNINITIALIZED_MSG, 0, 0);
- }
-
ScopedBstr url_;
ScopedComPtr<IOleDocumentSite> doc_site_;
@@ -1142,10 +1068,9 @@ END_MSG_MAP()
EventHandlers onprivatemessage_;
EventHandlers onextensionready_;
- // The UrlmonUrlRequest instance instantiated for downloading the base URL.
- scoped_refptr<CComObject<UrlmonUrlRequest> > base_url_request_;
-
- base::Thread worker_thread_;
+ // Handle network requests when host network stack is used. Passed to the
+ // automation client on initialization.
+ UrlmonUrlRequestManager url_fetcher_;
};
#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 482fb2b..5d05068 100644
--- a/chrome_frame/chrome_frame_automation.cc
+++ b/chrome_frame/chrome_frame_automation.cc
@@ -408,6 +408,7 @@ ChromeFrameAutomationClient::ChromeFrameAutomationClient()
handle_top_level_requests_(false),
tab_handle_(-1),
external_tab_cookie_(NULL),
+ url_fetcher_(NULL),
navigate_after_initialization_(false) {
}
@@ -493,8 +494,10 @@ void ChromeFrameAutomationClient::Uninitialize() {
tab_ = NULL; // scoped_refptr::Release
}
- // Clean up any outstanding requests
- CleanupRequests();
+ if (url_fetcher_) {
+ // Clean up any outstanding requests
+ url_fetcher_->StopAllRequests();
+ }
// Wait for the background thread to exit.
ReleaseAutomationServer();
@@ -850,10 +853,54 @@ void ChromeFrameAutomationClient::InitializeComplete(
// kind of beings.
// By default we marshal the IPC message to the main/GUI thread and from there
// we safely invoke chrome_frame_delegate_->OnMessageReceived(msg).
+
+
+bool ChromeFrameAutomationClient::ProcessUrlRequestMessage(TabProxy* tab,
+ const IPC::Message& msg, bool ui_thread) {
+ // Either directly call appropriate url_fetcher function
+ // or postpone call to the UI thread.
+ bool invoke = ui_thread || thread_safe_url_fetcher_;
+ uint16 msg_type = msg.type();
+ switch (msg_type) {
+ default:
+ return false;
+
+ case AutomationMsg_RequestStart::ID:
+ if (invoke)
+ AutomationMsg_RequestStart::Dispatch(&msg, url_fetcher_,
+ &PluginUrlRequestManager::StartUrlRequest);
+ break;
+
+ case AutomationMsg_RequestRead::ID:
+ if (invoke)
+ AutomationMsg_RequestRead::Dispatch(&msg, url_fetcher_,
+ &PluginUrlRequestManager::ReadUrlRequest);
+ break;
+
+ case AutomationMsg_RequestEnd::ID:
+ if (invoke)
+ AutomationMsg_RequestEnd::Dispatch(&msg, url_fetcher_,
+ &PluginUrlRequestManager::EndUrlRequest);
+ break;
+ }
+
+ if (!invoke) {
+ PostTask(FROM_HERE, NewRunnableMethod(this,
+ &ChromeFrameAutomationClient::ProcessUrlRequestMessage,
+ tab, msg, true));
+ }
+
+ return true;
+}
+
void ChromeFrameAutomationClient::OnMessageReceived(TabProxy* tab,
const IPC::Message& msg) {
DCHECK(tab == tab_.get());
+ // Quickly process network related messages.
+ if (url_fetcher_ && ProcessUrlRequestMessage(tab, msg, false))
+ return;
+
// Early check to avoid needless marshaling
if (chrome_frame_delegate_ == NULL)
return;
@@ -998,119 +1045,9 @@ void ChromeFrameAutomationClient::PrintTab() {
tab_->PrintAsync();
}
-// IPC:Message::Sender implementation
-bool ChromeFrameAutomationClient::Send(IPC::Message* msg) {
- if (automation_server_) {
- return automation_server_->Send(msg);
- }
- return false;
-}
-
-bool ChromeFrameAutomationClient::AddRequest(PluginUrlRequest* request) {
- DCHECK_EQ(PlatformThread::CurrentId(), ui_thread_id_);
-
- if (!request) {
- NOTREACHED();
- return false;
- }
-
-#ifndef NDEBUG
- RequestMap::const_iterator it = request_map_.find(request->id());
- scoped_refptr<PluginUrlRequest> other(request_map_.end() == it ?
- NULL : (*it).second);
- DCHECK(other.get() == NULL);
-#endif
- request_map_[request->id()] = request;
- return true;
-}
-
-bool ChromeFrameAutomationClient::ReadRequest(
- int request_id, int bytes_to_read) {
- bool result = false;
- PluginUrlRequest* request = LookupRequest(request_id);
- if (request)
- result = request->Read(bytes_to_read);
-
- return result;
-}
-
-void ChromeFrameAutomationClient::RemoveRequest(PluginUrlRequest* request) {
- DCHECK_EQ(PlatformThread::CurrentId(), ui_thread_id_);
-
- // We check if the request pointer passed in is actually present in the map
- // before going ahead and deleting it. This avoids the issue where we would
- // incorrectly delete a different request with the same request id.
- if (IsValidRequest(request)) {
- request_map_.erase(request->id());
- }
-}
-
-void ChromeFrameAutomationClient::RemoveRequest(int request_id, bool abort) {
- DCHECK_EQ(PlatformThread::CurrentId(), ui_thread_id_);
- PluginUrlRequest* request = LookupRequest(request_id);
- if (request) {
- if (abort) {
- // The request object will get removed asynchronously.
- request->Stop();
- } else {
- request_map_.erase(request_id);
- }
- }
-}
-
-PluginUrlRequest* ChromeFrameAutomationClient::LookupRequest(
- int request_id) const {
- DCHECK_EQ(PlatformThread::CurrentId(), ui_thread_id_);
- PluginUrlRequest* request = NULL;
- RequestMap::const_iterator it = request_map_.find(request_id);
- if (request_map_.end() != it)
- request = (*it).second;
- return request;
-}
-
-bool ChromeFrameAutomationClient::IsValidRequest(
- PluginUrlRequest* request) const {
- DCHECK_EQ(PlatformThread::CurrentId(), ui_thread_id_);
- bool is_valid = false;
- // if request is invalid then request->id() won't work
- // hence perform reverse map lookup for validity of the
- // request pointer.
- if (request) {
- for (RequestMap::const_iterator it = request_map_.begin();
- it != request_map_.end(); it++) {
- if (request == (*it).second) {
- is_valid = true;
- break;
- }
- }
- }
-
- return is_valid;
-}
-
-void ChromeFrameAutomationClient::CleanupRequests() {
- DCHECK_EQ(PlatformThread::CurrentId(), ui_thread_id_);
-
- std::vector<scoped_refptr<PluginUrlRequest> > 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.
- RequestMap::iterator index = request_map_.begin();
- while (index != request_map_.end()) {
- PluginUrlRequest* request = (*index).second;
- DCHECK(request != NULL);
- request_list.push_back(request);
- index++;
- }
- request_map_.clear();
-
- for (unsigned int index = 0; index < request_list.size(); ++index) {
- request_list[index]->Stop();
- }
-}
-
bool ChromeFrameAutomationClient::Reinitialize(
- ChromeFrameDelegate* delegate) {
+ ChromeFrameDelegate* delegate,
+ PluginUrlRequestManager* url_fetcher) {
if (!tab_.get() || !::IsWindow(chrome_window_)) {
NOTREACHED();
DLOG(WARNING) << "ChromeFrameAutomationClient instance reused "
@@ -1123,8 +1060,9 @@ bool ChromeFrameAutomationClient::Reinitialize(
return false;
}
- CleanupRequests();
+ url_fetcher_->StopAllRequests();
chrome_frame_delegate_ = delegate;
+ SetUrlFetcher(url_fetcher);
SetParentWindow(NULL);
return true;
}
@@ -1146,6 +1084,43 @@ void ChromeFrameAutomationClient::SetPageFontSize(
return;
}
- Send(new AutomationMsg_SetPageFontSize(0, tab_handle_, font_size));
+ automation_server_->Send(
+ new AutomationMsg_SetPageFontSize(0, tab_handle_, font_size));
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// PluginUrlRequestDelegate implementation.
+// Forward network related responses to Chrome.
+
+void ChromeFrameAutomationClient::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) {
+ const IPC::AutomationURLResponse response = {
+ mime_type,
+ headers ? headers : "",
+ size,
+ last_modified,
+ peristent_cookies,
+ redirect_url,
+ redirect_status
+ };
+
+ automation_server_->Send(new AutomationMsg_RequestStarted(0,
+ tab_->handle(), request_id, response));
+}
+
+void ChromeFrameAutomationClient::OnReadComplete(int request_id,
+ const void* buffer, int len) {
+ std::string data(reinterpret_cast<const char*>(buffer), len);
+ automation_server_->Send(new AutomationMsg_RequestData(0, tab_->handle(),
+ request_id, data));
+}
+
+void ChromeFrameAutomationClient::OnResponseEnd(int request_id,
+ const URLRequestStatus& status) {
+ automation_server_->Send(new AutomationMsg_RequestEnd(0, tab_->handle(),
+ request_id, status));
}
diff --git a/chrome_frame/chrome_frame_automation.h b/chrome_frame/chrome_frame_automation.h
index aa62948..4cfa187 100644
--- a/chrome_frame/chrome_frame_automation.h
+++ b/chrome_frame/chrome_frame_automation.h
@@ -148,7 +148,8 @@ class ProxyFactory {
class ChromeFrameAutomationClient
: public CWindowImpl<ChromeFrameAutomationClient>,
public TaskMarshallerThroughWindowsMessages<ChromeFrameAutomationClient>,
- public PluginRequestHandler,
+ public base::RefCountedThreadSafe<ChromeFrameAutomationClient>,
+ public PluginUrlRequestDelegate,
public TabProxy::TabProxyDelegate,
public ProxyFactory::LaunchDelegate {
public:
@@ -227,18 +228,6 @@ class ChromeFrameAutomationClient
// the whole tab.
void PrintTab();
- // PluginRequestHandler
- bool AddRequest(PluginUrlRequest* request);
- void RemoveRequest(PluginUrlRequest* request);
- virtual bool Send(IPC::Message* msg);
-
- // URL request related
- bool ReadRequest(int request_id, int bytes_to_read);
- void RemoveRequest(int request_id, bool abort);
- PluginUrlRequest* LookupRequest(int request_id) const;
- bool IsValidRequest(PluginUrlRequest* request) const;
- void CleanupRequests();
-
void set_use_chrome_network(bool use_chrome_network) {
use_chrome_network_ = use_chrome_network;
}
@@ -258,7 +247,8 @@ class ChromeFrameAutomationClient
// Called if the same instance of the ChromeFrameAutomationClient object
// is reused.
- bool Reinitialize(ChromeFrameDelegate* chrome_frame_delegate);
+ bool Reinitialize(ChromeFrameDelegate* chrome_frame_delegate,
+ PluginUrlRequestManager* url_fetcher);
// Attaches an existing external tab to this automation client instance.
void AttachExternalTab(intptr_t external_tab_cookie);
@@ -280,8 +270,6 @@ class ChromeFrameAutomationClient
void InitializeComplete(AutomationLaunchResult result);
private:
- typedef std::map<int, scoped_refptr<PluginUrlRequest> > RequestMap;
-
// Usage: From bkgnd thread invoke:
// CallDelegate(FROM_HERE, NewRunnableMethod(chrome_frame_delegate_,
// ChromeFrameDelegate::Something,
@@ -323,9 +311,6 @@ class ChromeFrameAutomationClient
// Keeps track of the version of Chrome we're talking to.
std::string automation_server_version_;
- // Map of outstanding requests
- RequestMap request_map_;
-
typedef enum InitializationState {
UNINITIALIZED = 0,
INITIALIZING,
@@ -346,6 +331,31 @@ class ChromeFrameAutomationClient
bool navigate_after_initialization_;
ChromeFrameLaunchParams chrome_launch_params_;
+
+ // When host network stack is used, this object is in charge of
+ // handling network requests.
+ PluginUrlRequestManager* url_fetcher_;
+ bool thread_safe_url_fetcher_;
+
+ bool ProcessUrlRequestMessage(TabProxy* tab, const IPC::Message& msg,
+ bool ui_thread);
+
+ // PluginUrlRequestDelegate implementation. Simply adds tab's handle
+ // as parameter and forwards to Chrome via IPC.
+ virtual void 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);
+ virtual void OnReadComplete(int request_id, const void* buffer, int len);
+ virtual void OnResponseEnd(int request_id, const URLRequestStatus& status);
+
+ public:
+ void SetUrlFetcher(PluginUrlRequestManager* url_fetcher) {
+ DCHECK(url_fetcher != NULL);
+ url_fetcher_ = url_fetcher;
+ thread_safe_url_fetcher_ = url_fetcher->IsThreadSafe();
+ url_fetcher_->set_delegate(this);
+ }
};
#endif // CHROME_FRAME_CHROME_FRAME_AUTOMATION_H_
diff --git a/chrome_frame/chrome_frame_npapi.cc b/chrome_frame/chrome_frame_npapi.cc
index b2a8acb..8f21f93 100644
--- a/chrome_frame/chrome_frame_npapi.cc
+++ b/chrome_frame/chrome_frame_npapi.cc
@@ -269,9 +269,13 @@ bool ChromeFrameNPAPI::Initialize(NPMIMEType mime_type, NPP instance,
if (chrome_network_arg_set)
automation_client_->set_use_chrome_network(chrome_network_arg);
-
}
+ // Setup Url fetcher.
+ url_fetcher_.set_NPPInstance(instance_);
+ url_fetcher_.set_frame_busting(!is_privileged_);
+ automation_client_->SetUrlFetcher(&url_fetcher_);
+
// TODO(joshia): Initialize navigation here and send proxy config as
// part of LaunchSettings
/*
@@ -434,12 +438,7 @@ void ChromeFrameNPAPI::UrlNotify(const char* url, NPReason reason,
npapi::PopPopupsEnabledState(instance_);
}
- // It is now safe to release the additional reference on the request
- NPAPIUrlRequest* request = RequestFromNotifyData(notify_data);
- if (request) {
- request->Stop();
- request->Release();
- }
+ url_fetcher_.UrlNotify(url, reason, notify_data);
}
void ChromeFrameNPAPI::OnAcceleratorPressed(int tab_handle,
@@ -513,32 +512,6 @@ void ChromeFrameNPAPI::OnOpenURL(int tab_handle,
npapi::GetURLNotify(instance_, url.spec().c_str(), target.c_str(), NULL);
}
-void ChromeFrameNPAPI::OnRequestStart(int tab_handle, int request_id,
- const IPC::AutomationURLRequest& request) {
- scoped_refptr<NPAPIUrlRequest> new_request(new NPAPIUrlRequest(instance_));
- DCHECK(new_request);
- if (new_request->Initialize(automation_client_.get(), tab_handle,
- request_id, request.url, request.method,
- request.referrer, request.extra_request_headers,
- request.upload_data.get(), !is_privileged_)) {
- if (new_request->Start()) {
- // Keep additional reference on request for NPSTREAM
- // This will be released in NPP_UrlNotify
- new_request->AddRef();
- }
- }
-}
-
-void ChromeFrameNPAPI::OnRequestRead(int tab_handle, int request_id,
- int bytes_to_read) {
- automation_client_->ReadRequest(request_id, bytes_to_read);
-}
-
-void ChromeFrameNPAPI::OnRequestEnd(int tab_handle, int request_id,
- const URLRequestStatus& status) {
- automation_client_->RemoveRequest(request_id, true);
-}
-
void ChromeFrameNPAPI::OnSetCookieAsync(int tab_handle, const GURL& url,
const std::string& cookie) {
// Use the newer NPAPI way if available
@@ -1438,24 +1411,6 @@ bool ChromeFrameNPAPI::GetBrowserIncognitoMode() {
return incognito_mode;
}
-NPAPIUrlRequest* ChromeFrameNPAPI::ValidateRequest(
- NPP instance, void* notify_data) {
- ChromeFrameNPAPI* plugin_instance =
- ChromeFrameNPAPI::ChromeFrameInstanceFromPluginInstance(instance);
- if (plugin_instance) {
- return plugin_instance->RequestFromNotifyData(notify_data);
- }
-
- return NULL;
-}
-
-NPAPIUrlRequest* ChromeFrameNPAPI::RequestFromNotifyData(
- void* notify_data) const {
- NPAPIUrlRequest* request = reinterpret_cast<NPAPIUrlRequest*>(notify_data);
- DCHECK(request ? automation_client_->IsValidRequest(request) : 1);
- return request;
-}
-
bool ChromeFrameNPAPI::HandleContextMenuCommand(UINT cmd,
const IPC::ContextMenuParams& params) {
if (cmd == IDC_ABOUT_CHROME_FRAME) {
@@ -1463,3 +1418,21 @@ bool ChromeFrameNPAPI::HandleContextMenuCommand(UINT cmd,
}
return false;
}
+
+bool ChromeFrameNPAPI::NewStream(NPMIMEType type, NPStream* stream,
+ NPBool seekable, uint16* stream_type) {
+ return url_fetcher_.NewStream(type, stream, seekable, stream_type);
+}
+
+int32 ChromeFrameNPAPI::WriteReady(NPStream* stream) {
+ return url_fetcher_.WriteReady(stream);
+}
+
+int32 ChromeFrameNPAPI::Write(NPStream* stream, int32 offset, int32 len,
+ void* buffer) {
+ return url_fetcher_.Write(stream, offset, len, buffer);
+}
+
+NPError ChromeFrameNPAPI::DestroyStream(NPStream* stream, NPReason reason) {
+ return url_fetcher_.DestroyStream(stream, reason);
+}
diff --git a/chrome_frame/chrome_frame_npapi.h b/chrome_frame/chrome_frame_npapi.h
index 45b4028..c5e17cb 100644
--- a/chrome_frame/chrome_frame_npapi.h
+++ b/chrome_frame/chrome_frame_npapi.h
@@ -65,6 +65,9 @@ class ChromeFrameNPAPI
void UrlNotify(const char* url, NPReason reason, void* notify_data);
bool NewStream(NPMIMEType type, NPStream* stream, NPBool seekable,
uint16* stream_type);
+ int32 WriteReady(NPStream* stream);
+ int32 Write(NPStream* stream, int32 offset, int32 len, void* buffer);
+ NPError DestroyStream(NPStream* stream, NPReason reason);
void Print(NPPrint* print_info);
@@ -99,10 +102,6 @@ class ChromeFrameNPAPI
// which represents our plugin class.
static ChromeFrameNPAPI* ChromeFrameInstanceFromNPObject(void* object);
- // Return a UrlRequest instance associated with the given instance and
- // stream combination.
- static NPAPIUrlRequest* ValidateRequest(NPP instance, void* notify_data);
-
BEGIN_MSG_MAP(ChromeFrameNPAPI)
MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
CHAIN_MSG_MAP(Base)
@@ -137,12 +136,6 @@ END_MSG_MAP()
const std::string& message,
const std::string& origin,
const std::string& target);
- virtual void OnRequestStart(int tab_handle, int request_id,
- const IPC::AutomationURLRequest& request);
- virtual void OnRequestRead(int tab_handle, int request_id,
- int bytes_to_read);
- virtual void OnRequestEnd(int tab_handle, int request_id,
- const URLRequestStatus& status);
virtual void OnSetCookieAsync(int tab_handle, const GURL& url,
const std::string& cookie);
@@ -283,9 +276,6 @@ END_MSG_MAP()
// Host function to compile-time asserts over members of this class.
static void CompileAsserts();
- // Get request from the stream notify data
- NPAPIUrlRequest* RequestFromNotifyData(void* notify_data) const;
-
static LRESULT CALLBACK DropKillFocusHook(int code, WPARAM wparam,
LPARAM lparam); // NO_LINT
@@ -342,6 +332,8 @@ END_MSG_MAP()
// The value of src property keeping the current URL.
std::string src_;
+ // Used to fetch network resources when host network stack is in use.
+ NPAPIUrlRequestManager url_fetcher_;
};
#endif // CHROME_FRAME_CHROME_FRAME_NPAPI_H_
diff --git a/chrome_frame/chrome_frame_npapi_entrypoints.cc b/chrome_frame/chrome_frame_npapi_entrypoints.cc
index 17a0953..ef5af01 100644
--- a/chrome_frame/chrome_frame_npapi_entrypoints.cc
+++ b/chrome_frame/chrome_frame_npapi_entrypoints.cc
@@ -119,27 +119,23 @@ NPError NPP_SetWindow(NPP instance, NPWindow* window_info) {
NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream,
NPBool seekable, uint16* stream_type) {
- NPAPIUrlRequest* url_request = ChromeFrameNPAPI::ValidateRequest(
- instance, stream->notifyData);
- if (url_request) {
- if (!url_request->OnStreamCreated(type, stream))
- return NPERR_GENERIC_ERROR;
+ ChromeFrameNPAPI* plugin_instance =
+ ChromeFrameNPAPI::ChromeFrameInstanceFromPluginInstance(instance);
+ if (plugin_instance == NULL) {
+ return NPERR_INVALID_INSTANCE_ERROR;
}
- // We need to return the requested stream mode if we are returning a success
- // code. If we don't do this it causes Opera to blow up.
- *stream_type = NP_NORMAL;
- return NPERR_NO_ERROR;
+ return plugin_instance->NewStream(type, stream, seekable, stream_type);
}
NPError NPP_DestroyStream(NPP instance, NPStream* stream, NPReason reason) {
- NPAPIUrlRequest* url_request = ChromeFrameNPAPI::ValidateRequest(
- instance, stream->notifyData);
- if (url_request) {
- url_request->OnStreamDestroyed(reason);
+ ChromeFrameNPAPI* plugin_instance =
+ ChromeFrameNPAPI::ChromeFrameInstanceFromPluginInstance(instance);
+ if (plugin_instance == NULL) {
+ return NPERR_INVALID_INSTANCE_ERROR;
}
- return NPERR_NO_ERROR;
+ return plugin_instance->DestroyStream(stream, reason);
}
NPError NPP_GetValue(NPP instance, NPPVariable variable, void* value) {
@@ -163,24 +159,25 @@ NPError NPP_SetValue(NPP instance, NPNVariable variable, void* value) {
int32 NPP_WriteReady(NPP instance, NPStream* stream) {
static const int kMaxBytesForPluginConsumption = 0x7FFFFFFF;
- NPAPIUrlRequest* url_request = ChromeFrameNPAPI::ValidateRequest(
- instance, stream->notifyData);
- if (url_request) {
- return url_request->OnWriteReady();
+ ChromeFrameNPAPI* plugin_instance =
+ ChromeFrameNPAPI::ChromeFrameInstanceFromPluginInstance(instance);
+
+ if (plugin_instance == NULL) {
+ return kMaxBytesForPluginConsumption;
}
- return kMaxBytesForPluginConsumption;
+ return plugin_instance->WriteReady(stream);
}
int32 NPP_Write(NPP instance, NPStream* stream, int32 offset, int32 len,
void* buffer) {
- NPAPIUrlRequest* url_request = ChromeFrameNPAPI::ValidateRequest(
- instance, stream->notifyData);
- if (url_request) {
- return url_request->OnWrite(buffer, len);
- }
+ ChromeFrameNPAPI* plugin_instance =
+ ChromeFrameNPAPI::ChromeFrameInstanceFromPluginInstance(instance);
+
+ if (plugin_instance == NULL)
+ return len;
- return len;
+ return plugin_instance->Write(stream, offset, len, buffer);
}
void NPP_URLNotify(NPP instance, const char* url, NPReason reason,
diff --git a/chrome_frame/http_negotiate.cc b/chrome_frame/http_negotiate.cc
index 5ca49d4..240d9ff 100644
--- a/chrome_frame/http_negotiate.cc
+++ b/chrome_frame/http_negotiate.cc
@@ -52,6 +52,45 @@ BEGIN_VTABLE_PATCHES(IInternetProtocolSink)
HttpNegotiatePatch::ReportProgress)
END_VTABLE_PATCHES()
+class SimpleBindStatusCallback : public CComObjectRootEx<CComSingleThreadModel>,
+ public IBindStatusCallback {
+ public:
+ BEGIN_COM_MAP(SimpleBindStatusCallback)
+ COM_INTERFACE_ENTRY(IBindStatusCallback)
+ END_COM_MAP()
+
+ // IBindStatusCallback implementation
+ STDMETHOD(OnStartBinding)(DWORD reserved, IBinding* binding) {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(GetPriority)(LONG* priority) {
+ return E_NOTIMPL;
+ }
+ STDMETHOD(OnLowResource)(DWORD reserved) {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(OnProgress)(ULONG progress, ULONG max_progress,
+ ULONG status_code, LPCWSTR status_text) {
+ return E_NOTIMPL;
+ }
+ STDMETHOD(OnStopBinding)(HRESULT result, LPCWSTR error) {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(GetBindInfo)(DWORD* bind_flags, BINDINFO* bind_info) {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(OnDataAvailable)(DWORD flags, DWORD size, FORMATETC* formatetc,
+ STGMEDIUM* storage) {
+ return E_NOTIMPL;
+ }
+ STDMETHOD(OnObjectAvailable)(REFIID iid, IUnknown* object) {
+ return E_NOTIMPL;
+ }
+};
HttpNegotiatePatch::HttpNegotiatePatch() {
}
@@ -65,10 +104,9 @@ bool HttpNegotiatePatch::Initialize() {
DLOG(WARNING) << __FUNCTION__ << " called more than once.";
return true;
}
-
- // Use our UrlmonUrlRequest class as we need a temporary object that
+ // Use our SimpleBindStatusCallback class as we need a temporary object that
// implements IBindStatusCallback.
- CComObjectStackEx<UrlmonUrlRequest> request;
+ CComObjectStackEx<SimpleBindStatusCallback> request;
ScopedComPtr<IBindCtx> bind_ctx;
HRESULT hr = CreateAsyncBindCtx(0, &request, NULL, bind_ctx.Receive());
DCHECK(SUCCEEDED(hr)) << "CreateAsyncBindCtx";
diff --git a/chrome_frame/npapi_url_request.cc b/chrome_frame/npapi_url_request.cc
index 33052a1..d7d9a2f 100644
--- a/chrome_frame/npapi_url_request.cc
+++ b/chrome_frame/npapi_url_request.cc
@@ -33,6 +33,39 @@
#include "chrome_frame/np_browser_functions.h"
#include "net/base/net_errors.h"
+class NPAPIUrlRequest : public PluginUrlRequest {
+ public:
+ explicit NPAPIUrlRequest(NPP instance);
+ ~NPAPIUrlRequest();
+
+ virtual bool Start();
+ virtual void Stop();
+ virtual bool Read(int bytes_to_read);
+
+ // Called from NPAPI
+ bool OnStreamCreated(const char* mime_type, NPStream* stream);
+ NPError OnStreamDestroyed(NPReason reason);
+ int OnWriteReady();
+ int OnWrite(void* buffer, int len);
+
+ // Thread unsafe implementation of ref counting, since
+ // this will be called on the plugin UI thread only.
+ virtual unsigned long API_CALL AddRef();
+ virtual unsigned long API_CALL Release();
+
+ private:
+ PluginUrlRequestDelegate* delegate_;
+ unsigned long ref_count_;
+ NPP instance_;
+ NPStream* stream_;
+ size_t pending_read_size_;
+ URLRequestStatus status_;
+
+ PlatformThreadId thread_;
+ static int instance_count_;
+ DISALLOW_COPY_AND_ASSIGN(NPAPIUrlRequest);
+};
+
int NPAPIUrlRequest::instance_count_ = 0;
NPAPIUrlRequest::NPAPIUrlRequest(NPP instance)
@@ -62,9 +95,7 @@ bool NPAPIUrlRequest::Start() {
NOTREACHED() << "PluginUrlRequest only supports 'GET'/'POST'";
}
- if (NPERR_NO_ERROR == result) {
- request_handler()->AddRequest(this);
- } else {
+ if (NPERR_NO_ERROR != result) {
int os_error = net::ERR_FAILED;
switch (result) {
case NPERR_INVALID_URL:
@@ -74,7 +105,8 @@ bool NPAPIUrlRequest::Start() {
break;
}
- OnResponseEnd(URLRequestStatus(URLRequestStatus::FAILED, os_error));
+ delegate_->OnResponseEnd(id(),
+ URLRequestStatus(URLRequestStatus::FAILED, os_error));
return false;
}
@@ -88,10 +120,6 @@ void NPAPIUrlRequest::Stop() {
npapi::DestroyStream(instance_, stream_, NPRES_USER_BREAK);
stream_ = NULL;
}
-
- request_handler()->RemoveRequest(this);
- if (!status_.is_io_pending())
- OnResponseEnd(status_);
}
bool NPAPIUrlRequest::Read(int bytes_to_read) {
@@ -105,13 +133,13 @@ bool NPAPIUrlRequest::OnStreamCreated(const char* mime_type, NPStream* stream) {
// TODO(iyengar)
// Add support for passing persistent cookies and information about any URL
// redirects to Chrome.
- OnResponseStarted(mime_type, stream->headers, stream->end,
+ delegate_->OnResponseStarted(id(), mime_type, stream->headers, stream->end,
base::Time::FromTimeT(stream->lastmodified), std::string(),
std::string(), 0);
return true;
}
-void NPAPIUrlRequest::OnStreamDestroyed(NPReason reason) {
+NPError NPAPIUrlRequest::OnStreamDestroyed(NPReason reason) {
URLRequestStatus::Status status = URLRequestStatus::FAILED;
switch (reason) {
case NPRES_DONE:
@@ -128,6 +156,9 @@ void NPAPIUrlRequest::OnStreamDestroyed(NPReason reason) {
status_.set_os_error(net::ERR_CONNECTION_CLOSED);
break;
}
+
+ delegate_->OnResponseEnd(id(), status_);
+ return NPERR_NO_ERROR;
}
int NPAPIUrlRequest::OnWriteReady() {
@@ -136,7 +167,7 @@ int NPAPIUrlRequest::OnWriteReady() {
int NPAPIUrlRequest::OnWrite(void* buffer, int len) {
pending_read_size_ = 0;
- OnReadComplete(buffer, len);
+ delegate_->OnReadComplete(id(), buffer, len);
return len;
}
@@ -155,3 +186,123 @@ STDMETHODIMP_(ULONG) NPAPIUrlRequest::Release() {
return ret;
}
+NPAPIUrlRequestManager::NPAPIUrlRequestManager() : instance_(NULL) {
+}
+
+NPAPIUrlRequestManager::~NPAPIUrlRequestManager() {
+ StopAll();
+}
+
+// PluginUrlRequestManager implementation
+bool NPAPIUrlRequestManager::IsThreadSafe() {
+ return false;
+}
+
+void NPAPIUrlRequestManager::StartRequest(int request_id,
+ const IPC::AutomationURLRequest& request_info) {
+ scoped_refptr<NPAPIUrlRequest> new_request(new NPAPIUrlRequest(instance_));
+ DCHECK(new_request);
+ if (new_request->Initialize(this, request_id, request_info.url,
+ request_info.method, request_info.referrer,
+ request_info.extra_request_headers, request_info.upload_data.get(),
+ enable_frame_busting_)) {
+ // Add to map.
+ DCHECK(NULL == request_map_[request_id].get());
+ request_map_[request_id] = new_request;
+ if (new_request->Start()) {
+ // Keep additional reference on request for NPSTREAM
+ // This will be released in NPP_UrlNotify
+ new_request->AddRef();
+ }
+ }
+}
+
+void NPAPIUrlRequestManager::ReadRequest(int request_id, int bytes_to_read) {
+ scoped_refptr<NPAPIUrlRequest> request = request_map_[request_id];
+ DCHECK(request.get());
+ if (request)
+ request->Read(bytes_to_read);
+}
+
+void NPAPIUrlRequestManager::EndRequest(int request_id) {
+ scoped_refptr<NPAPIUrlRequest> request = request_map_[request_id];
+ if (request)
+ request->Stop();
+}
+
+void NPAPIUrlRequestManager::StopAll() {
+ for (RequestMap::iterator index = request_map_.begin();
+ index != request_map_.end();
+ ++index) {
+ scoped_refptr<NPAPIUrlRequest> request = (*index).second;
+ request->Stop();
+ }
+}
+
+// PluginRequestDelegate implementation.
+// Callbacks from NPAPIUrlRequest. Simply forward to the delegate.
+void NPAPIUrlRequestManager::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) {
+ delegate_->OnResponseStarted(request_id, mime_type, headers, size,
+ last_modified, peristent_cookies, redirect_url, redirect_status);
+}
+
+void NPAPIUrlRequestManager::OnReadComplete(int request_id, const void* buffer,
+ int len) {
+ delegate_->OnReadComplete(request_id, buffer, len);
+}
+
+void NPAPIUrlRequestManager::OnResponseEnd(int request_id,
+ const URLRequestStatus& status) {
+ // Delete from map.
+ RequestMap::iterator it = request_map_.find(request_id);
+ DCHECK(request_map_.end() != it);
+ scoped_refptr<NPAPIUrlRequest> request = (*it).second;
+ request_map_.erase(it);
+
+ // Inform delegate unless canceled.
+ if (status.status() != URLRequestStatus::CANCELED)
+ delegate_->OnResponseEnd(request_id, status);
+}
+
+// Notifications from browser. Find the NPAPIUrlRequest and forward to it.
+bool NPAPIUrlRequestManager::NewStream(NPMIMEType type, NPStream* stream,
+ NPBool seekable, uint16* stream_type) {
+ NPAPIUrlRequest* request = RequestFromNotifyData(stream->notifyData);
+ DCHECK(request_map_.find(request->id()) != request_map_.end());
+ // We need to return the requested stream mode if we are returning a success
+ // code. If we don't do this it causes Opera to blow up.
+ *stream_type = NP_NORMAL;
+ return request->OnStreamCreated(type, stream);
+}
+
+int32 NPAPIUrlRequestManager::WriteReady(NPStream* stream) {
+ NPAPIUrlRequest* request = RequestFromNotifyData(stream->notifyData);
+ DCHECK(request_map_.find(request->id()) != request_map_.end());
+ return request->OnWriteReady();
+}
+
+int32 NPAPIUrlRequestManager::Write(NPStream* stream, int32 offset,
+ int32 len, void* buffer) {
+ NPAPIUrlRequest* request = RequestFromNotifyData(stream->notifyData);
+ DCHECK(request_map_.find(request->id()) != request_map_.end());
+ return request->OnWrite(buffer, len);
+}
+
+NPError NPAPIUrlRequestManager::DestroyStream(NPStream* stream,
+ NPReason reason) {
+ NPAPIUrlRequest* request = RequestFromNotifyData(stream->notifyData);
+ DCHECK(request_map_.find(request->id()) != request_map_.end());
+ return request->OnStreamDestroyed(reason);
+}
+
+void NPAPIUrlRequestManager::UrlNotify(const char* url, NPReason reason,
+ void* notify_data) {
+ NPAPIUrlRequest* request = RequestFromNotifyData(notify_data);
+ if (request) {
+ request->Stop();
+ request->Release();
+ }
+}
diff --git a/chrome_frame/npapi_url_request.h b/chrome_frame/npapi_url_request.h
index 9151fcf..87e40a9 100644
--- a/chrome_frame/npapi_url_request.h
+++ b/chrome_frame/npapi_url_request.h
@@ -5,40 +5,58 @@
#ifndef CHROME_FRAME_NPAPI_URL_REQUEST_H_
#define CHROME_FRAME_NPAPI_URL_REQUEST_H_
+#include <map>
+
#include "base/platform_thread.h"
#include "chrome_frame/plugin_url_request.h"
#include "third_party/WebKit/WebCore/bridge/npapi.h"
-class NPAPIUrlRequest : public PluginUrlRequest {
+class NPAPIUrlRequest;
+class NPAPIUrlRequestManager : public PluginUrlRequestManager,
+ public PluginUrlRequestDelegate {
public:
- explicit NPAPIUrlRequest(NPP instance);
- ~NPAPIUrlRequest();
-
- virtual bool Start();
- virtual void Stop();
- virtual bool Read(int bytes_to_read);
-
- // Called from NPAPI
- bool OnStreamCreated(const char* mime_type, NPStream* stream);
- void OnStreamDestroyed(NPReason reason);
- int OnWriteReady();
- int OnWrite(void* buffer, int len);
-
- // Thread unsafe implementation of ref counting, since
- // this will be called on the plugin UI thread only.
- virtual unsigned long API_CALL AddRef();
- virtual unsigned long API_CALL Release();
+ NPAPIUrlRequestManager();
+ ~NPAPIUrlRequestManager();
+
+ void set_NPPInstance(NPP instance) {
+ instance_ = instance;
+ }
+
+ // Notifications from the browser. We find the appropriate NPAPIUrlRequest
+ // and forward the call.
+ bool NewStream(NPMIMEType type, NPStream* stream,
+ NPBool seekable, uint16* stream_type);
+ int32 WriteReady(NPStream* stream);
+ int32 Write(NPStream* stream, int32 offset, int32 len, void* buffer);
+ NPError DestroyStream(NPStream* stream, NPReason reason);
+ void UrlNotify(const char* url, NPReason reason, void* notify_data);
private:
- unsigned long ref_count_;
- NPP instance_;
- NPStream* stream_;
- size_t pending_read_size_;
- URLRequestStatus status_;
+ // PluginUrlRequestManager implementation. Called from AutomationClient.
+ virtual bool IsThreadSafe();
+ virtual void StartRequest(int request_id,
+ const IPC::AutomationURLRequest& request_info);
+ virtual void ReadRequest(int request_id, int bytes_to_read);
+ virtual void EndRequest(int request_id);
+ virtual void StopAll();
+
+ // Outstanding requests map.
+ typedef std::map<int, scoped_refptr<NPAPIUrlRequest> > RequestMap;
+ RequestMap request_map_;
+
+ // PluginUrlRequestDelegate implementation. Forwards back to delegate.
+ virtual void 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);
+ virtual void OnReadComplete(int request_id, const void* buffer, int len);
+ virtual void OnResponseEnd(int request_id, const URLRequestStatus& status);
+
+ static inline NPAPIUrlRequest* RequestFromNotifyData(void* notify_data) {
+ return reinterpret_cast<NPAPIUrlRequest*>(notify_data);
+ }
- PlatformThreadId thread_;
- static int instance_count_;
- DISALLOW_COPY_AND_ASSIGN(NPAPIUrlRequest);
+ NPP instance_;
};
#endif // CHROME_FRAME_NPAPI_URL_REQUEST_H_
diff --git a/chrome_frame/plugin_url_request.cc b/chrome_frame/plugin_url_request.cc
index 325ac44..ac746c6 100644
--- a/chrome_frame/plugin_url_request.cc
+++ b/chrome_frame/plugin_url_request.cc
@@ -8,24 +8,20 @@
#include "chrome_frame/np_browser_functions.h"
PluginUrlRequest::PluginUrlRequest()
- : request_handler_(NULL),
- tab_(0),
+ : delegate_(NULL),
remote_request_id_(-1),
post_data_len_(0),
- status_(URLRequestStatus::IO_PENDING),
- frame_busting_enabled_(false) {
+ enable_frame_busting_(false) {
}
PluginUrlRequest::~PluginUrlRequest() {
}
-bool PluginUrlRequest::Initialize(PluginRequestHandler* request_handler,
- int tab, int remote_request_id, const std::string& url,
- const std::string& method, const std::string& referrer,
- const std::string& extra_headers, net::UploadData* upload_data,
- bool enable_frame_busting) {
- request_handler_ = request_handler;
- tab_ = tab;
+bool PluginUrlRequest::Initialize(PluginUrlRequestDelegate* delegate,
+ int remote_request_id, const std::string& url, const std::string& method,
+ const std::string& referrer, const std::string& extra_headers,
+ net::UploadData* upload_data, bool enable_frame_busting) {
+ delegate_ = delegate;
remote_request_id_ = remote_request_id;
url_ = url;
method_ = method;
@@ -49,38 +45,7 @@ bool PluginUrlRequest::Initialize(PluginRequestHandler* request_handler,
}
}
- frame_busting_enabled_ = enable_frame_busting;
+ enable_frame_busting_ = enable_frame_busting;
return true;
}
-
-void PluginUrlRequest::OnResponseStarted(const char* mime_type,
- const char* headers, int size, base::Time last_modified,
- const std::string& persistent_cookies,
- const std::string& redirect_url, int redirect_status) {
- const IPC::AutomationURLResponse response = {
- mime_type,
- headers ? headers : "",
- size,
- last_modified,
- persistent_cookies,
- redirect_url,
- redirect_status
- };
- request_handler_->Send(new AutomationMsg_RequestStarted(0, tab_,
- remote_request_id_, response));
-}
-
-void PluginUrlRequest::OnResponseEnd(const URLRequestStatus& status) {
- DCHECK(!status.is_io_pending());
- DCHECK(status.is_success() || status.os_error());
- request_handler_->Send(new AutomationMsg_RequestEnd(0, tab_,
- remote_request_id_, status));
-}
-
-void PluginUrlRequest::OnReadComplete(const void* buffer, int len) {
- std::string data(reinterpret_cast<const char*>(buffer), len);
- request_handler_->Send(new AutomationMsg_RequestData(0, tab_,
- remote_request_id_, data));
-}
-
diff --git a/chrome_frame/plugin_url_request.h b/chrome_frame/plugin_url_request.h
index 4993770..efd6741 100644
--- a/chrome_frame/plugin_url_request.h
+++ b/chrome_frame/plugin_url_request.h
@@ -18,43 +18,11 @@
#include "net/url_request/url_request_status.h"
class PluginUrlRequest;
+class PluginUrlRequestDelegate;
+class PluginUrlRequestManager;
-// Interface for a class that keeps a collection of outstanding
-// reqeusts and offers an outgoing channel.
-class PluginRequestHandler
- : public IPC::Message::Sender,
- public base::RefCountedThreadSafe<PluginRequestHandler> {
+class DECLSPEC_NOVTABLE PluginUrlRequestDelegate {
public:
- virtual bool AddRequest(PluginUrlRequest* request) = 0;
- virtual void RemoveRequest(PluginUrlRequest* request) = 0;
-};
-
-// A reference counting solution whose method's are compatible with
-// scoped_refptr and COM's IUnknown. Don't cast this object directly over to
-// IUnknown though since IUnknown's first method is QueryInterface.
-class UrlRequestReference {
- public:
- virtual unsigned long API_CALL AddRef() = 0; // NOLINT
- virtual unsigned long API_CALL Release() = 0; // NOLINT
-};
-
-class PluginUrlRequest : public UrlRequestReference {
- public:
- PluginUrlRequest();
- ~PluginUrlRequest();
-
- bool Initialize(PluginRequestHandler* handler, int tab,
- int remote_request_id, const std::string& url,
- const std::string& method, const std::string& referrer,
- const std::string& extra_headers,
- net::UploadData* upload_data,
- bool intercept_frame_options);
-
- // Called in response to automation IPCs
- virtual bool Start() = 0;
- virtual void Stop() = 0;
- virtual bool Read(int bytes_to_read) = 0;
-
// Persistent cookies are read from the host browser and passed off to Chrome
// 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
@@ -63,17 +31,77 @@ class PluginUrlRequest : public UrlRequestReference {
// 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.
- void OnResponseStarted(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);
+ virtual void 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) = 0;
+ virtual void OnReadComplete(int request_id, const void* buffer, int len) = 0;
+ virtual void OnResponseEnd(int request_id, const URLRequestStatus& status) = 0;
+ protected:
+ PluginUrlRequestDelegate() {}
+ ~PluginUrlRequestDelegate() {}
+};
- void OnReadComplete(const void* buffer, int len);
- void OnResponseEnd(const URLRequestStatus& status);
+class DECLSPEC_NOVTABLE PluginUrlRequestManager {
+ public:
+ PluginUrlRequestManager() : delegate_(NULL), enable_frame_busting_(true) {}
+ virtual ~PluginUrlRequestManager() {}
+
+ void set_frame_busting(bool enable) {
+ enable_frame_busting_ = enable;
+ }
+
+ virtual void set_delegate(PluginUrlRequestDelegate* delegate) {
+ delegate_ = delegate;
+ }
+
+ virtual bool IsThreadSafe() = 0;
+
+ // These are called directly from Automation Client when network related
+ // automation messages are received from Chrome.
+ // Strip 'tab' handle and forward to the virtual methods implemented by
+ // derived classes.
+ void StartUrlRequest(int tab, int request_id,
+ const IPC::AutomationURLRequest& request_info) {
+ StartRequest(request_id, request_info);
+ }
+
+ void ReadUrlRequest(int tab, int request_id, int bytes_to_read) {
+ ReadRequest(request_id, bytes_to_read);
+ }
+
+ void EndUrlRequest(int tab, int request_id, const URLRequestStatus& s) {
+ EndRequest(request_id);
+ }
- PluginRequestHandler* request_handler() const {
- return request_handler_;
+ void StopAllRequests() {
+ StopAll();
}
+ protected:
+ PluginUrlRequestDelegate* delegate_;
+ bool enable_frame_busting_;
+
+ private:
+ virtual void StartRequest(int request_id,
+ const IPC::AutomationURLRequest& request_info) = 0;
+ virtual void ReadRequest(int request_id, int bytes_to_read) = 0;
+ virtual void EndRequest(int request_id) = 0;
+ virtual void StopAll() = 0;
+};
+
+// Used as base class. Holds Url request properties (url, method, referrer..)
+class PluginUrlRequest {
+ public:
+ PluginUrlRequest();
+ ~PluginUrlRequest();
+
+ bool Initialize(PluginUrlRequestDelegate* delegate,
+ int remote_request_id, const std::string& url, const std::string& method,
+ const std::string& referrer, const std::string& extra_headers,
+ net::UploadData* upload_data, bool enable_frame_busting_);
+
+ // Accessors.
int id() const {
return remote_request_id_;
}
@@ -86,10 +114,6 @@ class PluginUrlRequest : public UrlRequestReference {
return method_;
}
- void set_method(const std::string& new_method) {
- method_ = new_method;
- }
-
const std::string& referrer() const {
return referrer_;
}
@@ -102,6 +126,7 @@ class PluginUrlRequest : public UrlRequestReference {
return post_data_len_;
}
+ protected:
HRESULT get_upload_data(IStream** ret) {
DCHECK(ret);
if (!upload_data_.get())
@@ -111,26 +136,19 @@ class PluginUrlRequest : public UrlRequestReference {
return S_OK;
}
+ void set_url(const std::string& url) {
+ url_ = url;
+ }
+
void ClearPostData() {
upload_data_.Release();
post_data_len_ = 0;
}
- bool is_done() const {
- return (URLRequestStatus::IO_PENDING != status_);
- }
-
- void set_url(const std::string& url) {
- url_ = url;
- }
-
- protected:
void SendData();
- bool frame_busting_enabled_;
+ bool enable_frame_busting_;
- private:
- scoped_refptr<PluginRequestHandler> request_handler_;
- int tab_;
+ PluginUrlRequestDelegate* delegate_;
int remote_request_id_;
uint64 post_data_len_;
std::string url_;
@@ -138,7 +156,6 @@ class PluginUrlRequest : public UrlRequestReference {
std::string referrer_;
std::string extra_headers_;
ScopedComPtr<IStream> upload_data_;
- URLRequestStatus::Status status_;
};
#endif // CHROME_FRAME_PLUGIN_URL_REQUEST_H_
diff --git a/chrome_frame/test/chrome_frame_test_utils.h b/chrome_frame/test/chrome_frame_test_utils.h
index 277fc52..57e356c 100644
--- a/chrome_frame/test/chrome_frame_test_utils.h
+++ b/chrome_frame/test/chrome_frame_test_utils.h
@@ -130,6 +130,16 @@ class TimedMsgLoop {
MessageLoopForUI loop_;
};
+// Saves typing. It's somewhat hard to create a wrapper around
+// testing::InvokeWithoutArgs since it returns a
+// non-public (testing::internal) type.
+#define QUIT_LOOP(loop) testing::InvokeWithoutArgs(\
+ CreateFunctor(&loop, &chrome_frame_test::TimedMsgLoop::Quit))
+
+#define QUIT_LOOP_SOON(loop, seconds) testing::InvokeWithoutArgs(\
+ CreateFunctor(&loop, &chrome_frame_test::TimedMsgLoop::QuitAfter, \
+ seconds))
+
// Launches IE as a COM server and returns the corresponding IWebBrowser2
// interface pointer.
// Returns S_OK on success.
diff --git a/chrome_frame/test/chrome_frame_unittests.cc b/chrome_frame/test/chrome_frame_unittests.cc
index 2795de5..f343ff2c 100644
--- a/chrome_frame/test/chrome_frame_unittests.cc
+++ b/chrome_frame/test/chrome_frame_unittests.cc
@@ -21,7 +21,6 @@
#include "chrome_frame/test/chrome_frame_unittests.h"
#include "chrome_frame/chrome_frame_automation.h"
#include "chrome_frame/chrome_frame_delegate.h"
-#include "chrome_frame/crash_reporting/vectored_handler-impl.h"
#include "chrome_frame/test/chrome_frame_test_utils.h"
#include "chrome_frame/test_utils.h"
#include "chrome/common/chrome_switches.h"
@@ -892,16 +891,6 @@ template <> struct RunnableMethodTraits<chrome_frame_test::TimedMsgLoop> {
void ReleaseCallee(chrome_frame_test::TimedMsgLoop* obj) {}
};
-// Saves typing. It's somewhat hard to create a wrapper around
-// testing::InvokeWithoutArgs since it returns a
-// non-public (testing::internal) type.
-#define QUIT_LOOP(loop) testing::InvokeWithoutArgs(\
- CreateFunctor(&loop, &chrome_frame_test::TimedMsgLoop::Quit))
-
-#define QUIT_LOOP_SOON(loop, seconds) testing::InvokeWithoutArgs(\
- CreateFunctor(&loop, &chrome_frame_test::TimedMsgLoop::QuitAfter, \
- seconds))
-
// We mock ChromeFrameDelegate only. The rest is with real AutomationProxy
TEST(CFACWithChrome, CreateTooFast) {
MockCFDelegate cfd;
diff --git a/chrome_frame/test/url_request_test.cc b/chrome_frame/test/url_request_test.cc
new file mode 100644
index 0000000..e095aa5
--- /dev/null
+++ b/chrome_frame/test/url_request_test.cc
@@ -0,0 +1,152 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#include <atlbase.h>
+#include <atlcom.h>
+#include "app/win_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gmock_mutant.h"
+
+#include "chrome_frame/urlmon_url_request.h"
+#include "chrome_frame/urlmon_url_request_private.h"
+#include "chrome_frame/test/chrome_frame_test_utils.h"
+#include "chrome_frame/test/http_server.h"
+using testing::CreateFunctor;
+
+const int kChromeFrameLongNavigationTimeoutInSeconds = 10;
+
+class MockUrlDelegate : public PluginUrlRequestDelegate {
+ public:
+ MOCK_METHOD8(OnResponseStarted, void(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));
+ MOCK_METHOD3(OnReadComplete, void(int request_id, const void* buffer,
+ int len));
+ MOCK_METHOD2(OnResponseEnd, void(int request_id,
+ const URLRequestStatus& status));
+
+ static bool ImplementsThreadSafeReferenceCounting() {
+ return false;
+ }
+ void AddRef() {}
+ void Release() {}
+
+ void PostponeReadRequest(chrome_frame_test::TimedMsgLoop* loop,
+ UrlmonUrlRequest* request, int bytes_to_read) {
+ loop->PostDelayedTask(FROM_HERE, NewRunnableMethod(this,
+ &MockUrlDelegate::Read, request, bytes_to_read), 0);
+ }
+
+ private:
+ void Read(UrlmonUrlRequest* request, int bytes_to_read) {
+ request->Read(bytes_to_read);
+ }
+};
+
+// Simplest UrlmonUrlRequest. Retrieve a file from local web server.
+TEST(UrlmonUrlRequestTest, Simple1) {
+ MockUrlDelegate mock;
+ ChromeFrameHTTPServer server;
+ chrome_frame_test::TimedMsgLoop loop;
+ win_util::ScopedCOMInitializer init_com;
+ CComObjectStackEx<UrlmonUrlRequest> request;
+
+ server.SetUp();
+ request.AddRef();
+ request.Initialize(&mock, 1, // request_id
+ server.Resolve(L"chrome_frame_window_open.html").spec(),
+ "get",
+ "", // referrer
+ "", // extra request
+ NULL, // upload data
+ true); // frame busting
+
+ EXPECT_CALL(mock, OnResponseStarted(1, testing::_, testing::_, testing::_,
+ testing::_, testing::_, testing::_,
+ testing::_))
+ .Times(1)
+ .WillOnce(testing::IgnoreResult(testing::InvokeWithoutArgs(CreateFunctor(
+ &request, &UrlmonUrlRequest::Read, 512))));
+
+
+ EXPECT_CALL(mock, OnReadComplete(1, testing::_, testing::Gt(0)))
+ .Times(testing::AtLeast(1))
+ .WillRepeatedly(testing::InvokeWithoutArgs(CreateFunctor(&mock,
+ &MockUrlDelegate::PostponeReadRequest, &loop, &request, 64)));
+
+ EXPECT_CALL(mock, OnResponseEnd(1, testing::_))
+ .Times(1)
+ .WillOnce(QUIT_LOOP_SOON(loop, 2));
+
+ request.Start();
+ loop.RunFor(kChromeFrameLongNavigationTimeoutInSeconds);
+ request.Release();
+ server.TearDown();
+}
+
+// Simplest test - retrieve file from local web server.
+TEST(UrlmonUrlRequestManagerTest, Simple1) {
+ MockUrlDelegate mock;
+ ChromeFrameHTTPServer server;
+ chrome_frame_test::TimedMsgLoop loop;
+ server.SetUp();
+ scoped_ptr<UrlmonUrlRequestManager> mgr(new UrlmonUrlRequestManager());
+ mgr->set_delegate(&mock);
+ IPC::AutomationURLRequest r1 = {
+ server.Resolve(L"chrome_frame_window_open.html").spec(), "get" };
+
+ EXPECT_CALL(mock, OnResponseStarted(1, testing::_, testing::_, testing::_,
+ testing::_, testing::_, testing::_, testing::_))
+ .Times(1)
+ .WillOnce(testing::InvokeWithoutArgs(CreateFunctor(mgr.get(),
+ &PluginUrlRequestManager::ReadUrlRequest, 0, 1, 512)));
+
+ EXPECT_CALL(mock, OnReadComplete(1, testing::_, testing::Gt(0)))
+ .Times(testing::AtLeast(1))
+ .WillRepeatedly(testing::InvokeWithoutArgs(CreateFunctor(mgr.get(),
+ &PluginUrlRequestManager::ReadUrlRequest, 0, 1, 2)));
+
+ EXPECT_CALL(mock, OnResponseEnd(1, testing::_))
+ .Times(1)
+ .WillOnce(QUIT_LOOP_SOON(loop, 2));
+
+ mgr->StartUrlRequest(0, 1, r1);
+ loop.RunFor(kChromeFrameLongNavigationTimeoutInSeconds);
+ mgr.reset();
+ server.TearDown();
+
+}
+
+TEST(UrlmonUrlRequestManagerTest, Abort1) {
+ MockUrlDelegate mock;
+ ChromeFrameHTTPServer server;
+ chrome_frame_test::TimedMsgLoop loop;
+ server.SetUp();
+ scoped_ptr<UrlmonUrlRequestManager> mgr(new UrlmonUrlRequestManager());
+ mgr->set_delegate(&mock);
+ IPC::AutomationURLRequest r1 = {
+ server.Resolve(L"chrome_frame_window_open.html").spec(), "get" };
+
+ EXPECT_CALL(mock, OnResponseStarted(1, testing::_, testing::_, testing::_,
+ testing::_, testing::_, testing::_, testing::_))
+ .Times(1)
+ .WillOnce(testing::DoAll(
+ testing::InvokeWithoutArgs(CreateFunctor(mgr.get(),
+ &PluginUrlRequestManager::EndUrlRequest, 0, 1, URLRequestStatus())),
+ testing::InvokeWithoutArgs(CreateFunctor(mgr.get(),
+ &PluginUrlRequestManager::ReadUrlRequest, 0, 1, 2))));
+
+ EXPECT_CALL(mock, OnReadComplete(1, testing::_, testing::Gt(0)))
+ .Times(0);
+
+ EXPECT_CALL(mock, OnResponseEnd(1, testing::_))
+ .Times(1)
+ .WillOnce(QUIT_LOOP_SOON(loop, 2));
+
+ mgr->StartUrlRequest(0, 1, r1);
+ loop.RunFor(kChromeFrameLongNavigationTimeoutInSeconds);
+ mgr.reset();
+ server.TearDown();
+}
diff --git a/chrome_frame/urlmon_url_request.cc b/chrome_frame/urlmon_url_request.cc
index bf78269..42b1000 100644
--- a/chrome_frame/urlmon_url_request.cc
+++ b/chrome_frame/urlmon_url_request.cc
@@ -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 @@ class WrappedBindContext : public IBindCtx,
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 @@ UrlmonUrlRequest::~UrlmonUrlRequest() {
}
bool UrlmonUrlRequest::Start() {
- DCHECK_EQ(PlatformThread::CurrentId(), thread_);
-
- if (!worker_thread_ || !worker_thread_->message_loop()) {
- NOTREACHED() << __FUNCTION__ << " Urlmon request thread not initialized";
- return false;
- }
-
- Create(HWND_MESSAGE);
- if (!IsWindow()) {
- NOTREACHED() << "Failed to create urlmon message window: "
- << GetLastError();
- return false;
+ thread_ = PlatformThread::CurrentId();
+ status_.Start();
+ HRESULT hr = StartAsyncDownload();
+ if (FAILED(hr)) {
+ status_.set_result(URLRequestStatus::FAILED, HresultToNetError(hr));
+ NotifyDelegateAndDie();
}
-
- // 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_);
-
- if (!worker_thread_ || !worker_thread_->message_loop()) {
- NOTREACHED() << __FUNCTION__ << " Urlmon request thread not initialized";
- return;
- }
-
- // 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;
- }
-}
+ 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;
-void UrlmonUrlRequest::StopAsync() {
- DCHECK(worker_thread_ != NULL);
+ case Status::ABORTING:
+ status_.Cancel();
+ break;
- if (binding_) {
- binding_->Abort();
- } else {
- status_.set_status(URLRequestStatus::CANCELED);
- status_.set_os_error(net::ERR_FAILED);
- EndRequest();
+ case Status::DONE:
+ status_.Cancel();
+ NotifyDelegateAndDie();
+ break;
}
}
-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::OnLowResource(DWORD reserved) {
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::OnProgress(ULONG progress, ULONG max_progress,
}
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();
+
+ // 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;
+ }
- 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);
+ 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();
- // 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_);
- }
+ 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 @@ STDMETHODIMP UrlmonUrlRequest::GetBindInfo(DWORD* bind_flags,
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::GetBindInfo(DWORD* bind_flags,
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 @@ STDMETHODIMP UrlmonUrlRequest::OnDataAvailable(DWORD flags, DWORD size,
}
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::OnObjectAvailable(REFIID iid, IUnknown* object) {
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 @@ STDMETHODIMP UrlmonUrlRequest::BeginningTransaction(const wchar_t* url,
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 @@ STDMETHODIMP UrlmonUrlRequest::BeginningTransaction(const wchar_t* url,
// 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::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(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 @@ STDMETHODIMP UrlmonUrlRequest::OnResponse(DWORD dwResponseCode,
// 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 @@ STDMETHODIMP UrlmonUrlRequest::OnResponse(DWORD dwResponseCode,
}
}
- 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 @@ STDMETHODIMP UrlmonUrlRequest::OnSecurityProblem(DWORD problem) {
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 @@ HRESULT UrlmonUrlRequest::StartAsyncDownload() {
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,46 +821,15 @@ HRESULT UrlmonUrlRequest::StartAsyncDownload() {
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();
- // 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();
+ if (delegate) {
+ delegate->OnResponseEnd(id(), status_.get_result());
+ }
}
int UrlmonUrlRequest::GetHttpResponseStatus() const {
@@ -1096,3 +995,236 @@ net::Error UrlmonUrlRequest::HresultToNetError(HRESULT hr) {
}
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();
+}
diff --git a/chrome_frame/urlmon_url_request.h b/chrome_frame/urlmon_url_request.h
index d4abee5..e47edcc 100644
--- a/chrome_frame/urlmon_url_request.h
+++ b/chrome_frame/urlmon_url_request.h
@@ -8,263 +8,77 @@
#include <urlmon.h>
#include <atlbase.h>
#include <atlcom.h>
-#include <atlwin.h>
-#include <algorithm>
#include <string>
-#include "base/lock.h"
-#include "base/platform_thread.h"
-#include "base/thread.h"
#include "base/scoped_comptr_win.h"
+#include "base/thread.h"
+#include "base/waitable_event.h"
#include "chrome_frame/plugin_url_request.h"
-#include "chrome_frame/chrome_frame_delegate.h"
+#include "chrome_frame/utils.h"
-#include "net/base/net_errors.h"
-#include "net/base/upload_data.h"
+class UrlmonUrlRequest;
-class UrlmonUrlRequest
- : public CComObjectRootEx<CComMultiThreadModel>,
- public PluginUrlRequest,
- public IServiceProviderImpl<UrlmonUrlRequest>,
- public IBindStatusCallback,
- public IHttpNegotiate,
- public IAuthenticate,
- public IHttpSecurity,
- public CWindowImpl<UrlmonUrlRequest>,
- public TaskMarshallerThroughWindowsMessages<UrlmonUrlRequest> {
+class UrlmonUrlRequestManager :
+ public PluginUrlRequestManager,
+ public PluginUrlRequestDelegate {
public:
- typedef TaskMarshallerThroughWindowsMessages<UrlmonUrlRequest>
- TaskMarshaller;
-
- UrlmonUrlRequest();
- ~UrlmonUrlRequest();
-
-BEGIN_COM_MAP(UrlmonUrlRequest)
- COM_INTERFACE_ENTRY(IHttpNegotiate)
- COM_INTERFACE_ENTRY(IServiceProvider)
- COM_INTERFACE_ENTRY(IBindStatusCallback)
- COM_INTERFACE_ENTRY(IWindowForBindingUI)
- COM_INTERFACE_ENTRY(IAuthenticate)
- COM_INTERFACE_ENTRY(IHttpSecurity)
-END_COM_MAP()
-
-BEGIN_SERVICE_MAP(UrlmonUrlRequest)
- SERVICE_ENTRY(IID_IHttpNegotiate);
-END_SERVICE_MAP()
-
-BEGIN_MSG_MAP(UrlmonUrlRequest)
- CHAIN_MSG_MAP(TaskMarshaller)
-END_MSG_MAP()
-
- // PluginUrlRequest implementation
- virtual bool Start();
- virtual void Stop();
- virtual bool Read(int bytes_to_read);
-
- void TransferToHost(IUnknown* host);
-
- // IBindStatusCallback implementation
- STDMETHOD(OnStartBinding)(DWORD reserved, IBinding* binding);
- STDMETHOD(GetPriority)(LONG* priority);
- STDMETHOD(OnLowResource)(DWORD reserved);
- STDMETHOD(OnProgress)(ULONG progress, ULONG max_progress,
- ULONG status_code, LPCWSTR status_text);
- STDMETHOD(OnStopBinding)(HRESULT result, LPCWSTR error);
- STDMETHOD(GetBindInfo)(DWORD* bind_flags, BINDINFO* bind_info);
- STDMETHOD(OnDataAvailable)(DWORD flags, DWORD size, FORMATETC* formatetc,
- STGMEDIUM* storage);
- STDMETHOD(OnObjectAvailable)(REFIID iid, IUnknown* object);
-
- // IHttpNegotiate implementation
- STDMETHOD(BeginningTransaction)(const wchar_t* url,
- const wchar_t* current_headers, DWORD reserved,
- wchar_t** additional_headers);
- STDMETHOD(OnResponse)(DWORD dwResponseCode, const wchar_t* response_headers,
- const wchar_t* request_headers, wchar_t** additional_headers);
-
- // IWindowForBindingUI implementation. This interface is used typically to
- // query the window handle which URLMON uses as the parent of error dialogs.
- STDMETHOD(GetWindow)(REFGUID guid_reason, HWND* parent_window);
-
- // IAuthenticate implementation. Used to return the parent window for the
- // dialog displayed by IE for authenticating with a proxy.
- STDMETHOD(Authenticate)(HWND* parent_window, LPWSTR* user_name,
- LPWSTR* password);
-
- // IHttpSecurity implementation.
- STDMETHOD(OnSecurityProblem)(DWORD problem);
-
- HRESULT ConnectToExistingMoniker(IMoniker* moniker, IBindCtx* context,
- const std::wstring& url);
-
- void set_parent_window(HWND parent_window) {
- 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;
- }
-
- virtual void OnFinalMessage(HWND window);
-
- protected:
- // The following functions issue and handle Urlmon requests on the dedicated
- // Urlmon thread.
- void StartAsync();
- void StopAsync();
- void ReadAsync(int bytes_to_read);
- void ReleaseBindings();
-
- static const size_t kCopyChunkSize = 32 * 1024;
- // URL requests are handled on this thread.
- base::Thread* worker_thread_;
-
- // A fake stream class to make it easier to copy received data using
- // IStream::CopyTo instead of allocating temporary buffers and keeping
- // track of data copied so far.
- class SendStream
- : public CComObjectRoot,
- public IStream {
- public:
- SendStream() {
- }
-
- BEGIN_COM_MAP(SendStream)
- COM_INTERFACE_ENTRY(IStream)
- COM_INTERFACE_ENTRY(ISequentialStream)
- END_COM_MAP()
+ UrlmonUrlRequestManager();
+ ~UrlmonUrlRequestManager();
- void Initialize(UrlmonUrlRequest* request) {
- request_ = request;
- }
-
- STDMETHOD(Read)(void* pv, ULONG cb, ULONG* read) {
- DCHECK(false) << __FUNCTION__;
- return E_NOTIMPL;
- }
-
- STDMETHOD(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_->OnReadComplete(buffer, size_to_write);
- if (size_written)
- *size_written = size_to_write;
- return S_OK;
- }
-
- STDMETHOD(Seek)(LARGE_INTEGER move, DWORD origin, ULARGE_INTEGER* new_pos) {
- DCHECK(false) << __FUNCTION__;
- return E_NOTIMPL;
- }
-
- STDMETHOD(SetSize)(ULARGE_INTEGER new_size) {
- DCHECK(false) << __FUNCTION__;
- return E_NOTIMPL;
- }
-
- STDMETHOD(CopyTo)(IStream* stream, ULARGE_INTEGER cb, ULARGE_INTEGER* read,
- ULARGE_INTEGER* written) {
- DCHECK(false) << __FUNCTION__;
- return E_NOTIMPL;
- }
-
- STDMETHOD(Commit)(DWORD flags) {
- DCHECK(false) << __FUNCTION__;
- return E_NOTIMPL;
- }
-
- STDMETHOD(Revert)() {
- DCHECK(false) << __FUNCTION__;
- return E_NOTIMPL;
- }
-
- STDMETHOD(LockRegion)(ULARGE_INTEGER offset, ULARGE_INTEGER cb,
- DWORD type) {
- DCHECK(false) << __FUNCTION__;
- return E_NOTIMPL;
- }
-
- STDMETHOD(UnlockRegion)(ULARGE_INTEGER offset, ULARGE_INTEGER cb,
- DWORD type) {
- DCHECK(false) << __FUNCTION__;
- return E_NOTIMPL;
- }
-
- STDMETHOD(Stat)(STATSTG *pstatstg, DWORD grfStatFlag) {
- return E_NOTIMPL;
- }
-
- STDMETHOD(Clone)(IStream** stream) {
- DCHECK(false) << __FUNCTION__;
- return E_NOTIMPL;
- }
-
- protected:
- scoped_refptr<UrlmonUrlRequest> request_;
- DISALLOW_COPY_AND_ASSIGN(SendStream);
- };
-
- // Manage data caching. Note: this class supports cache
- // size less than 2GB
- class Cache {
- public:
- // Adds data to the end of the cache.
- bool Append(IStream* source, size_t* bytes_copied);
-
- // Reads from the cache.
- bool Read(IStream* dest, size_t size, size_t* bytes_copied);
-
- // Returns the size of the cache.
- size_t Size() const;
-
- // Returns true if the cache has valid data.
- bool is_valid() const {
- return Size() != 0;
- }
-
- protected:
- std::vector<byte> cache_;
- char read_buffer_[kCopyChunkSize];
- };
-
- HRESULT StartAsyncDownload();
- // Sends over the response end notification to chrome, releases the bindings
- // and releases the initial reference on the UrlmonUrlRequest object.
- // After this function is called we should not attempt to access any members
- // as the object could become invalid at any point.
- 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;
- std::string GetHttpHeaders() const;
-
- static net::Error HresultToNetError(HRESULT hr);
+ // Use specific moniker and bind context when Chrome request this url.
+ // Used from ChromeActiveDocument's implementation of IPersistMoniker::Load().
+ void UseMonikerForUrl(IMoniker* moniker, IBindCtx* bind_ctx,
+ const std::wstring& url);
+ void StealMonikerFromRequest(int request_id, IMoniker** moniker);
private:
- std::wstring redirect_url_;
- int redirect_status_;
- ScopedComPtr<IBinding> binding_;
- ScopedComPtr<IMoniker> moniker_;
- ScopedComPtr<IBindCtx> bind_context_;
- Cache cached_data_;
- size_t pending_read_size_;
- URLRequestStatus status_;
-
- PlatformThreadId thread_;
- static int instance_count_;
- HWND parent_window_;
- // Set to true if a redirect notification was aborted.
- bool ignore_redirect_stop_binding_error_;
-
- DISALLOW_COPY_AND_ASSIGN(UrlmonUrlRequest);
+ struct MonikerForUrl {
+ ScopedComPtr<IMoniker> moniker;
+ ScopedComPtr<IBindCtx> bind_ctx;
+ std::wstring url;
+ };
+
+ friend class MessageLoop;
+ friend struct RunnableMethodTraits<UrlmonUrlRequestManager>;
+ static bool ImplementsThreadSafeReferenceCounting() { return true; }
+ void AddRef() {}
+ void Release() {}
+
+ // PluginUrlRequestManager implementation.
+ virtual bool IsThreadSafe();
+ virtual void StartRequest(int request_id,
+ const IPC::AutomationURLRequest& request_info);
+ virtual void ReadRequest(int request_id, int bytes_to_read);
+ virtual void EndRequest(int request_id);
+ virtual void StopAll();
+
+ // PluginUrlRequestDelegate implementation
+ virtual void 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);
+ virtual void OnReadComplete(int request_id, const void* buffer, int len);
+ virtual void OnResponseEnd(int request_id, const URLRequestStatus& status);
+
+ // Methods executed in worker thread.
+ void StartRequestWorker(int request_id,
+ const IPC::AutomationURLRequest& request_info,
+ MonikerForUrl* moniker_for_url);
+ void ReadRequestWorker(int request_id, int bytes_to_read);
+ void EndRequestWorker(int request_id);
+ void StopAllWorker();
+ void StealMonikerFromRequestWorker(int request_id, IMoniker** moniker,
+ base::WaitableEvent* done);
+
+ // Map for (request_id <-> UrlmonUrlRequest)
+ typedef std::map<int, scoped_refptr<UrlmonUrlRequest> > RequestMap;
+ RequestMap request_map_;
+ scoped_refptr<UrlmonUrlRequest> LookupRequest(int request_id);
+
+ scoped_ptr<MonikerForUrl> moniker_for_url_;
+ STAThread worker_thread_;
+ base::WaitableEvent map_empty_;
+ bool stopping_;
};
#endif // CHROME_FRAME_URLMON_URL_REQUEST_H_
diff --git a/chrome_frame/urlmon_url_request_private.h b/chrome_frame/urlmon_url_request_private.h
new file mode 100644
index 0000000..9b42904
--- /dev/null
+++ b/chrome_frame/urlmon_url_request_private.h
@@ -0,0 +1,307 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_FRAME_URLMON_URL_REQUEST_PRIVATE_H_
+#define CHROME_FRAME_URLMON_URL_REQUEST_PRIVATE_H_
+
+#include <atlbase.h>
+#include <atlcom.h>
+#include <string>
+#include <vector>
+
+#include "net/base/net_errors.h"
+#include "net/http/http_response_headers.h"
+#include "net/url_request/url_request_status.h"
+
+class UrlmonUrlRequest
+ : public CComObjectRootEx<CComMultiThreadModel>,
+ public PluginUrlRequest,
+ public IServiceProviderImpl<UrlmonUrlRequest>,
+ public IBindStatusCallback,
+ public IHttpNegotiate,
+ public IAuthenticate,
+ public IHttpSecurity {
+ public:
+ static int instance_count_;
+ virtual bool Start();
+ virtual void Stop();
+ virtual bool Read(int bytes_to_read);
+
+ // Special function needed by ActiveDocument::Load()
+ HRESULT ConnectToExistingMoniker(IMoniker* moniker, IBindCtx* context,
+ const std::wstring& url);
+
+ // Used from "OnDownloadRequestInHost".
+ void StealMoniker(IMoniker** moniker);
+
+ // Parent Window for UrlMon error dialogs
+ void set_parent_window(HWND parent_window) {
+ parent_window_ = parent_window;
+ }
+
+ protected:
+ UrlmonUrlRequest();
+ ~UrlmonUrlRequest();
+
+ BEGIN_COM_MAP(UrlmonUrlRequest)
+ COM_INTERFACE_ENTRY(IHttpNegotiate)
+ COM_INTERFACE_ENTRY(IServiceProvider)
+ COM_INTERFACE_ENTRY(IBindStatusCallback)
+ COM_INTERFACE_ENTRY(IWindowForBindingUI)
+ COM_INTERFACE_ENTRY(IAuthenticate)
+ COM_INTERFACE_ENTRY(IHttpSecurity)
+ END_COM_MAP()
+
+ BEGIN_SERVICE_MAP(UrlmonUrlRequest)
+ SERVICE_ENTRY(IID_IHttpNegotiate);
+ END_SERVICE_MAP()
+
+
+ // IBindStatusCallback implementation
+ STDMETHOD(OnStartBinding)(DWORD reserved, IBinding* binding);
+ STDMETHOD(GetPriority)(LONG* priority);
+ STDMETHOD(OnLowResource)(DWORD reserved);
+ STDMETHOD(OnProgress)(ULONG progress, ULONG max_progress,
+ ULONG status_code, LPCWSTR status_text);
+ STDMETHOD(OnStopBinding)(HRESULT result, LPCWSTR error);
+ STDMETHOD(GetBindInfo)(DWORD* bind_flags, BINDINFO* bind_info);
+ STDMETHOD(OnDataAvailable)(DWORD flags, DWORD size, FORMATETC* formatetc,
+ STGMEDIUM* storage);
+ STDMETHOD(OnObjectAvailable)(REFIID iid, IUnknown* object);
+
+ // IHttpNegotiate implementation
+ STDMETHOD(BeginningTransaction)(const wchar_t* url,
+ const wchar_t* current_headers, DWORD reserved,
+ wchar_t** additional_headers);
+ STDMETHOD(OnResponse)(DWORD dwResponseCode, const wchar_t* response_headers,
+ const wchar_t* request_headers, wchar_t** additional_headers);
+
+ // IWindowForBindingUI implementation. This interface is used typically to
+ // query the window handle which URLMON uses as the parent of error dialogs.
+ STDMETHOD(GetWindow)(REFGUID guid_reason, HWND* parent_window);
+
+ // IAuthenticate implementation. Used to return the parent window for the
+ // dialog displayed by IE for authenticating with a proxy.
+ STDMETHOD(Authenticate)(HWND* parent_window, LPWSTR* user_name,
+ LPWSTR* password);
+
+ // IHttpSecurity implementation.
+ STDMETHOD(OnSecurityProblem)(DWORD problem);
+
+ protected:
+ void ReleaseBindings();
+
+ static const size_t kCopyChunkSize = 32 * 1024;
+ // A fake stream class to make it easier to copy received data using
+ // IStream::CopyTo instead of allocating temporary buffers and keeping
+ // track of data copied so far.
+ class SendStream : public CComObjectRoot, public IStream {
+ public:
+ SendStream() {
+ }
+
+ BEGIN_COM_MAP(SendStream)
+ COM_INTERFACE_ENTRY(IStream)
+ COM_INTERFACE_ENTRY(ISequentialStream)
+ END_COM_MAP()
+
+ void Initialize(UrlmonUrlRequest* request) {
+ request_ = request;
+ }
+
+ STDMETHOD(Write)(const void * buffer, ULONG size, ULONG* size_written);
+ STDMETHOD(Read)(void* pv, ULONG cb, ULONG* read) {
+ DCHECK(false) << __FUNCTION__;
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(Seek)(LARGE_INTEGER move, DWORD origin, ULARGE_INTEGER* new_pos) {
+ DCHECK(false) << __FUNCTION__;
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(SetSize)(ULARGE_INTEGER new_size) {
+ DCHECK(false) << __FUNCTION__;
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(CopyTo)(IStream* stream, ULARGE_INTEGER cb, ULARGE_INTEGER* read,
+ ULARGE_INTEGER* written) {
+ DCHECK(false) << __FUNCTION__;
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(Commit)(DWORD flags) {
+ DCHECK(false) << __FUNCTION__;
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(Revert)() {
+ DCHECK(false) << __FUNCTION__;
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(LockRegion)(ULARGE_INTEGER offset, ULARGE_INTEGER cb,
+ DWORD type) {
+ DCHECK(false) << __FUNCTION__;
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(UnlockRegion)(ULARGE_INTEGER offset, ULARGE_INTEGER cb,
+ DWORD type) {
+ DCHECK(false) << __FUNCTION__;
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(Stat)(STATSTG *pstatstg, DWORD grfStatFlag) {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(Clone)(IStream** stream) {
+ DCHECK(false) << __FUNCTION__;
+ return E_NOTIMPL;
+ }
+
+ protected:
+ scoped_refptr<UrlmonUrlRequest> request_;
+ DISALLOW_COPY_AND_ASSIGN(SendStream);
+ };
+
+ // Manage data caching. Note: this class supports cache
+ // size less than 2GB
+ class Cache {
+ public:
+ // Adds data to the end of the cache.
+ bool Append(IStream* source, size_t* bytes_copied);
+
+ // Reads from the cache.
+ bool Read(IStream* dest, size_t size, size_t* bytes_copied);
+
+ // Returns the size of the cache.
+ size_t Size() const;
+
+ // Returns true if the cache has valid data.
+ bool is_valid() const {
+ return Size() != 0;
+ }
+
+ protected:
+ std::vector<byte> cache_;
+ char read_buffer_[kCopyChunkSize];
+ };
+
+ HRESULT StartAsyncDownload();
+ void NotifyDelegateAndDie();
+ int GetHttpResponseStatus() const;
+ std::string GetHttpHeaders() const;
+ static net::Error HresultToNetError(HRESULT hr);
+
+ private:
+ // This class simplifies tracking the progress of operation. We have 3 main
+ // states: DONE, WORKING and ABORTING.
+ // When in [DONE] or [ABORTING] state, there is additional information
+ // about the result of operation.
+ // Start(), SetRedirected(), Cancel() and Done() methods trigger the state
+ // change. See comments bellow.
+ class Status {
+ public:
+ enum State {DONE, ABORTING, WORKING};
+ struct Redirection {
+ Redirection() : http_code(0) { }
+ int http_code;
+ std::string utf8_url;
+ };
+
+ Status() : state_(Status::DONE) {
+ }
+
+ State get_state() const {
+ return state_;
+ }
+
+ // Switch from [DONE] to [WORKING].
+ void Start() {
+ DCHECK_EQ(state_, DONE);
+ state_ = WORKING;
+ }
+
+ // Save redirection information and switch to [ABORTING] state.
+ // Assumes binding_->Abort() will be called!
+ void SetRedirected(int http_code, const std::string& utf8_url) {
+ DCHECK_EQ(state_, WORKING);
+ DCHECK_EQ(result_.status(), URLRequestStatus::SUCCESS);
+ redirect_.utf8_url = utf8_url;
+
+ // At times we receive invalid redirect codes like 0, 200, etc. We
+ // default to 302 in this case.
+ redirect_.http_code = http_code;
+ if (!net::HttpResponseHeaders::IsRedirectResponseCode(http_code))
+ redirect_.http_code = 302;
+
+ state_ = ABORTING;
+ }
+
+ // Set the result as URLRequestStatus::CANCELED.
+ // Switch to [ABORTING] state (if not already in that state).
+ void Cancel() {
+ if (state_ == DONE)
+ return;
+
+ if (state_ == WORKING) {
+ state_ = ABORTING;
+ } else {
+ // state_ == ABORTING
+ redirect_.http_code = 0;
+ redirect_.utf8_url.clear();
+ }
+
+ set_result(URLRequestStatus::CANCELED, 0);
+ }
+
+ void Done() {
+ state_ = DONE;
+ }
+
+ bool was_redirected() const {
+ return redirect_.http_code != 0;
+ }
+
+ const Redirection& get_redirection() const {
+ return redirect_;
+ }
+
+ const URLRequestStatus& get_result() const {
+ return result_;
+ }
+
+ void set_result(URLRequestStatus::Status status, int os_error) {
+ result_.set_status(status);
+ result_.set_os_error(os_error);
+ }
+
+ void set_result(HRESULT hr) {
+ result_.set_status(FAILED(hr)? URLRequestStatus::FAILED:
+ URLRequestStatus::SUCCESS);
+ result_.set_os_error(HresultToNetError(hr));
+ }
+
+ private:
+ Redirection redirect_;
+ State state_;
+ URLRequestStatus result_;
+ };
+
+ Status status_;
+ ScopedComPtr<IBinding> binding_;
+ ScopedComPtr<IMoniker> moniker_;
+ ScopedComPtr<IBindCtx> bind_context_;
+ Cache cached_data_;
+ size_t pending_read_size_;
+ PlatformThreadId thread_;
+ HWND parent_window_;
+
+ DISALLOW_COPY_AND_ASSIGN(UrlmonUrlRequest);
+};
+
+#endif // CHROME_FRAME_URLMON_URL_REQUEST_PRIVATE_H_
diff --git a/chrome_frame/utils.h b/chrome_frame/utils.h
index f42062c..e90a0d4 100644
--- a/chrome_frame/utils.h
+++ b/chrome_frame/utils.h
@@ -12,6 +12,7 @@
#include "base/basictypes.h"
#include "base/logging.h"
+#include "base/thread.h"
// utils.h : Various utility functions and classes
@@ -315,6 +316,25 @@ STDMETHODIMP QueryInterfaceIfDelegateSupports(void* obj, REFIID iid,
extern const wchar_t kChromeFrameHeadlessMode[];
+// Thread that enters STA and has a UI message loop.
+class STAThread : public base::Thread {
+ public:
+ explicit STAThread(const char *name) : Thread(name) {}
+ bool Start() {
+ return StartWithOptions(Options(MessageLoop::TYPE_UI, 0));
+ }
+ protected:
+ // Called just prior to starting the message loop
+ virtual void Init() {
+ ::CoInitialize(0);
+ }
+
+ // Called just after the message loop ends
+ virtual void CleanUp() {
+ ::CoUninitialize();
+ }
+};
+
// The urls retrieved from the IMoniker interface don't contain the anchor
// portion of the actual url navigated to. This function checks whether the
// url passed in the bho_url parameter contains an anchor and if yes checks