Skip to content

Commit

Permalink
Use the OnHttpEquiv notification to switch to CF when the http-equiv …
Browse files Browse the repository at this point in the history
…meta tag is detected. This implementation is still behind the registry switch (set PatchProtocols to 0 in the registry). I will switch over to it wholesale in a separate patch.

We use the same mechanism for re-initiating the navigation we use to transfer downloads over to the host, so I moved the common code to utils.cc and added NavigateBrowserToMoniker.

When we see a browser instance attempting to load a CF document, we raise a TLS flag that we catch in  HttpNegotiatePatch::ReportProgress when the mime type is being reported.  This is the same place where we examine http headers and report the mime type.

BUG=n/a
TEST=Set PatchProtocols (REG_DWORD) to 0 in the CF HKCU config key and make sure we detect and handle the meta tag as well or better than before.


Review URL: http://codereview.chromium.org/489004

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@34366 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
tommi@chromium.org committed Dec 11, 2009
1 parent 2f7aff2 commit b22ef28
Show file tree
Hide file tree
Showing 8 changed files with 210 additions and 204 deletions.
125 changes: 46 additions & 79 deletions chrome_frame/bho.cc
Original file line number Diff line number Diff line change
Expand Up @@ -156,92 +156,59 @@ HRESULT Bho::FinalConstruct() {
void Bho::FinalRelease() {
}

HRESULT STDMETHODCALLTYPE Bho::OnHttpEquiv(
IBrowserService_OnHttpEquiv_Fn original_httpequiv,
HRESULT Bho::OnHttpEquiv(IBrowserService_OnHttpEquiv_Fn original_httpequiv,
IBrowserService* browser, IShellView* shell_view, BOOL done,
VARIANT* in_arg, VARIANT* out_arg) {
if (!done && in_arg && (VT_BSTR == V_VT(in_arg))) {
if (StrStrI(V_BSTR(in_arg), kChromeContentPrefix)) {
// OnHttpEquiv is invoked for meta tags within sub frames as well.
// We want to switch renderers only for the top level frame. Since
// the same |browser| and |shell_view| are passed in to OnHttpEquiv
// even for sub iframes, we determine if this is the top one by
// checking if there are any sub frames created or not.
ScopedComPtr<IWebBrowser2> web_browser2;
DoQueryService(SID_SWebBrowserApp, browser, web_browser2.Receive());
if (web_browser2 && !HasSubFrames(web_browser2))
SwitchRenderer(web_browser2, browser, shell_view, V_BSTR(in_arg));
}
}

return original_httpequiv(browser, shell_view, done, in_arg, out_arg);
}
// OnHttpEquiv is invoked for meta tags within sub frames as well.
// We want to switch renderers only for the top level frame.
bool is_top_level = (browser->GetBrowserIndex() == -1);

// OnHttpEquiv with 'done' set to TRUE is called for all pages.
// 0 or more calls with done set to FALSE are made.
// When done is FALSE, the current moniker may not represent the page
// being navigated to so we always have to wait for done to be TRUE
// before re-initiating the navigation.

if (done) {
if (CheckForCFNavigation(browser, false)) {
ScopedComPtr<IOleObject> mshtml_ole_object;
HRESULT hr = shell_view->GetItemObject(SVGIO_BACKGROUND, IID_IOleObject,
reinterpret_cast<void**>(mshtml_ole_object.Receive()));
DCHECK(FAILED(hr) || mshtml_ole_object != NULL);
if (mshtml_ole_object) {
ScopedComPtr<IMoniker> moniker;
hr = mshtml_ole_object->GetMoniker(OLEGETMONIKER_ONLYIFTHERE,
OLEWHICHMK_OBJFULL, moniker.Receive());
DCHECK(FAILED(hr) || moniker != NULL);
if (moniker) {
DLOG(INFO) << "Navigating in CF";
ScopedComPtr<IBindCtx> bind_context;
// This bind context will be discarded by IE and a new one
// constructed, so it's OK to create a sync bind context.
::CreateBindCtx(0, bind_context.Receive());
DCHECK(bind_context);
hr = NavigateBrowserToMoniker(browser, moniker, bind_context);
} else {
DLOG(ERROR) << "Couldn't get the current moniker";
}
}

bool Bho::HasSubFrames(IWebBrowser2* web_browser2) {
bool has_sub_frames = false;
ScopedComPtr<IDispatch> doc_dispatch;
if (web_browser2) {
HRESULT hr = web_browser2->get_Document(doc_dispatch.Receive());
DCHECK(SUCCEEDED(hr) && doc_dispatch) <<
"web_browser2->get_Document failed. Error: " << hr;
ScopedComPtr<IOleContainer> container;
if (SUCCEEDED(hr) && doc_dispatch) {
container.QueryFrom(doc_dispatch);
ScopedComPtr<IEnumUnknown> enumerator;
if (container) {
container->EnumObjects(OLECONTF_EMBEDDINGS, enumerator.Receive());
ScopedComPtr<IUnknown> unk;
ULONG items_retrieved = 0;
if (enumerator)
enumerator->Next(1, unk.Receive(), &items_retrieved);
has_sub_frames = (items_retrieved != 0);
if (FAILED(hr)) {
NOTREACHED();
// Lower the flag.
CheckForCFNavigation(browser, true);
} else {
// The navigate-in-gcf flag will be cleared in
// HttpNegotiatePatch::ReportProgress when the mime type is reported.
}
}
} else if (is_top_level && in_arg && VT_BSTR == V_VT(in_arg)) {
if (StrStrI(V_BSTR(in_arg), kChromeContentPrefix)) {
MarkBrowserOnThreadForCFNavigation(browser);
}
}

return has_sub_frames;
}

HRESULT Bho::SwitchRenderer(IWebBrowser2* web_browser2,
IBrowserService* browser, IShellView* shell_view,
const wchar_t* meta_tag) {
DCHECK(web_browser2 && browser && shell_view && meta_tag);

// Get access to the mshtml instance and the moniker
ScopedComPtr<IOleObject> mshtml_ole_object;
HRESULT hr = shell_view->GetItemObject(SVGIO_BACKGROUND, IID_IOleObject,
reinterpret_cast<void**>(mshtml_ole_object.Receive()));
if (!mshtml_ole_object) {
NOTREACHED() << "shell_view->GetItemObject failed. Error: " << hr;
return hr;
}

std::wstring url;
ScopedComPtr<IMoniker> moniker;
hr = mshtml_ole_object->GetMoniker(OLEGETMONIKER_ONLYIFTHERE,
OLEWHICHMK_OBJFULL, moniker.Receive());
DCHECK(moniker) << "mshtml_ole_object->GetMoniker failed. Error: " << hr;

if (moniker)
hr = GetUrlFromMoniker(moniker, NULL, &url);

DCHECK(!url.empty()) << "GetUrlFromMoniker failed. Error: " << hr;
DCHECK(!StartsWith(url, kChromeProtocolPrefix, false));

if (!url.empty()) {
url.insert(0, kChromeProtocolPrefix);
// Navigate to new url
VARIANT empty = ScopedVariant::kEmptyVariant;
VARIANT flags = { VT_I4 };
V_I4(&flags) = 0;
ScopedVariant url_var(url.c_str());
hr = web_browser2->Navigate2(url_var.AsInput(), &flags, &empty, &empty,
&empty);
DCHECK(SUCCEEDED(hr)) << "web_browser2->Navigate2 failed. Error: " << hr
<< std::endl << "Url: " << url;
}

return S_OK;
return original_httpequiv(browser, shell_view, done, in_arg, out_arg);
}

Bho* Bho::GetCurrentThreadBhoInstance() {
Expand Down
5 changes: 1 addition & 4 deletions chrome_frame/bho.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,7 @@ END_SINK_MAP()

protected:
bool PatchProtocolHandler(const CLSID& handler_clsid);
static bool HasSubFrames(IWebBrowser2* web_browser2);
static HRESULT SwitchRenderer(IWebBrowser2* web_browser2,
IBrowserService* browser, IShellView* shell_view,
const wchar_t* meta_tag);

std::string referrer_;

static base::LazyInstance<base::ThreadLocalPointer<Bho> >
Expand Down
1 change: 0 additions & 1 deletion chrome_frame/chrome_frame_activex.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

#include "chrome_frame/chrome_frame_activex.h"

#include <shdeprecated.h> // for IBrowserService2
#include <wininet.h>

#include <algorithm>
Expand Down
2 changes: 2 additions & 0 deletions chrome_frame/extra_system_apis.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
#ifndef CHROME_FRAME_EXTRA_SYSTEM_APIS_H_
#define CHROME_FRAME_EXTRA_SYSTEM_APIS_H_

#include <mshtml.h>

// This is an interface provided by the WebBrowser object. It allows us to
// notify the browser of navigation events. MSDN documents this interface
// (see http://msdn2.microsoft.com/en-us/library/aa752109(VS.85).aspx)
Expand Down
58 changes: 36 additions & 22 deletions chrome_frame/http_negotiate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -234,43 +234,57 @@ HRESULT HttpNegotiatePatch::ReportProgress(
if (status_code == BINDSTATUS_MIMETYPEAVAILABLE ||
status_code == BINDSTATUS_VERIFIEDMIMETYPEAVAILABLE ||
status_code == LOCAL_BINDSTATUS_SERVER_MIMETYPEAVAILABLE) {
bool is_top_level_request = !IsSubFrameRequest(me);

if (is_top_level_request) {
// Check to see if we need to alter the mime type that gets reported
// by inspecting the raw header information:
ScopedComPtr<IWinInetHttpInfo> win_inet_http_info;
HRESULT hr = win_inet_http_info.QueryFrom(me);

// Try slightly harder if we couldn't QI directly.
if (!win_inet_http_info || FAILED(hr)) {
hr = DoQueryService(IID_IWinInetHttpInfo, me,
win_inet_http_info.Receive());
}
bool render_in_chrome_frame = false;

// Check if this is a top level browser request that should be
// rendered in CF.
ScopedComPtr<IBrowserService> browser;
DoQueryService(IID_IShellBrowser, me, browser.Receive());
if (browser) {
render_in_chrome_frame = CheckForCFNavigation(browser, true);
}

if (!render_in_chrome_frame) {
bool is_top_level_request = !IsSubFrameRequest(me);

if (is_top_level_request) {
// Check to see if we need to alter the mime type that gets reported
// by inspecting the raw header information:
ScopedComPtr<IWinInetHttpInfo> win_inet_http_info;
HRESULT hr = win_inet_http_info.QueryFrom(me);

// Note that it has been observed that getting an IWinInetHttpInfo will
// fail if we are loading a page like about:blank that isn't loaded via
// wininet.
if (win_inet_http_info) {
// We have headers: check to see if the server is requesting CF via
// the X-UA-Compatible: chrome=1 HTTP header.
std::string headers(GetRawHttpHeaders(win_inet_http_info));
if (net::HttpUtil::HasHeader(headers, kUACompatibleHttpHeader)) {
// Try slightly harder if we couldn't QI directly.
if (!win_inet_http_info || FAILED(hr)) {
hr = DoQueryService(IID_IWinInetHttpInfo, me,
win_inet_http_info.Receive());
}

// Note that it has been observed that getting an IWinInetHttpInfo will
// fail if we are loading a page like about:blank that isn't loaded via
// wininet.
if (win_inet_http_info) {
// We have headers: check to see if the server is requesting CF via
// the X-UA-Compatible: chrome=1 HTTP header.
std::string headers(GetRawHttpHeaders(win_inet_http_info));
net::HttpUtil::HeadersIterator it(headers.begin(), headers.end(),
"\r\n");
while (it.GetNext()) {
if (LowerCaseEqualsASCII(it.name_begin(), it.name_end(),
kUACompatibleHttpHeader)) {
std::string ua_value(StringToLowerASCII(it.values()));
if (ua_value.find("chrome=1") != std::string::npos) {
status_text = kChromeMimeType;
render_in_chrome_frame = true;
break;
}
}
}
}
}
}

if (render_in_chrome_frame) {
status_text = kChromeMimeType;
}
}

return original(me, status_code, status_text);
Expand Down
84 changes: 6 additions & 78 deletions chrome_frame/urlmon_url_request.cc
Original file line number Diff line number Diff line change
Expand Up @@ -136,85 +136,13 @@ bool UrlmonUrlRequest::Read(int bytes_to_read) {
void UrlmonUrlRequest::TransferToHost(IUnknown* host) {
DCHECK_EQ(PlatformThread::CurrentId(), thread_);
DCHECK(host);

DCHECK(moniker_);
if (!moniker_)
return;

ScopedComPtr<IMoniker> moniker;
moniker.Attach(moniker_.Detach());

// Create a new bind context that's not associated with our callback.
// Calling RevokeBindStatusCallback doesn't disassociate the callback with
// the bind context in IE7. The returned bind context has the same
// implementation of GetRunningObjectTable as the bind context we held which
// basically delegates to ole32's GetRunningObjectTable. The object table
// is then used to determine if the moniker is already running and via
// that mechanism is associated with the same internet request as has already
// been issued.
ScopedComPtr<IBindCtx> bind_context;
CreateBindCtx(0, bind_context.Receive());
DCHECK(bind_context);

LPOLESTR url = NULL;
if (SUCCEEDED(moniker->GetDisplayName(bind_context, NULL, &url))) {
DLOG(INFO) << __FUNCTION__ << " " << url;

// TODO(tommi): See if we can get HlinkSimpleNavigateToMoniker to work
// instead. Looks like we'll need to support IHTMLDocument2 (get_URL in
// particular), access to IWebBrowser2 etc.
// HlinkSimpleNavigateToMoniker(moniker, url, NULL, host, bind_context,
// NULL, 0, 0);

ScopedComPtr<IWebBrowser2> wb2;
HRESULT hr = DoQueryService(SID_SWebBrowserApp, host, wb2.Receive());
DCHECK(wb2);
DLOG_IF(WARNING, FAILED(hr)) << StringPrintf(L"SWebBrowserApp 0x%08X", hr);

ScopedComPtr<IWebBrowserPriv> wbp;
ScopedComPtr<IWebBrowserPriv2IE7> wbp2_ie7;
ScopedComPtr<IWebBrowserPriv2IE8> wbp2_ie8;
if (SUCCEEDED(hr = wbp.QueryFrom(wb2))) {
ScopedVariant var_url(url);
hr = wbp->NavigateWithBindCtx(var_url.AsInput(), NULL, NULL, NULL, NULL,
bind_context, NULL);
DLOG_IF(WARNING, FAILED(hr))
<< StringPrintf(L"NavigateWithBindCtx 0x%08X", hr);
} else {
DLOG(WARNING) << StringPrintf(L"IWebBrowserPriv 0x%08X", hr);
IWebBrowserPriv2IE7* common_wbp2 = NULL;
if (SUCCEEDED(hr = wbp2_ie7.QueryFrom(wb2))) {
common_wbp2 = wbp2_ie7;
} else if (SUCCEEDED(hr = wbp2_ie8.QueryFrom(wb2))) {
common_wbp2 = reinterpret_cast<IWebBrowserPriv2IE7*>(wbp2_ie8.get());
}

if (common_wbp2) {
typedef HRESULT (WINAPI* CreateUriFn)(LPCWSTR uri, DWORD flags,
DWORD_PTR reserved, IUri** ret);

CreateUriFn create_uri = reinterpret_cast<CreateUriFn>(
::GetProcAddress(::GetModuleHandleA("urlmon"), "CreateUri"));
DCHECK(create_uri);
if (create_uri) {
ScopedComPtr<IUri> uri_obj;
hr = create_uri(url, 0, 0, uri_obj.Receive());
DLOG_IF(WARNING, FAILED(hr))
<< StringPrintf(L"create_uri 0x%08X", hr);
hr = common_wbp2->NavigateWithBindCtx2(uri_obj, NULL, NULL, NULL,
NULL, bind_context, NULL);
DLOG_IF(WARNING, FAILED(hr))
<< StringPrintf(L"NavigateWithBindCtx2 0x%08X", hr);
}
} else {
DLOG(WARNING) << StringPrintf(L"IWebBrowserPriv2 0x%08X", hr);
NOTREACHED();
}
}

DCHECK(wbp || wbp2_ie7 || wbp2_ie8);

::CoTaskMemFree(url);
if (moniker_) {
ScopedComPtr<IBindCtx> bind_context;
CreateBindCtx(0, bind_context.Receive());
DCHECK(bind_context);
NavigateBrowserToMoniker(host, moniker_, bind_context);
moniker_.Release();
}
}

Expand Down
Loading

0 comments on commit b22ef28

Please sign in to comment.