diff options
-rw-r--r-- | chrome_frame/test/test_mock_with_web_server.cc | 2 | ||||
-rw-r--r-- | chrome_frame/test/test_with_web_server.cc | 8 | ||||
-rw-r--r-- | chrome_frame/urlmon_bind_status_callback.cc | 41 | ||||
-rw-r--r-- | chrome_frame/urlmon_bind_status_callback.h | 5 | ||||
-rw-r--r-- | chrome_frame/urlmon_moniker.cc | 62 | ||||
-rw-r--r-- | chrome_frame/urlmon_moniker.h | 7 | ||||
-rw-r--r-- | chrome_frame/urlmon_url_request.cc | 108 | ||||
-rw-r--r-- | chrome_frame/urlmon_url_request.h | 25 | ||||
-rw-r--r-- | chrome_frame/urlmon_url_request_private.h | 9 |
9 files changed, 174 insertions, 93 deletions
diff --git a/chrome_frame/test/test_mock_with_web_server.cc b/chrome_frame/test/test_mock_with_web_server.cc index 27c44a3..9588618 100644 --- a/chrome_frame/test/test_mock_with_web_server.cc +++ b/chrome_frame/test/test_mock_with_web_server.cc @@ -327,7 +327,7 @@ TEST_F(ChromeFrameTestWithWebServer, FLAKY_FullTabModeIE_KeyboardTest) { if (hr == S_FALSE) return; - loop.RunFor(kChromeFrameLongNavigationTimeoutInSeconds); + loop.RunFor(0x9999); } const wchar_t kAboutVersionUrl[] = L"gcf:about:version"; diff --git a/chrome_frame/test/test_with_web_server.cc b/chrome_frame/test/test_with_web_server.cc index 991efdae..758e07e 100644 --- a/chrome_frame/test/test_with_web_server.cc +++ b/chrome_frame/test/test_with_web_server.cc @@ -793,7 +793,7 @@ TEST_F(ChromeFrameTestWithWebServer, FullTabModeIE_TestPostReissue) { if (!MonikerPatchEnabled()) return; - MessageLoopForUI loop; // must come before the server. + chrome_frame_test::TimedMsgLoop loop; // must come before the server. // The order of pages in this array is assumed to be mshtml, cf, script. const wchar_t* kPages[] = { @@ -807,7 +807,7 @@ TEST_F(ChromeFrameTestWithWebServer, FullTabModeIE_TestPostReissue) { ASSERT_TRUE(LaunchBrowser(IE, server.FormatHttpPath(kPages[0]).c_str())); - loop.MessageLoop::Run(); + loop.RunFor(kChromeFrameLongNavigationTimeoutInSeconds); // Check if the last request to /quit gave us the OK signal. const test_server::Request& r = server.last_request(); @@ -827,7 +827,7 @@ TEST_F(ChromeFrameTestWithWebServer, FullTabModeIE_TestMultipleGet) { if (!MonikerPatchEnabled()) return; - MessageLoopForUI loop; // must come before the server. + chrome_frame_test::TimedMsgLoop loop; // must come before the server. // The order of pages in this array is assumed to be mshtml, cf, script. const wchar_t* kPages[] = { @@ -841,7 +841,7 @@ TEST_F(ChromeFrameTestWithWebServer, FullTabModeIE_TestMultipleGet) { ASSERT_TRUE(LaunchBrowser(IE, server.FormatHttpPath(kPages[0]).c_str())); - loop.MessageLoop::Run(); + loop.RunFor(kChromeFrameLongNavigationTimeoutInSeconds); // Check if the last request to /quit gave us the OK signal. const test_server::Request& r = server.last_request(); diff --git a/chrome_frame/urlmon_bind_status_callback.cc b/chrome_frame/urlmon_bind_status_callback.cc index 0681849..cf920c2 100644 --- a/chrome_frame/urlmon_bind_status_callback.cc +++ b/chrome_frame/urlmon_bind_status_callback.cc @@ -81,11 +81,8 @@ bool SniffData::InitializeCache(const std::wstring& url) { url_ = url; renderer_type_ = UNDETERMINED; - const int kAllocationSize = 32 * 1024; - HGLOBAL memory = GlobalAlloc(0, kAllocationSize); - HRESULT hr = CreateStreamOnHGlobal(memory, TRUE, cache_.Receive()); + HRESULT hr = CreateStreamOnHGlobal(NULL, TRUE, cache_.Receive()); if (FAILED(hr)) { - GlobalFree(memory); NOTREACHED(); return false; } @@ -182,7 +179,8 @@ void SniffData::DetermineRendererType() { ///////////////////////////////////////////////////////////////////// -HRESULT BSCBStorageBind::Initialize(IMoniker* moniker, IBindCtx* bind_ctx) { +HRESULT BSCBStorageBind::Initialize(IMoniker* moniker, IBindCtx* bind_ctx, + bool no_cache) { DLOG(INFO) << __FUNCTION__ << me() << StringPrintf(" tid=%i", PlatformThread::CurrentId()); @@ -200,6 +198,7 @@ HRESULT BSCBStorageBind::Initialize(IMoniker* moniker, IBindCtx* bind_ctx) { std::wstring url = GetActualUrlFromMoniker(moniker, bind_ctx, std::wstring()); data_sniffer_.InitializeCache(url); + no_cache_ = no_cache; return hr; } @@ -227,13 +226,15 @@ STDMETHODIMP BSCBStorageBind::OnDataAvailable(DWORD flags, DWORD size, STGMEDIUM* stgmed) { DLOG(INFO) << __FUNCTION__ << StringPrintf(" tid=%i", PlatformThread::CurrentId()); - if (!stgmed || !format_etc) { - DLOG(INFO) << __FUNCTION__ << me() << "Invalid stgmed or format_etc"; - return CallbackImpl::OnDataAvailable(flags, size, format_etc, stgmed); - } - if ((stgmed->tymed != TYMED_ISTREAM) || !stgmed->pstm) { - DLOG(INFO) << __FUNCTION__ << me() << "stgmedium is not a valid stream"; + // Do not touch anything other than text/html. + const CLIPFORMAT text_html = RegisterClipboardFormat(CFSTR_MIME_HTML); + bool is_interesting = (format_etc && stgmed && stgmed->pstm && + (stgmed->tymed == TYMED_ISTREAM) && (text_html == format_etc->cfFormat)); + + if (!is_interesting) { + // Play back report progress so far. + MayPlayBack(flags); return CallbackImpl::OnDataAvailable(flags, size, format_etc, stgmed); } @@ -309,13 +310,25 @@ HRESULT BSCBStorageBind::MayPlayBack(DWORD flags) { } if (data_sniffer_.is_cache_valid()) { + if (data_sniffer_.is_chrome()) { + ScopedComPtr<IStream> cache; + if (no_cache_) { + // This flag is set by BindToObject indicating taht we don't need + // to cache as we'll be able to read data from the bind later. + CreateStreamOnHGlobal(NULL, TRUE, cache.Receive()); + } else { + // Binding began with BindToStorage and the data cann't be read + // back so pass on the data read so far. + cache = data_sniffer_.cache_; + } + DCHECK(cache); + NavigationManager::SetForSwitch(bind_ctx_, cache); + } + hr = data_sniffer_.DrainCache(delegate(), flags | BSCF_FIRSTDATANOTIFICATION, clip_format_); DLOG_IF(WARNING, INET_E_TERMINATED_BIND != hr) << __FUNCTION__ << " mshtml OnDataAvailable returned: " << std::hex << hr; - if (data_sniffer_.is_chrome()) { - NavigationManager::SetForSwitch(bind_ctx_); - } } return hr; diff --git a/chrome_frame/urlmon_bind_status_callback.h b/chrome_frame/urlmon_bind_status_callback.h index f1b1473..c6b4ba7 100644 --- a/chrome_frame/urlmon_bind_status_callback.h +++ b/chrome_frame/urlmon_bind_status_callback.h @@ -91,14 +91,14 @@ class SniffData { class BSCBStorageBind : public BSCBImpl { public: typedef BSCBImpl CallbackImpl; - BSCBStorageBind() : clip_format_(CF_NULL) {} + BSCBStorageBind() : clip_format_(CF_NULL), no_cache_(false) {} BEGIN_COM_MAP(BSCBStorageBind) COM_INTERFACE_ENTRY(IBindStatusCallback) COM_INTERFACE_ENTRY_CHAIN(CallbackImpl) END_COM_MAP() - HRESULT Initialize(IMoniker* moniker, IBindCtx* bind_ctx); + HRESULT Initialize(IMoniker* moniker, IBindCtx* bind_ctx, bool no_cache); HRESULT MayPlayBack(DWORD flags); // IBindStatusCallback @@ -121,6 +121,7 @@ END_COM_MAP() std::vector<Progress> saved_progress_; CLIPFORMAT clip_format_; + bool no_cache_; private: DISALLOW_COPY_AND_ASSIGN(BSCBStorageBind); diff --git a/chrome_frame/urlmon_moniker.cc b/chrome_frame/urlmon_moniker.cc index 49632fd..27b15df 100644 --- a/chrome_frame/urlmon_moniker.cc +++ b/chrome_frame/urlmon_moniker.cc @@ -15,7 +15,12 @@ static const int kMonikerBindToObject = 8; static const int kMonikerBindToStorage = kMonikerBindToObject + 1; -static wchar_t kBindContextParamName[] = L"_CHROMEFRAME_PRECREATE_"; + +// These are non const due to API expectations +static wchar_t* kBindContextCachedData = L"_CHROMEFRAME_PRECREATE_"; +static wchar_t* kBindToObjectBind = L"_CHROMEFRAME_BTO_BIND_"; +wchar_t* kChromeRequestParam = L"_CHROMEFRAME_REQUEST_"; + base::LazyInstance<base::ThreadLocalPointer<NavigationManager> > NavigationManager::thread_singleton_(base::LINKER_INITIALIZED); @@ -139,22 +144,18 @@ void NavigationManager::UnregisterThreadInstance() { } // Mark a bind context for navigation by storing a bind context param. -bool NavigationManager::SetForSwitch(IBindCtx* bind_context) { +bool NavigationManager::SetForSwitch(IBindCtx* bind_context, IStream* data) { if (!bind_context) { NOTREACHED(); return false; } - ScopedComPtr<IStream> dummy; - HRESULT hr = CreateStreamOnHGlobal(NULL, TRUE, dummy.Receive()); - if (dummy) { - hr = bind_context->RegisterObjectParam(kBindContextParamName, dummy); - } - + RewindStream(data); + HRESULT hr = bind_context->RegisterObjectParam(kBindContextCachedData, data); return SUCCEEDED(hr); } -bool NavigationManager::ResetSwitch(IBindCtx* bind_context) { +bool NavigationManager::IsSetToSwitch(IBindCtx* bind_context) { if (!bind_context) { NOTREACHED(); return false; @@ -162,13 +163,29 @@ bool NavigationManager::ResetSwitch(IBindCtx* bind_context) { ScopedComPtr<IUnknown> should_switch; HRESULT hr = E_FAIL; - hr = bind_context->GetObjectParam(kBindContextParamName, + hr = bind_context->GetObjectParam(kBindContextCachedData, should_switch.Receive()); - hr = bind_context->RevokeObjectParam(kBindContextParamName); - return !!should_switch; } +HRESULT NavigationManager::ResetSwitch(IBindCtx* bind_context, IStream** data) { + if (!bind_context) { + NOTREACHED(); + return false; + } + + ScopedComPtr<IUnknown> data_unknown; + HRESULT hr = E_FAIL; + hr = bind_context->GetObjectParam(kBindContextCachedData, + data_unknown.Receive()); + hr = bind_context->RevokeObjectParam(kBindContextCachedData); + if (data_unknown) { + hr = data_unknown.QueryInterface(data); + DCHECK(SUCCEEDED(hr)); + } + return hr; +} + ///////////////////////////////////////// // static @@ -211,8 +228,7 @@ bool ShouldWrapCallback(IMoniker* moniker, REFIID iid, IBindCtx* bind_context) { } ScopedComPtr<IUnknown> our_request; - hr = bind_context->GetObjectParam(L"_CHROMEFRAME_REQUEST_", - our_request.Receive()); + hr = bind_context->GetObjectParam(kChromeRequestParam, our_request.Receive()); if (our_request) { DLOG(INFO) << __FUNCTION__ << " Url: " << url << " Not wrapping: request from chrome frame."; @@ -243,7 +259,9 @@ HRESULT MonikerPatch::BindToObject(IMoniker_BindToObject_Fn original, DCHECK(to_left == NULL); HRESULT hr = S_OK; - if (NavigationManager::ResetSwitch(bind_ctx)) { + // Bind context is marked for switch when we sniff data in BSCBStorageBind + // and determine that the renderer to be used is Chrome. + if (NavigationManager::IsSetToSwitch(bind_ctx)) { // We could implement the BindToObject ourselves here but instead we // simply register Chrome Frame ActiveDoc as a handler for 'text/html' // in this bind context. This makes urlmon instantiate CF Active doc @@ -252,6 +270,13 @@ HRESULT MonikerPatch::BindToObject(IMoniker_BindToObject_Fn original, CLSID classes[] = { CLSID_ChromeActiveDocument }; hr = RegisterMediaTypeClass(bind_ctx, arraysize(media_types), media_types, classes, 0); + } else { + // In case the binding begins with BindToObject we do not need + // to cache the data in the sniffing code. + ScopedComPtr<IStream> no_cache; + CreateStreamOnHGlobal(NULL, TRUE, no_cache.Receive()); + if (no_cache) + bind_ctx->RegisterObjectParam(kBindToObjectBind, no_cache); } hr = original(me, bind_ctx, to_left, iid, obj); @@ -267,9 +292,14 @@ HRESULT MonikerPatch::BindToStorage(IMoniker_BindToStorage_Fn original, HRESULT hr = S_OK; CComObject<BSCBStorageBind>* callback = NULL; if (ShouldWrapCallback(me, iid, bind_ctx)) { + // Is this bind context marked as no cache by BindToObject already? + ScopedComPtr<IUnknown> no_cache; + if (bind_ctx) + bind_ctx->GetObjectParam(kBindToObjectBind, no_cache.Receive()); + hr = CComObject<BSCBStorageBind>::CreateInstance(&callback); callback->AddRef(); - hr = callback->Initialize(me, bind_ctx); + hr = callback->Initialize(me, bind_ctx, !!no_cache); DCHECK(SUCCEEDED(hr)); } diff --git a/chrome_frame/urlmon_moniker.h b/chrome_frame/urlmon_moniker.h index 5cea8cf..97f60ff 100644 --- a/chrome_frame/urlmon_moniker.h +++ b/chrome_frame/urlmon_moniker.h @@ -89,8 +89,9 @@ class NavigationManager { static NavigationManager* GetThreadInstance(); // Mark a bind context for navigation by storing a bind context param. - static bool SetForSwitch(IBindCtx* bind_context); - static bool ResetSwitch(IBindCtx* bind_context); + static bool SetForSwitch(IBindCtx* bind_context, IStream* data); + static bool IsSetToSwitch(IBindCtx* bind_context); + static HRESULT ResetSwitch(IBindCtx* bind_context, IStream** data); void RegisterThreadInstance(); void UnregisterThreadInstance(); @@ -177,4 +178,6 @@ class MonikerPatch { }; +extern wchar_t* kChromeRequestParam; + #endif // CHROME_FRAME_URLMON_MONIKER_H_ diff --git a/chrome_frame/urlmon_url_request.cc b/chrome_frame/urlmon_url_request.cc index cc0c872..3542bc2 100644 --- a/chrome_frame/urlmon_url_request.cc +++ b/chrome_frame/urlmon_url_request.cc @@ -109,11 +109,30 @@ bool UrlmonUrlRequest::Read(int bytes_to_read) { return true; } -HRESULT UrlmonUrlRequest::UseBindCtx(IMoniker* moniker, LPBC bc) { +HRESULT UrlmonUrlRequest::InitPending(const GURL& url, IMoniker* moniker, + IBindCtx* bind_context, + bool enable_frame_busting, + bool privileged_mode, + HWND notification_window) { DCHECK(bind_context_ == NULL); DCHECK(moniker_ == NULL); - bind_context_ = bc; + bind_context_ = bind_context; moniker_ = moniker; + url_ = url; + enable_frame_busting_ = enable_frame_busting; + privileged_mode_ = privileged_mode; + parent_window_ = notification_window; + + ScopedComPtr<IStream> data; + NavigationManager::ResetSwitch(bind_context, data.Receive()); + if (data) + cached_data_.Append(data); + + // Request has already started and data is fetched. We will get the + // GetBindInfo call as per contract but the return values are + // ignored. So just set "get" as a method to make our GetBindInfo + // implementation happy. + method_ = "get"; return S_OK; } @@ -127,16 +146,21 @@ void UrlmonUrlRequest::StealMoniker(IMoniker** moniker, IBindCtx** bctx) { } size_t UrlmonUrlRequest::SendDataToDelegate(size_t bytes_to_read) { - // We can optimize a bit by setting this string as a class member - // and avoid frequent memory reallocations. - std::string data; - size_t bytes_copied; + size_t bytes_copied = 0; + if (delegate_) { + // We can optimize a bit by setting this string as a class member + // and avoid frequent memory reallocations. + std::string data; + + size_t bytes = std::min(static_cast<size_t>(bytes_to_read), + cached_data_.size()); + cached_data_.Read(WriteInto(&data, 1 + bytes), bytes, &bytes_copied); + DCHECK_EQ(bytes, data.length()); + ++calling_delegate_; + delegate_->OnReadComplete(id(), data); + --calling_delegate_; + } - size_t bytes = std::min(size_t(bytes_to_read), cached_data_.size()); - cached_data_.Read(WriteInto(&data, 1 + bytes), bytes, &bytes_copied); - ++calling_delegate_; - delegate_->OnReadComplete(id(), data); - --calling_delegate_; return bytes_copied; } @@ -361,10 +385,6 @@ STDMETHODIMP UrlmonUrlRequest::OnDataAvailable(DWORD flags, DWORD size, } HRESULT hr = S_OK; - if (BSCF_FIRSTDATANOTIFICATION & flags) { - DCHECK(!cached_data_.is_valid()); - } - // Always read data into cache. We have to read all the data here at this // time or it won't be available later. Since the size of the data could // be more than pending read size, it's not straightforward (or might even @@ -476,7 +496,6 @@ STDMETHODIMP UrlmonUrlRequest::OnResponse(DWORD dwResponseCode, DLOG(INFO) << __FUNCTION__ << StringPrintf(" this=0x%08X, tid=%i", this, ::GetCurrentThreadId()); DCHECK_EQ(thread_, PlatformThread::CurrentId()); - DCHECK(binding_ != NULL); std::string raw_headers = WideToUTF8(response_headers); @@ -655,13 +674,17 @@ HRESULT UrlmonUrlRequest::StartAsyncDownload() { // Inform our moniker patch this binding should nto be tortured. // TODO(amit): factor this out. - hr = bind_context_->RegisterObjectParam(L"_CHROMEFRAME_REQUEST_", self); + hr = bind_context_->RegisterObjectParam(kChromeRequestParam, self); DCHECK(SUCCEEDED(hr)); hr = moniker_->BindToStorage(bind_context_, NULL, __uuidof(IStream), reinterpret_cast<void**>(stream.Receive())); - bind_context_->RevokeObjectParam(L"_CHROMEFRAME_REQUEST_"); + // BindToStorage can complete synchronously and OnStopBinding is + // called in its context. If that's the case then bind_context_ + // is already released. + if (bind_context_) + bind_context_->RevokeObjectParam(kChromeRequestParam); if (hr == S_OK) { DCHECK(binding_ != NULL || status_.get_state() == Status::DONE); @@ -943,7 +966,22 @@ PluginUrlRequestManager::ThreadSafeFlags void UrlmonUrlRequestManager::SetInfoForUrl(const std::wstring& url, IMoniker* moniker, LPBC bind_ctx) { - url_info_.Set(url, moniker, bind_ctx); + CComObject<UrlmonUrlRequest>* new_request = NULL; + CComObject<UrlmonUrlRequest>::CreateInstance(&new_request); + if (new_request) { + GURL start_url(url); + DCHECK(start_url.is_valid()); + DCHECK(pending_request_ == NULL); + + pending_request_ = new_request; + pending_request_->InitPending(start_url, moniker, bind_ctx, + enable_frame_busting_, privileged_mode_, + notification_window_); + // Start the request + bool is_started = pending_request_->Start(); + DCHECK(is_started); + ; + } } void UrlmonUrlRequestManager::StartRequest(int request_id, @@ -955,9 +993,19 @@ void UrlmonUrlRequestManager::StartRequest(int request_id, return; DCHECK(LookupRequest(request_id).get() == NULL); - - CComObject<UrlmonUrlRequest>* new_request = NULL; - CComObject<UrlmonUrlRequest>::CreateInstance(&new_request); + DCHECK(GURL(request_info.url).is_valid()); + + scoped_refptr<UrlmonUrlRequest> new_request; + bool is_started = false; + if (pending_request_) { + DCHECK(pending_request_->IsForUrl(GURL(request_info.url))); + new_request.swap(pending_request_); + is_started = true; + } else { + CComObject<UrlmonUrlRequest>* created_request = NULL; + CComObject<UrlmonUrlRequest>::CreateInstance(&created_request); + new_request = created_request; + } new_request->Initialize(static_cast<PluginUrlRequestDelegate*>(this), request_id, @@ -970,14 +1018,16 @@ void UrlmonUrlRequestManager::StartRequest(int request_id, new_request->set_parent_window(notification_window_); new_request->set_privileged_mode(privileged_mode_); - // Shall we use previously fetched data? - if (url_info_.IsForUrl(request_info.url)) { - new_request->UseBindCtx(url_info_.moniker_, url_info_.bind_ctx_); - url_info_.Clear(); - } - request_map_[request_id] = new_request; - new_request->Start(); + + if (!is_started) { + // Freshly created, start now. + new_request->Start(); + } else { + // Request is already underway, call OnResponse so that the + // other side can start reading. + new_request->OnResponse(0, L"", NULL, NULL); + } } void UrlmonUrlRequestManager::ReadRequest(int request_id, int bytes_to_read) { diff --git a/chrome_frame/urlmon_url_request.h b/chrome_frame/urlmon_url_request.h index 0b8898e..5dbb417 100644 --- a/chrome_frame/urlmon_url_request.h +++ b/chrome_frame/urlmon_url_request.h @@ -104,30 +104,7 @@ class UrlmonUrlRequestManager typedef std::map<int, scoped_refptr<UrlmonUrlRequest> > RequestMap; RequestMap request_map_; scoped_refptr<UrlmonUrlRequest> LookupRequest(int request_id); - - struct UrlInfo { - void Clear() { - url_ = GURL::EmptyGURL(); - bind_ctx_.Release(); - moniker_.Release(); - } - - void Set(const std::wstring& url, IMoniker* moniker, LPBC bc) { - DCHECK(bind_ctx_.get() == NULL); - DCHECK(moniker_.get() == NULL); - url_ = GURL(url); - moniker_ = moniker; - bind_ctx_ = bc; - } - - bool IsForUrl(const std::string& url) { - return GURL(url) == url_; - } - - GURL url_; - ScopedComPtr<IBindCtx> bind_ctx_; - ScopedComPtr<IMoniker> moniker_; - } url_info_; + scoped_refptr<UrlmonUrlRequest> pending_request_; bool stopping_; int calling_delegate_; // re-entrancy protection (debug only check) diff --git a/chrome_frame/urlmon_url_request_private.h b/chrome_frame/urlmon_url_request_private.h index 4dc973f..102f67b 100644 --- a/chrome_frame/urlmon_url_request_private.h +++ b/chrome_frame/urlmon_url_request_private.h @@ -31,7 +31,9 @@ class UrlmonUrlRequest virtual bool Read(int bytes_to_read); // Special function needed by ActiveDocument::Load() - HRESULT UseBindCtx(IMoniker* moniker, LPBC bc); + HRESULT InitPending(const GURL& url, IMoniker* moniker, IBindCtx* bind_ctx, + bool enable_frame_busting, bool privileged_mode, + HWND notification_window); // Used from "DownloadRequestInHost". void StealMoniker(IMoniker** moniker, IBindCtx** bctx); @@ -47,6 +49,10 @@ class UrlmonUrlRequest privileged_mode_ = privileged_mode; } + bool IsForUrl(const GURL& other) const { + return url_ == other; + } + protected: UrlmonUrlRequest(); ~UrlmonUrlRequest(); @@ -245,6 +251,7 @@ class UrlmonUrlRequest ScopedComPtr<IBinding> binding_; ScopedComPtr<IMoniker> moniker_; ScopedComPtr<IBindCtx> bind_context_; + GURL url_; Cache cached_data_; size_t pending_read_size_; PlatformThreadId thread_; |