summaryrefslogtreecommitdiffstats
path: root/chrome_frame/urlmon_moniker.h
blob: ea3245cc5e282225194884657a4c6f18c143b79d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
// 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 "googleurl/src/gurl.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_