// 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_MONIKER_H_ #define CHROME_FRAME_URLMON_MONIKER_H_ #include <atlbase.h> #include <atlcom.h> #include <urlmon.h> #include <string> #include "base/lazy_instance.h" #include "base/logging.h" #include "base/threading/thread_local.h" #include "base/win/scoped_variant.h" #include "chrome_frame/utils.h" // This file contains classes that are used to cache the contents of a top-level // http request (not for sub frames) while that request is parsed for the // presence of a meta tag indicating that the page should be rendered in CF. // Here are a few scenarios we handle and how the classes come to play. // // Scenario 1: Non CF url navigation through address bar (www.msn.com) // - Bho::BeforeNavigate - top level url = www.msn.com // - MSHTML -> MonikerPatch::BindToStorage. // (IEFrame starts this by calling mshtml!*SuperNavigate*) // - check if the url is a top level url // - iff the url is a top level url, we switch in our own callback object // and hook it up to the bind context (BSCBStorageBind) // - otherwise just call the original // - BSCBStorageBind::OnDataAvailable - sniffs data and determines that the // renderer is not chrome. Goes into pass through mode. // - The page loads in mshtml. // // // Scenario 2: CF navigation through address bar URL // - Bho::BeforeNavigate - top level url = http://wave.google.com/ // - MSHTML -> MonikerPatch::BindToStorage. // (IEFrame starts this by calling mshtml!*SuperNavigate*) // - request_data is NULL // - check if the url is a top level url // - iff the url is a top level url, we switch in our own callback object // and hook it up to the bind context (BSCBStorageBind) // - BSCBStorageBind::OnDataAvailable - sniffs data and determines that the // renderer is chrome. It then registers a special bind context param and // sets a magic clip format in the format_etc. Then goes into pass through // mode. // - mshtml looks at the clip format and re-issues the navigation with the // same bind context. Also returns INET_E_TERMINATED_BIND so that same // underlying transaction objects are used. // - IEFrame -> MonikerPatch::BindToStorage // - We check for the special bind context param and instantiate and // return our ActiveDoc // // Scenario 3: CF navigation through mshtml link // Same as scenario #2. // // // Scenario 4: CF navigation through link click in chrome loads non CF page // - Link click comes to ChromeActiveDocument::OnOpenURL // - web_browser->Navigate with URL // - [Scenario 1] // // // Scenario 5: CF navigation through link click in chrome loads CF page // - Link click comes to ChromeActiveDocument::OnOpenURL // - web_browser->Navigate with URL // - [Scenario 2] // // This class is the link between a few static, moniker related functions to // the bho. The specific services needed by those functions are abstracted into // this interface for easier testability. class NavigationManager { public: NavigationManager() { } // Returns the Bho instance for the current thread. This is returned from // TLS. Returns NULL if no instance exists on the current thread. static NavigationManager* GetThreadInstance(); void RegisterThreadInstance(); void UnregisterThreadInstance(); virtual ~NavigationManager() { DCHECK(GetThreadInstance() != this); } // Returns the url of the current top level navigation. const std::wstring& url() const { return url_; } // Called to set the current top level URL that's being navigated to. void set_url(const wchar_t* url) { DVLOG(1) << __FUNCTION__ << " " << url; url_ = url; } // Returns the referrer header value of the current top level navigation. const std::string& referrer() const { return referrer_; } void set_referrer(const std::string& referrer) { referrer_ = referrer; } // Return true if this is a URL that represents a top-level // document that might have to be rendered in CF. virtual bool IsTopLevelUrl(const wchar_t* url); // Called when we've detected the http-equiv meta tag in the current page // and need to switch over from mshtml to CF. virtual HRESULT NavigateToCurrentUrlInCF(IBrowserService* browser); void set_post_data(VARIANT* post_data) { post_data_.Reset(); if (post_data) { if (V_VT(post_data) == (VT_BYREF | VT_VARIANT)) { post_data_.Set(*post_data->pvarVal); } else { NOTREACHED() << "unexpected type for post_data: " << std::hex << post_data->vt; } } } const base::win::ScopedVariant& post_data() const { return post_data_; } void set_headers(VARIANT* headers) { headers_.Reset(); if (headers) { headers_ = *headers; } } const base::win::ScopedVariant& headers() const { return headers_; } protected: std::string referrer_; std::wstring url_; base::win::ScopedVariant post_data_; base::win::ScopedVariant headers_; static base::LazyInstance<base::ThreadLocalPointer<NavigationManager> > thread_singleton_; private: DISALLOW_COPY_AND_ASSIGN(NavigationManager); }; // static-only class that manages an IMoniker patch. // We need this patch to stay in the loop when top-level HTML content is // downloaded that might have the CF http-equiv meta tag. // When we detect candidates for those requests, we add our own callback // object (as explained at the top of this file) and use it to cache the // original document contents in order to avoid multiple network trips // if we need to switch the renderer over to CF. class MonikerPatch { MonikerPatch() {} // no instances should be created of this class. public: // Patches two IMoniker methods, BindToObject and BindToStorage. static bool Initialize(); // Nullifies the IMoniker patches. static void Uninitialize(); // Typedefs for IMoniker methods. typedef HRESULT (STDMETHODCALLTYPE* IMoniker_BindToObject_Fn)(IMoniker* me, IBindCtx* bind_ctx, IMoniker* to_left, REFIID iid, void** obj); typedef HRESULT (STDMETHODCALLTYPE* IMoniker_BindToStorage_Fn)(IMoniker* me, IBindCtx* bind_ctx, IMoniker* to_left, REFIID iid, void** obj); static STDMETHODIMP BindToObject(IMoniker_BindToObject_Fn original, IMoniker* me, IBindCtx* bind_ctx, IMoniker* to_left, REFIID iid, void** obj); static STDMETHODIMP BindToStorage(IMoniker_BindToStorage_Fn original, IMoniker* me, IBindCtx* bind_ctx, IMoniker* to_left, REFIID iid, void** obj); }; extern wchar_t* kChromeRequestParam; #endif // CHROME_FRAME_URLMON_MONIKER_H_