diff options
Diffstat (limited to 'chrome_frame/chrome_active_document.cc')
-rw-r--r-- | chrome_frame/chrome_active_document.cc | 251 |
1 files changed, 194 insertions, 57 deletions
diff --git a/chrome_frame/chrome_active_document.cc b/chrome_frame/chrome_active_document.cc index 2842e01..56b13be 100644 --- a/chrome_frame/chrome_active_document.cc +++ b/chrome_frame/chrome_active_document.cc @@ -7,6 +7,7 @@ #include <hlink.h> #include <htiface.h> +#include <initguid.h> #include <mshtmcid.h> #include <shdeprecated.h> #include <shlguid.h> @@ -42,6 +43,10 @@ static const wchar_t kUseChromeNetworking[] = L"UseChromeNetworking"; static const wchar_t kHandleTopLevelRequests[] = L"HandleTopLevelRequests"; +DEFINE_GUID(CGID_DocHostCmdPriv, 0x000214D4L, 0, 0, 0xC0, 0, 0, 0, 0, 0, 0, + 0x46); + + base::ThreadLocalPointer<ChromeActiveDocument> g_active_doc_cache; bool g_first_launch_by_process_ = true; @@ -49,6 +54,7 @@ bool g_first_launch_by_process_ = true; ChromeActiveDocument::ChromeActiveDocument() : first_navigation_(true), is_automation_client_reused_(false) { + memset(&navigation_info_, 0, sizeof(navigation_info_)); } HRESULT ChromeActiveDocument::FinalConstruct() { @@ -179,6 +185,20 @@ STDMETHODIMP ChromeActiveDocument::Load(BOOL fully_avalable, if (NULL == moniker_name) { return E_INVALIDARG; } + + ScopedComPtr<IOleClientSite> client_site; + if (bind_context) { + ScopedComPtr<IUnknown> site; + bind_context->GetObjectParam(SZ_HTML_CLIENTSITE_OBJECTPARAM, + site.Receive()); + if (site) + client_site.QueryFrom(site); + } + + if (client_site) { + SetClientSite(client_site); + } + CComHeapPtr<WCHAR> display_name; moniker_name->GetDisplayName(bind_context, NULL, &display_name); std::wstring url = display_name; @@ -273,6 +293,72 @@ STDMETHODIMP ChromeActiveDocument::Exec(const GUID* cmd_group_guid, return OLECMDERR_E_NOTSUPPORTED; } +STDMETHODIMP ChromeActiveDocument::LoadHistory(IStream* stream, + IBindCtx* bind_context) { + // Read notes in ChromeActiveDocument::SaveHistory + DCHECK(stream); + LARGE_INTEGER offset = {0}; + ULARGE_INTEGER cur_pos = {0}; + STATSTG statstg = {0}; + + stream->Seek(offset, STREAM_SEEK_CUR, &cur_pos); + stream->Stat(&statstg, STATFLAG_NONAME); + + DWORD url_size = statstg.cbSize.LowPart - cur_pos.LowPart; + ScopedBstr url_bstr; + DWORD bytes_read = 0; + stream->Read(url_bstr.AllocateBytes(url_size), url_size, &bytes_read); + std::wstring url(url_bstr); + + bool is_new_navigation = true; + bool is_chrome_protocol = false; + + if (!ParseUrl(url, &is_new_navigation, &is_chrome_protocol, &url)) { + DLOG(WARNING) << __FUNCTION__ << " Failed to parse url:" << url; + return E_INVALIDARG; + } + + if (!LaunchUrl(url, is_new_navigation)) { + NOTREACHED() << __FUNCTION__ << " Failed to launch url:" << url; + return E_INVALIDARG; + } + return S_OK; +} + +STDMETHODIMP ChromeActiveDocument::SaveHistory(IStream* stream) { + // TODO(sanjeevr): We need to fetch the entire list of navigation entries + // from Chrome and persist it in the stream. And in LoadHistory we need to + // pass this list back to Chrome which will recreate the list. This will allow + // Back-Forward navigation to anchors to work correctly when we navigate to a + // page outside of ChromeFrame and then come back. + if (!stream) { + NOTREACHED(); + return E_INVALIDARG; + } + + LARGE_INTEGER offset = {0}; + ULARGE_INTEGER new_pos = {0}; + DWORD written = 0; + std::wstring url = UTF8ToWide(navigation_info_.url.spec()); + return stream->Write(url.c_str(), (url.length() + 1) * sizeof(wchar_t), + &written); +} + +STDMETHODIMP ChromeActiveDocument::SetPositionCookie(DWORD position_cookie) { + int index = static_cast<int>(position_cookie); + navigation_info_.navigation_index = index; + automation_client_->NavigateToIndex(index); + return S_OK; +} + +STDMETHODIMP ChromeActiveDocument::GetPositionCookie(DWORD* position_cookie) { + if (!position_cookie) + return E_INVALIDARG; + + *position_cookie = navigation_info_.navigation_index; + return S_OK; +} + STDMETHODIMP ChromeActiveDocument::GetUrlForEvents(BSTR* url) { if (NULL == url) { return E_POINTER; @@ -281,6 +367,10 @@ STDMETHODIMP ChromeActiveDocument::GetUrlForEvents(BSTR* url) { return S_OK; } +STDMETHODIMP ChromeActiveDocument::GetAddressBarUrl(BSTR* url) { + return GetUrlForEvents(url); +} + HRESULT ChromeActiveDocument::IOleObject_SetClientSite( IOleClientSite* client_site) { if (client_site == NULL) { @@ -301,7 +391,11 @@ HRESULT ChromeActiveDocument::IOleObject_SetClientSite( doc_site_.Release(); in_place_frame_.Release(); } - return Base::IOleObject_SetClientSite(client_site); + + if (client_site != m_spClientSite) + return Base::IOleObject_SetClientSite(client_site); + + return S_OK; } @@ -363,9 +457,6 @@ HRESULT ChromeActiveDocument::ActiveXDocActivate(LONG verb) { } } m_spClientSite->ShowObject(); - // Inform IE about the zone for this URL. We do this here as we need to the - // IOleInPlaceSite interface to be setup. - IEExec(&CGID_Explorer, SBCMDID_MIXEDZONE, 0, NULL, NULL); return S_OK; } @@ -442,30 +533,21 @@ void ChromeActiveDocument::OnDidNavigate(int tab_handle, void ChromeActiveDocument::UpdateNavigationState( const IPC::NavigationInfo& new_navigation_info) { + HRESULT hr = S_OK; bool is_title_changed = (navigation_info_.title != new_navigation_info.title); - bool is_url_changed = (navigation_info_.url.is_valid() && - (navigation_info_.url != new_navigation_info.url)); bool is_ssl_state_changed = (navigation_info_.security_style != new_navigation_info.security_style) || (navigation_info_.has_mixed_content != new_navigation_info.has_mixed_content); - navigation_info_ = new_navigation_info; - - if (is_title_changed) { - ScopedVariant title(navigation_info_.title.c_str()); - IEExec(NULL, OLECMDID_SETTITLE, OLECMDEXECOPT_DONTPROMPTUSER, - title.AsInput(), NULL); - } - if (is_ssl_state_changed) { int lock_status = SECURELOCK_SET_UNSECURE; - switch (navigation_info_.security_style) { + switch (new_navigation_info.security_style) { case SECURITY_STYLE_AUTHENTICATION_BROKEN: lock_status = SECURELOCK_SET_SECUREUNKNOWNBIT; break; case SECURITY_STYLE_AUTHENTICATED: - lock_status = navigation_info_.has_mixed_content ? + lock_status = new_navigation_info.has_mixed_content ? SECURELOCK_SET_MIXED : SECURELOCK_SET_SECUREUNKNOWNBIT; break; default: @@ -477,32 +559,78 @@ void ChromeActiveDocument::UpdateNavigationState( OLECMDEXECOPT_DODEFAULT, secure_lock_status.AsInput(), NULL); } - if (navigation_info_.url.is_valid() && - (is_url_changed || url_.Length() == 0)) { - url_.Allocate(UTF8ToWide(navigation_info_.url.spec()).c_str()); - // Now call the FireNavigateCompleteEvent which makes IE update the text - // in the address-bar. We call the FireBeforeNavigateComplete2Event and - // FireDocumentComplete event just for completeness sake. If some BHO - // chooses to cancel the navigation in the OnBeforeNavigate2 handler - // we will ignore the cancellation request. + // Ideally all navigations should come to Chrome Frame so that we can call + // BeforeNavigate2 on installed BHOs and give them a chance to cancel the + // navigation. However, in practice what happens is as below: + // The very first navigation that happens in CF happens via a Load or a + // LoadHistory call. In this case, IE already has the correct information for + // its travel log as well address bar. For other internal navigations (navs + // that only happen within Chrome such as anchor navigations) we need to + // update IE's internal state after the fact. In the case of internal + // navigations, we notify the BHOs but ignore the should_cancel flag. + bool is_internal_navigation = (new_navigation_info.navigation_index > 0) && + (new_navigation_info.navigation_index != + navigation_info_.navigation_index); - // Todo(joshia): investigate if there's a better way to set URL in the - // address bar + if (new_navigation_info.url.is_valid()) { + url_.Allocate(UTF8ToWide(new_navigation_info.url.spec()).c_str()); + } + + if (is_internal_navigation) { + ScopedComPtr<IDocObjectService> doc_object_svc; ScopedComPtr<IWebBrowserEventsService> web_browser_events_svc; DoQueryService(__uuidof(web_browser_events_svc), m_spClientSite, web_browser_events_svc.Receive()); + DCHECK(web_browser_events_svc); if (web_browser_events_svc) { - // TODO(joshia): maybe we should call FireBeforeNavigate2Event in - // ChromeActiveDocument::Load and abort if cancelled. VARIANT_BOOL should_cancel = VARIANT_FALSE; web_browser_events_svc->FireBeforeNavigate2Event(&should_cancel); + } + + // We need to tell IE that we support navigation so that IE will query us + // for IPersistHistory and call GetPositionCookie to save our navigation + // index. + ScopedVariant html_window(static_cast<IUnknown*>( + static_cast<IHTMLWindow2*>(this))); + IEExec(&CGID_DocHostCmdPriv, DOCHOST_DOCCANNAVIGATE, 0, + html_window.AsInput(), NULL); + + // We pass the HLNF_INTERNALJUMP flag to INTERNAL_CMDID_FINALIZE_TRAVEL_LOG + // since we want to make IE treat all internal navigations within this page + // (including anchor navigations and subframe navigations) as anchor + // navigations. This will ensure that IE calls GetPositionCookie + // to save the current position cookie in the travel log and then call + // SetPositionCookie when the user hits Back/Forward to come back here. + ScopedVariant internal_navigation(HLNF_INTERNALJUMP); + IEExec(&CGID_Explorer, INTERNAL_CMDID_FINALIZE_TRAVEL_LOG, 0, + internal_navigation.AsInput(), NULL); + + // We no longer need to lie to IE. If we lie persistently to IE, then + // IE reuses us for new navigations. + IEExec(&CGID_DocHostCmdPriv, DOCHOST_DOCCANNAVIGATE, 0, NULL, NULL); + + if (doc_object_svc) { + // Now call the FireNavigateCompleteEvent which makes IE update the text + // in the address-bar. + doc_object_svc->FireNavigateComplete2(this, 0); + doc_object_svc->FireDocumentComplete(this, 0); + } else if (web_browser_events_svc) { web_browser_events_svc->FireNavigateComplete2Event(); - if (VARIANT_TRUE != should_cancel) { - web_browser_events_svc->FireDocumentCompleteEvent(); - } + web_browser_events_svc->FireDocumentCompleteEvent(); } } + if (is_title_changed) { + ScopedVariant title(new_navigation_info.title.c_str()); + IEExec(NULL, OLECMDID_SETTITLE, OLECMDEXECOPT_DONTPROMPTUSER, + title.AsInput(), NULL); + } + + // It is important that we only update the navigation_info_ after we have + // finalized the travel log. This is because IE will ask for information + // such as navigation index when the travel log is finalized and we need + // supply the old index and not the new one. + navigation_info_ = new_navigation_info; // Update the IE zone here. Ideally we would like to do it when the active // document is activated. However that does not work at times as the frame we // get there is not the actual frame which handles the command. @@ -556,23 +684,12 @@ void ChromeActiveDocument::OnOpenURL(int tab_handle, Base::OnOpenURL(tab_handle, url_to_open, referrer, open_disposition); } -void ChromeActiveDocument::OnLoad(int tab_handle, const GURL& url) { - if (ready_state_ < READYSTATE_COMPLETE) { - ready_state_ = READYSTATE_COMPLETE; - FireOnChanged(DISPID_READYSTATE); - } -} - bool ChromeActiveDocument::PreProcessContextMenu(HMENU menu) { ScopedComPtr<IBrowserService> browser_service; ScopedComPtr<ITravelLog> travel_log; - - DoQueryService(SID_SShellBrowser, m_spClientSite, browser_service.Receive()); - if (!browser_service) - return true; - - browser_service->GetTravelLog(travel_log.Receive()); - if (!travel_log) + GetBrowserServiceAndTravelLog(browser_service.Receive(), + travel_log.Receive()); + if (!browser_service || !travel_log) return true; if (SUCCEEDED(travel_log->GetTravelEntry(browser_service, TLOG_BACK, NULL))) { @@ -581,7 +698,6 @@ bool ChromeActiveDocument::PreProcessContextMenu(HMENU menu) { EnableMenuItem(menu, IDS_CONTENT_CONTEXT_BACK, MF_BYCOMMAND | MFS_DISABLED); } - if (SUCCEEDED(travel_log->GetTravelEntry(browser_service, TLOG_FORE, NULL))) { EnableMenuItem(menu, IDS_CONTENT_CONTEXT_FORWARD, MF_BYCOMMAND | MF_ENABLED); @@ -731,10 +847,9 @@ bool ChromeActiveDocument::LaunchUrl(const std::wstring& url, std::string referrer; Bho* chrome_frame_bho = Bho::GetCurrentThreadBhoInstance(); - DCHECK(chrome_frame_bho != NULL); - if (chrome_frame_bho) { + if (chrome_frame_bho) referrer = chrome_frame_bho->referrer(); - } + if (!automation_client_->InitiateNavigation(utf8_url, referrer, is_privileged_)) { @@ -806,14 +921,36 @@ HRESULT ChromeActiveDocument::SetPageFontSize(const GUID* cmd_group_guid, void ChromeActiveDocument::OnGoToHistoryEntryOffset(int tab_handle, int offset) { - DLOG(INFO) << "GoToHistoryEntryOffset " << offset; + DLOG(INFO) << __FUNCTION__ << " - offset:" << offset; + ScopedComPtr<IBrowserService> browser_service; - DoQueryService(SID_SShellBrowser, m_spClientSite, browser_service.Receive()); - if (browser_service) { - ScopedComPtr<ITravelLog> travel_log; - browser_service->GetTravelLog(travel_log.Receive()); - if (travel_log) { - travel_log->Travel(browser_service, offset); - } + ScopedComPtr<ITravelLog> travel_log; + GetBrowserServiceAndTravelLog(browser_service.Receive(), + travel_log.Receive()); + + if (browser_service && travel_log) + travel_log->Travel(browser_service, offset); +} + +HRESULT ChromeActiveDocument::GetBrowserServiceAndTravelLog( + IBrowserService** browser_service, ITravelLog** travel_log) { + DCHECK(browser_service || travel_log); + ScopedComPtr<IBrowserService> browser_service_local; + HRESULT hr = DoQueryService(SID_SShellBrowser, m_spClientSite, + browser_service_local.Receive()); + if (!browser_service_local) { + NOTREACHED() << "DoQueryService for IBrowserService failed: " << hr; + return hr; } + + if (travel_log) { + hr = browser_service_local->GetTravelLog(travel_log); + DLOG_IF(INFO, !travel_log) << "browser_service->GetTravelLog failed: " + << hr; + } + + if (browser_service) + *browser_service = browser_service_local.Detach(); + + return hr; } |