| OLD | NEW |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #ifndef CHROME_FRAME_URLMON_MONIKER_H_ | 5 #ifndef CHROME_FRAME_URLMON_MONIKER_H_ |
| 6 #define CHROME_FRAME_URLMON_MONIKER_H_ | 6 #define CHROME_FRAME_URLMON_MONIKER_H_ |
| 7 | 7 |
| 8 #include <atlbase.h> | 8 #include <atlbase.h> |
| 9 #include <atlcom.h> | 9 #include <atlcom.h> |
| 10 #include <urlmon.h> | 10 #include <urlmon.h> |
| 11 #include <string> | 11 #include <string> |
| 12 | 12 |
| 13 #include "base/lazy_instance.h" | 13 #include "base/lazy_instance.h" |
| 14 #include "base/logging.h" | 14 #include "base/logging.h" |
| 15 #include "base/scoped_comptr_win.h" | 15 #include "base/scoped_comptr_win.h" |
| 16 #include "base/thread_local.h" | 16 #include "base/thread_local.h" |
| 17 | |
| 18 #include "chrome_frame/urlmon_moniker_base.h" | |
| 19 #include "chrome_frame/utils.h" | 17 #include "chrome_frame/utils.h" |
| 20 | 18 |
| 21 // This file contains classes that are used to cache the contents of a top-level | 19 // This file contains classes that are used to cache the contents of a top-level |
| 22 // http request (not for sub frames) while that request is parsed for the | 20 // http request (not for sub frames) while that request is parsed for the |
| 23 // presence of a meta tag indicating that the page should be rendered in CF. | 21 // presence of a meta tag indicating that the page should be rendered in CF. |
| 24 | 22 |
| 25 // Here are a few scenarios we handle and how the classes come to play. | 23 // Here are a few scenarios we handle and how the classes come to play. |
| 26 | 24 |
| 27 // | 25 // |
| 28 // Scenario 1: Non CF url navigation through address bar (www.msn.com) | 26 // Scenario 1: Non CF url navigation through address bar (www.msn.com) |
| 29 // - Bho::BeforeNavigate - top level url = www.msn.com | 27 // - Bho::BeforeNavigate - top level url = www.msn.com |
| 30 // - MSHTML -> MonikerPatch::BindToStorage. | 28 // - MSHTML -> MonikerPatch::BindToStorage. |
| 31 // (IEFrame starts this by calling mshtml!*SuperNavigate*) | 29 // (IEFrame starts this by calling mshtml!*SuperNavigate*) |
| 32 // - request_data is NULL | |
| 33 // - check if the url is a top level url | 30 // - check if the url is a top level url |
| 34 // - iff the url is a top level url, we switch in our own callback object | 31 // - iff the url is a top level url, we switch in our own callback object |
| 35 // and hook it up to the bind context (CFUrlmonBindStatusCallback) | 32 // and hook it up to the bind context (BSCBStorageBind) |
| 36 // The callback object caches the document in memory. | |
| 37 // - otherwise just call the original | 33 // - otherwise just call the original |
| 38 // - Bho::OnHttpEquiv is called with done == TRUE. At this point we determine | 34 // - BSCBStorageBind::OnDataAvailable - sniffs data and determines that the |
| 39 // that the page did not have the CF meta tag in it and we delete the cache. | 35 // renderer is not chrome. Goes into pass through mode. |
| 40 // - The page loads in mshtml. | 36 // - The page loads in mshtml. |
| 41 // | 37 // |
| 42 | 38 |
| 43 // | 39 // |
| 44 // Scenario 2: CF navigation through address bar URL | 40 // Scenario 2: CF navigation through address bar URL |
| 45 // - Bho::BeforeNavigate - top level url = http://wave.google.com/ | 41 // - Bho::BeforeNavigate - top level url = http://wave.google.com/ |
| 46 // - MSHTML -> MonikerPatch::BindToStorage. | 42 // - MSHTML -> MonikerPatch::BindToStorage. |
| 47 // (IEFrame starts this by calling mshtml!*SuperNavigate*) | 43 // (IEFrame starts this by calling mshtml!*SuperNavigate*) |
| 48 // - request_data is NULL | 44 // - request_data is NULL |
| 49 // - check if the url is a top level url | 45 // - check if the url is a top level url |
| 50 // - iff the url is a top level url (in this case, yes), we switch in our own | 46 // - iff the url is a top level url, we switch in our own callback object |
| 51 // callback object and hook it up to the bind context | 47 // and hook it up to the bind context (BSCBStorageBind) |
| 52 // (CFUrlmonBindStatusCallback) | 48 // - BSCBStorageBind::OnDataAvailable - sniffs data and determines that the |
| 53 // - As the document is fetched, the callback object caches the | 49 // renderer is chrome. It then registers a special bind context param and |
| 54 // document in memory. | 50 // sets a magic clip format in the format_etc. Then goes into pass through |
| 55 // - Bho::OnHttpEquiv (done == FALSE) | 51 // mode. |
| 56 // - mgr->NavigateToCurrentUrlInCF | 52 // - mshtml looks at the clip format and re-issues the navigation with the |
| 57 // - Set TLS (MarkBrowserOnThreadForCFNavigation) | 53 // same bind context. Also returns INET_E_TERMINATED_BIND so that same |
| 58 // - Create new bind context, moniker for the URL | 54 // underlying transaction objects are used. |
| 59 // - NavigateBrowserToMoniker with new moniker, bind context | |
| 60 // - Our callback _may_ get an error from mshtml indicating that mshtml | |
| 61 // isn't interested in the data anymore (since we started a new | |
| 62 // navigation). If that happens, our callback class | |
| 63 // (CFUrlmonBindStatusCallback) will continue to cache the document | |
| 64 // until all of it has been retrieved successfully. When the data | |
| 65 // is all read, we report INET_E_TERMINATE_BIND (see SimpleBindingImpl) | |
| 66 // as the end result. | |
| 67 // - In the case where all of the data has been downloaded before | |
| 68 // - OnHttpEquiv is called, we will already have the cache but the | |
| 69 // end bind status in the callback will be S_OK. | |
| 70 // - Bho::BeforeNavigate2 - top level url = http://wave.google.com/ | |
| 71 // - IEFrame -> MonikerPatch::BindToStorage | 55 // - IEFrame -> MonikerPatch::BindToStorage |
| 72 // - request_data is not NULL since we now have a cached copy of the content. | 56 // - We check for the special bind context param and instantiate and |
| 73 // - We call BindToStorageFromCache. | 57 // return our ActiveDoc |
| 74 // - HttpNegotiatePatch::ReportProgress | |
| 75 // - Check TLS (CheckForCFNavigation) and report chrome mime type | |
| 76 // - IEFrame does the following: | |
| 77 // - Creates a new moniker | |
| 78 // - Calls MonikerPatch::BindToObject | |
| 79 // - We create an instance of ChromeActiveDocument and initialize it | |
| 80 // with the cached document. | |
| 81 // - ChromeActiveDocument gives the UrlmonUrlRequestManager the cached | |
| 82 // contents (RequestData) which the manager will use as the content | |
| 83 // when serving up content for the CF document. | |
| 84 // | |
| 85 | 58 |
| 86 // | 59 // |
| 87 // Scenario 3: CF navigation through mshtml link | 60 // Scenario 3: CF navigation through mshtml link |
| 88 // Same as scenario #2. | 61 // Same as scenario #2. |
| 89 // | 62 // |
| 90 | 63 |
| 91 // | 64 // |
| 92 // Scenario 4: CF navigation through link click in chrome loads non CF page | 65 // Scenario 4: CF navigation through link click in chrome loads non CF page |
| 93 // - Link click comes to ChromeActiveDocument::OnOpenURL | 66 // - Link click comes to ChromeActiveDocument::OnOpenURL |
| 94 // - web_browser->Navigate with URL | 67 // - web_browser->Navigate with URL |
| 95 // - [Scenario 1] | 68 // - [Scenario 1] |
| 96 // | 69 // |
| 97 | 70 |
| 98 // | 71 // |
| 99 // Scenario 5: CF navigation through link click in chrome loads CF page | 72 // Scenario 5: CF navigation through link click in chrome loads CF page |
| 100 // - Link click comes to ChromeActiveDocument::OnOpenURL | 73 // - Link click comes to ChromeActiveDocument::OnOpenURL |
| 101 // - web_browser->Navigate with URL | 74 // - web_browser->Navigate with URL |
| 102 // - [Scenario 2] | 75 // - [Scenario 2] |
| 103 // | 76 // |
| 104 | 77 |
| 105 // An implementation of IStream that delegates to another source | |
| 106 // but caches all data that is read from the source. | |
| 107 class ReadStreamCache | |
| 108 : public CComObjectRootEx<CComSingleThreadModel>, | |
| 109 public DelegatingReadStream { | |
| 110 public: | |
| 111 ReadStreamCache() { | |
| 112 DLOG(INFO) << __FUNCTION__; | |
| 113 } | |
| 114 | |
| 115 ~ReadStreamCache() { | |
| 116 DLOG(INFO) << __FUNCTION__ << " cache: " << GetCacheSize(); | |
| 117 } | |
| 118 | |
| 119 BEGIN_COM_MAP(ReadStreamCache) | |
| 120 COM_INTERFACE_ENTRY(IStream) | |
| 121 COM_INTERFACE_ENTRY(ISequentialStream) | |
| 122 END_COM_MAP() | |
| 123 | |
| 124 IStream* cache() const { | |
| 125 return cache_; | |
| 126 } | |
| 127 | |
| 128 void RewindCache() const; | |
| 129 | |
| 130 HRESULT WriteToCache(const void* data, ULONG size, ULONG* written); | |
| 131 | |
| 132 // Returns how many bytes we've cached. Used for logging. | |
| 133 size_t GetCacheSize() const; | |
| 134 | |
| 135 // ISequentialStream. | |
| 136 STDMETHOD(Read)(void* pv, ULONG cb, ULONG* read); | |
| 137 | |
| 138 protected: | |
| 139 ScopedComPtr<IStream> cache_; | |
| 140 | |
| 141 private: | |
| 142 DISALLOW_COPY_AND_ASSIGN(ReadStreamCache); | |
| 143 }; | |
| 144 | |
| 145 // Basic implementation of IBinding with the option of offering a way | |
| 146 // to override the bind result error value. | |
| 147 class SimpleBindingImpl | |
| 148 : public CComObjectRootEx<CComSingleThreadModel>, | |
| 149 public DelegatingBinding { | |
| 150 public: | |
| 151 SimpleBindingImpl() : bind_results_(S_OK) { | |
| 152 } | |
| 153 | |
| 154 ~SimpleBindingImpl() { | |
| 155 } | |
| 156 | |
| 157 BEGIN_COM_MAP(SimpleBindingImpl) | |
| 158 COM_INTERFACE_ENTRY(IBinding) | |
| 159 COM_INTERFACE_ENTRY_FUNC_BLIND(0, DelegateQI) | |
| 160 END_COM_MAP() | |
| 161 | |
| 162 static STDMETHODIMP DelegateQI(void* obj, REFIID iid, void** ret, | |
| 163 DWORD cookie); | |
| 164 | |
| 165 STDMETHOD(GetBindResult)(CLSID* protocol, DWORD* result_code, | |
| 166 LPOLESTR* result, DWORD* reserved); | |
| 167 | |
| 168 void OverrideBindResults(HRESULT results) { | |
| 169 bind_results_ = results; | |
| 170 } | |
| 171 | |
| 172 protected: | |
| 173 HRESULT bind_results_; | |
| 174 | |
| 175 private: | |
| 176 DISALLOW_COPY_AND_ASSIGN(SimpleBindingImpl); | |
| 177 }; | |
| 178 | |
| 179 class RequestHeaders | |
| 180 : public base::RefCountedThreadSafe<RequestHeaders> { | |
| 181 public: | |
| 182 RequestHeaders() : response_code_(-1) { | |
| 183 } | |
| 184 | |
| 185 ~RequestHeaders() { | |
| 186 } | |
| 187 | |
| 188 void OnBeginningTransaction(const wchar_t* url, const wchar_t* headers, | |
| 189 const wchar_t* additional_headers); | |
| 190 | |
| 191 void OnResponse(DWORD response_code, const wchar_t* response_headers, | |
| 192 const wchar_t* request_headers); | |
| 193 | |
| 194 const std::wstring& request_url() const { | |
| 195 return request_url_; | |
| 196 } | |
| 197 | |
| 198 // Invokes BeginningTransaction and OnResponse on the |http| object | |
| 199 // providing already cached headers and status values. | |
| 200 // Any additional headers returned from either of the two methods are ignored. | |
| 201 HRESULT FireHttpNegotiateEvents(IHttpNegotiate* http) const; | |
| 202 | |
| 203 std::string GetReferrer(); | |
| 204 | |
| 205 protected: | |
| 206 std::wstring request_url_; | |
| 207 std::wstring begin_request_headers_; | |
| 208 std::wstring additional_request_headers_; | |
| 209 std::wstring request_headers_; | |
| 210 std::wstring response_headers_; | |
| 211 DWORD response_code_; | |
| 212 | |
| 213 private: | |
| 214 DISALLOW_COPY_AND_ASSIGN(RequestHeaders); | |
| 215 }; | |
| 216 | |
| 217 // Holds cached data for a urlmon request. | |
| 218 class RequestData | |
| 219 : public base::RefCountedThreadSafe<RequestData> { | |
| 220 public: | |
| 221 RequestData(); | |
| 222 ~RequestData(); | |
| 223 | |
| 224 void Initialize(RequestHeaders* headers); | |
| 225 | |
| 226 // Calls IBindStatusCallback::OnDataAvailable and caches any data that is | |
| 227 // read during that operation. | |
| 228 // We also cache the format of the data stream if available during the first | |
| 229 // call to this method. | |
| 230 HRESULT DelegateDataRead(IBindStatusCallback* callback, DWORD flags, | |
| 231 DWORD size, FORMATETC* format, STGMEDIUM* storage, | |
| 232 size_t* bytes_read); | |
| 233 | |
| 234 // Reads everything that's available from |data| into a cached stream. | |
| 235 void CacheAll(IStream* data); | |
| 236 | |
| 237 // Returns a new stream object to read the cache. | |
| 238 // The returned stream object's seek pointer is at pos 0. | |
| 239 HRESULT GetResetCachedContentStream(IStream** clone); | |
| 240 | |
| 241 size_t GetCachedContentSize() const { | |
| 242 return stream_delegate_->GetCacheSize(); | |
| 243 } | |
| 244 | |
| 245 const FORMATETC& format() const { | |
| 246 return format_; | |
| 247 } | |
| 248 | |
| 249 RequestHeaders* headers() const { | |
| 250 return headers_; | |
| 251 } | |
| 252 | |
| 253 void set_headers(RequestHeaders* headers) { | |
| 254 DCHECK(headers); | |
| 255 DCHECK(headers_ == NULL); | |
| 256 headers_ = headers; | |
| 257 } | |
| 258 | |
| 259 protected: | |
| 260 ScopedComPtr<ReadStreamCache, &GUID_NULL> stream_delegate_; | |
| 261 FORMATETC format_; | |
| 262 scoped_refptr<RequestHeaders> headers_; | |
| 263 | |
| 264 private: | |
| 265 DISALLOW_COPY_AND_ASSIGN(RequestData); | |
| 266 }; | |
| 267 | |
| 268 // This class is the link between a few static, moniker related functions to | 78 // This class is the link between a few static, moniker related functions to |
| 269 // the bho. The specific services needed by those functions are abstracted into | 79 // the bho. The specific services needed by those functions are abstracted into |
| 270 // this interface for easier testability. | 80 // this interface for easier testability. |
| 271 class NavigationManager { | 81 class NavigationManager { |
| 272 public: | 82 public: |
| 273 NavigationManager() { | 83 NavigationManager() { |
| 274 } | 84 } |
| 275 | 85 |
| 276 // Returns the Bho instance for the current thread. This is returned from | 86 // Returns the Bho instance for the current thread. This is returned from |
| 277 // TLS. Returns NULL if no instance exists on the current thread. | 87 // TLS. Returns NULL if no instance exists on the current thread. |
| 278 static NavigationManager* GetThreadInstance(); | 88 static NavigationManager* GetThreadInstance(); |
| 279 | 89 |
| 90 // Mark a bind context for navigation by storing a bind context param. |
| 91 static HRESULT AttachCFObject(IBindCtx* bind_context); |
| 92 static HRESULT DetachCFObject(IMoniker* moniker, IBindCtx* bind_context, |
| 93 IUnknown** object); |
| 94 |
| 280 void RegisterThreadInstance(); | 95 void RegisterThreadInstance(); |
| 281 void UnregisterThreadInstance(); | 96 void UnregisterThreadInstance(); |
| 282 | 97 |
| 283 virtual ~NavigationManager() { | 98 virtual ~NavigationManager() { |
| 284 DCHECK(GetThreadInstance() != this); | 99 DCHECK(GetThreadInstance() != this); |
| 285 } | 100 } |
| 286 | 101 |
| 287 // Returns the url of the current top level navigation. | 102 // Returns the url of the current top level navigation. |
| 288 const std::wstring& url() const { | 103 const std::wstring& url() const { |
| 289 return url_; | 104 return url_; |
| (...skipping 16 matching lines...) Expand all Loading... |
| 306 | 121 |
| 307 // Returns the referrer header value of the current top level navigation. | 122 // Returns the referrer header value of the current top level navigation. |
| 308 const std::string& referrer() const { | 123 const std::string& referrer() const { |
| 309 return referrer_; | 124 return referrer_; |
| 310 } | 125 } |
| 311 | 126 |
| 312 void set_referrer(const std::string& referrer) { | 127 void set_referrer(const std::string& referrer) { |
| 313 referrer_ = referrer; | 128 referrer_ = referrer; |
| 314 } | 129 } |
| 315 | 130 |
| 316 // Called when a top level navigation has finished and we don't need to | |
| 317 // keep the cached content around anymore. | |
| 318 virtual void ReleaseRequestData() { | |
| 319 DLOG(INFO) << __FUNCTION__; | |
| 320 url_.clear(); | |
| 321 SetActiveRequestData(NULL); | |
| 322 } | |
| 323 | |
| 324 // Return true if this is a URL that represents a top-level | 131 // Return true if this is a URL that represents a top-level |
| 325 // document that might have to be rendered in CF. | 132 // document that might have to be rendered in CF. |
| 326 virtual bool IsTopLevelUrl(const wchar_t* url) { | 133 virtual bool IsTopLevelUrl(const wchar_t* url) { |
| 327 return lstrcmpiW(url_.c_str(), url) == 0; | 134 return lstrcmpiW(url_.c_str(), url) == 0; |
| 328 } | 135 } |
| 329 | 136 |
| 330 // Called from HttpNegotiatePatch::BeginningTransaction when a request is | 137 // Called from HttpNegotiatePatch::BeginningTransaction when a request is |
| 331 // being issued. We check the url and headers and see if there is a referrer | 138 // being issued. We check the url and headers and see if there is a referrer |
| 332 // header that we need to cache. | 139 // header that we need to cache. |
| 333 virtual void OnBeginningTransaction(bool is_top_level, const wchar_t* url, | 140 virtual void OnBeginningTransaction(bool is_top_level, const wchar_t* url, |
| 334 const wchar_t* headers, | 141 const wchar_t* headers, |
| 335 const wchar_t* additional_headers); | 142 const wchar_t* additional_headers); |
| 336 | 143 |
| 337 // Called when we've detected the http-equiv meta tag in the current page | 144 // Called when we've detected the http-equiv meta tag in the current page |
| 338 // and need to switch over from mshtml to CF. | 145 // and need to switch over from mshtml to CF. |
| 339 virtual HRESULT NavigateToCurrentUrlInCF(IBrowserService* browser); | 146 virtual HRESULT NavigateToCurrentUrlInCF(IBrowserService* browser); |
| 340 | 147 |
| 341 virtual void SetActiveRequestData(RequestData* request_data); | |
| 342 | |
| 343 // When BindToObject is called on a URL before BindToStorage is called, | |
| 344 // the request and response headers are reported on that moniker. | |
| 345 // Later BindToStorage is called to fetch the content. We use the | |
| 346 // RequestHeaders class to carry over the headers to the RequestData object | |
| 347 // that will be created to hold the content. | |
| 348 virtual void SetActiveRequestHeaders(RequestHeaders* request_headers) { | |
| 349 request_headers_ = request_headers; | |
| 350 } | |
| 351 | |
| 352 virtual RequestHeaders* GetActiveRequestHeaders() { | |
| 353 return request_headers_; | |
| 354 } | |
| 355 | |
| 356 virtual RequestData* GetActiveRequestData(const wchar_t* url) { | |
| 357 return IsTopLevelUrl(url) ? request_data_.get() : NULL; | |
| 358 } | |
| 359 | |
| 360 protected: | 148 protected: |
| 361 std::string referrer_; | 149 std::string referrer_; |
| 362 std::wstring url_; | 150 std::wstring url_; |
| 363 scoped_refptr<RequestData> request_data_; | 151 |
| 364 scoped_refptr<RequestHeaders> request_headers_; | |
| 365 static base::LazyInstance<base::ThreadLocalPointer<NavigationManager> > | 152 static base::LazyInstance<base::ThreadLocalPointer<NavigationManager> > |
| 366 thread_singleton_; | 153 thread_singleton_; |
| 367 | 154 |
| 368 // If the url being navigated to within ChromeFrame has a fragment, this | 155 // If the url being navigated to within ChromeFrame has a fragment, this |
| 369 // member contains this URL. This member is cleared when the Chrome active | 156 // member contains this URL. This member is cleared when the Chrome active |
| 370 // document is loaded. | 157 // document is loaded. |
| 371 std::wstring original_url_with_fragment_; | 158 std::wstring original_url_with_fragment_; |
| 372 | 159 |
| 373 private: | 160 private: |
| 374 DISALLOW_COPY_AND_ASSIGN(NavigationManager); | 161 DISALLOW_COPY_AND_ASSIGN(NavigationManager); |
| (...skipping 22 matching lines...) Expand all Loading... |
| 397 IBindCtx* bind_ctx, IMoniker* to_left, REFIID iid, void** obj); | 184 IBindCtx* bind_ctx, IMoniker* to_left, REFIID iid, void** obj); |
| 398 | 185 |
| 399 static STDMETHODIMP BindToObject(IMoniker_BindToObject_Fn original, | 186 static STDMETHODIMP BindToObject(IMoniker_BindToObject_Fn original, |
| 400 IMoniker* me, IBindCtx* bind_ctx, | 187 IMoniker* me, IBindCtx* bind_ctx, |
| 401 IMoniker* to_left, REFIID iid, void** obj); | 188 IMoniker* to_left, REFIID iid, void** obj); |
| 402 | 189 |
| 403 static STDMETHODIMP BindToStorage(IMoniker_BindToStorage_Fn original, | 190 static STDMETHODIMP BindToStorage(IMoniker_BindToStorage_Fn original, |
| 404 IMoniker* me, IBindCtx* bind_ctx, | 191 IMoniker* me, IBindCtx* bind_ctx, |
| 405 IMoniker* to_left, REFIID iid, void** obj); | 192 IMoniker* to_left, REFIID iid, void** obj); |
| 406 | 193 |
| 407 // Reads content from cache (owned by RequestData) and simulates a regular | |
| 408 // binding by calling the expected methods on the callback object bound to | |
| 409 // the bind context. | |
| 410 static HRESULT BindToStorageFromCache(IBindCtx* bind_ctx, | |
| 411 const wchar_t* mime_type, | |
| 412 RequestData* data, | |
| 413 SimpleBindingImpl* binding, | |
| 414 IStream** cache_out); | |
| 415 }; | 194 }; |
| 416 | 195 |
| 417 #endif // CHROME_FRAME_URLMON_MONIKER_H_ | 196 #endif // CHROME_FRAME_URLMON_MONIKER_H_ |
| 418 | 197 |
| OLD | NEW |