- add sources.
[platform/framework/web/crosswalk.git] / src / chrome_frame / urlmon_moniker.cc
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome_frame/urlmon_moniker.h"
6
7 #include <exdisp.h>
8 #include <shlguid.h>
9
10 #include "base/strings/string_util.h"
11 #include "base/strings/stringprintf.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "chrome_frame/bho.h"
14 #include "chrome_frame/bind_context_info.h"
15 #include "chrome_frame/chrome_active_document.h"
16 #include "chrome_frame/exception_barrier.h"
17 #include "chrome_frame/urlmon_bind_status_callback.h"
18 #include "chrome_frame/utils.h"
19 #include "chrome_frame/vtable_patch_manager.h"
20 #include "net/http/http_util.h"
21
22 static const int kMonikerBindToObject = 8;
23 static const int kMonikerBindToStorage = kMonikerBindToObject + 1;
24
25 base::LazyInstance<base::ThreadLocalPointer<NavigationManager> >
26     NavigationManager::thread_singleton_ = LAZY_INSTANCE_INITIALIZER;
27
28 BEGIN_VTABLE_PATCHES(IMoniker)
29   VTABLE_PATCH_ENTRY(kMonikerBindToObject, MonikerPatch::BindToObject)
30   VTABLE_PATCH_ENTRY(kMonikerBindToStorage, MonikerPatch::BindToStorage)
31 END_VTABLE_PATCHES()
32
33 ////////////////////////////
34
35 HRESULT NavigationManager::NavigateToCurrentUrlInCF(IBrowserService* browser) {
36   DCHECK(browser);
37   DVLOG(1) << __FUNCTION__ << " " << url();
38
39   MarkBrowserOnThreadForCFNavigation(browser);
40
41   HRESULT hr = S_OK;
42   base::win::ScopedComPtr<IShellBrowser> shell_browser;
43   base::win::ScopedComPtr<IBindCtx> bind_context;
44   hr = ::CreateAsyncBindCtxEx(NULL, 0, NULL, NULL, bind_context.Receive(), 0);
45
46   base::win::ScopedComPtr<IMoniker> moniker;
47   DCHECK(bind_context);
48   if (SUCCEEDED(hr) &&
49       SUCCEEDED(hr = ::CreateURLMonikerEx(NULL, url_.c_str(), moniker.Receive(),
50                                           URL_MK_UNIFORM))) {
51     if (SUCCEEDED(hr)) {
52       // If there's a referrer, preserve it.
53       std::wstring headers;
54       if (!referrer_.empty()) {
55         headers = base::StringPrintf(L"Referer: %ls\r\n\r\n",
56             ASCIIToWide(referrer_).c_str());
57       }
58
59       // Pass in URL fragments if applicable.
60       std::wstring fragment;
61       GURL parsed_moniker_url(url_);
62       if (parsed_moniker_url.has_ref()) {
63         fragment = UTF8ToWide(parsed_moniker_url.ref());
64       }
65
66       VARIANT flags = { VT_I4 };
67       V_VT(&flags) = navNoHistory | navOpenInNewWindow;
68
69       hr = NavigateBrowserToMoniker(browser, moniker, headers.c_str(),
70           bind_context, fragment.c_str(), NULL, &flags);
71       DVLOG(1) << base::StringPrintf("NavigateBrowserToMoniker: 0x%08X", hr);
72     }
73   }
74
75   return hr;
76 }
77
78 bool NavigationManager::IsTopLevelUrl(const wchar_t* url) {
79   return CompareUrlsWithoutFragment(url_.c_str(), url);
80 }
81
82 /////////////////////////////////////////
83
84 NavigationManager* NavigationManager::GetThreadInstance() {
85   return thread_singleton_.Pointer()->Get();
86 }
87
88 void NavigationManager::RegisterThreadInstance() {
89   DCHECK(GetThreadInstance() == NULL);
90   thread_singleton_.Pointer()->Set(this);
91 }
92
93 void NavigationManager::UnregisterThreadInstance() {
94   DCHECK(GetThreadInstance() == this);
95   thread_singleton_.Pointer()->Set(NULL);
96 }
97
98 /////////////////////////////////////////
99
100 // static
101 bool MonikerPatch::Initialize() {
102   if (IS_PATCHED(IMoniker)) {
103     DLOG(WARNING) << __FUNCTION__ << " called more than once.";
104     return true;
105   }
106
107   base::win::ScopedComPtr<IMoniker> moniker;
108   HRESULT hr = ::CreateURLMoniker(NULL, L"http://localhost/",
109                                   moniker.Receive());
110   DCHECK(SUCCEEDED(hr));
111   if (SUCCEEDED(hr)) {
112     hr = vtable_patch::PatchInterfaceMethods(moniker, IMoniker_PatchInfo);
113     DLOG_IF(ERROR, FAILED(hr)) << base::StringPrintf(
114         "patch failed 0x%08X", hr);
115   }
116
117   return SUCCEEDED(hr);
118 }
119
120 // static
121 void MonikerPatch::Uninitialize() {
122   vtable_patch::UnpatchInterfaceMethods(IMoniker_PatchInfo);
123 }
124
125 bool ShouldWrapCallback(IMoniker* moniker, REFIID iid, IBindCtx* bind_context) {
126   CComHeapPtr<WCHAR> url;
127   HRESULT hr = moniker->GetDisplayName(bind_context, NULL, &url);
128   if (!url) {
129     DVLOG(1) << __FUNCTION__
130              << base::StringPrintf(" GetDisplayName failed. Error: 0x%x", hr);
131     return false;
132   }
133
134   if (!IsEqualIID(IID_IStream, iid)) {
135     DVLOG(1) << __FUNCTION__ << " Url: " << url
136              << " Not wrapping: IID is not IStream.";
137     return false;
138   }
139
140   base::win::ScopedComPtr<BindContextInfo> info;
141   BindContextInfo::FromBindContext(bind_context, info.Receive());
142   DCHECK(info);
143   if (info && info->chrome_request()) {
144     DVLOG(1) << __FUNCTION__ << " Url: " << url
145              << " Not wrapping: request from chrome frame.";
146     return false;
147   }
148
149   NavigationManager* mgr = NavigationManager::GetThreadInstance();
150   if (!mgr) {
151     DVLOG(1) << __FUNCTION__ << " Url: " << url
152              << " No navigation manager to wrap";
153     return false;
154   }
155
156   // Check whether request comes from MSHTML by checking for IInternetBindInfo.
157   // We prefer to avoid wrapping if BindToStorage is called from AcroPDF.dll
158   // (as a result of OnObjectAvailable)
159   base::win::ScopedComPtr<IUnknown> bscb_holder;
160   if (S_OK == bind_context->GetObjectParam(L"_BSCB_Holder_",
161                                            bscb_holder.Receive())) {
162     base::win::ScopedComPtr<IBindStatusCallback> bscb;
163     if (S_OK != DoQueryService(IID_IBindStatusCallback, bscb_holder,
164                                bscb.Receive()))
165       return false;
166
167     if (!bscb.get())
168       return false;
169
170     base::win::ScopedComPtr<IInternetBindInfo> bind_info;
171     if (S_OK != bind_info.QueryFrom(bscb))
172       return false;
173   }
174
175   // TODO(ananta)
176   // Use the IsSubFrameRequest function to determine if a request is a top
177   // level request. Something like this.
178   // base::win::ScopedComPtr<IUnknown> bscb_holder;
179   // bind_context->GetObjectParam(L"_BSCB_Holder_", bscb_holder.Receive());
180   // if (bscb_holder) {
181   //   base::win::ScopedComPtr<IHttpNegotiate> http_negotiate;
182   //   http_negotiate.QueryFrom(bscb_holder);
183   //   if (http_negotiate && !IsSubFrameRequest(http_negotiate))
184   //     return true;
185   //  }
186   // There are some cases where the IsSubFrameRequest function can return
187   // incorrect results.
188   bool should_wrap = mgr->IsTopLevelUrl(url);
189   if (!should_wrap) {
190     DVLOG(1) << __FUNCTION__ << " Url: " << url
191              << " Not wrapping: Not top level url.";
192   }
193   return should_wrap;
194 }
195
196 // static
197 HRESULT MonikerPatch::BindToObject(IMoniker_BindToObject_Fn original,
198                                    IMoniker* me, IBindCtx* bind_ctx,
199                                    IMoniker* to_left, REFIID iid, void** obj) {
200   DVLOG(1) << __FUNCTION__;
201   DCHECK(to_left == NULL);
202
203   ExceptionBarrierReportOnlyModule barrier;
204
205   HRESULT hr = S_OK;
206   // Bind context is marked for switch when we sniff data in BSCBStorageBind
207   // and determine that the renderer to be used is Chrome.
208   base::win::ScopedComPtr<BindContextInfo> info;
209   BindContextInfo::FromBindContext(bind_ctx, info.Receive());
210   DCHECK(info);
211   if (info) {
212     if (info->is_switching()) {
213       // We could implement the BindToObject ourselves here but instead we
214       // simply register Chrome Frame ActiveDoc as a handler for 'text/html'
215       // in this bind context.  This makes urlmon instantiate CF Active doc
216       // instead of mshtml.
217       const char* media_types[] = { "text/html" };
218       CLSID classes[] = { CLSID_ChromeActiveDocument };
219       hr = RegisterMediaTypeClass(bind_ctx, arraysize(media_types), media_types,
220                                   classes, 0);
221     } else {
222       // In case the binding begins with BindToObject we do not need
223       // to cache the data in the sniffing code.
224       info->set_no_cache(true);
225     }
226   }
227
228   hr = original(me, bind_ctx, to_left, iid, obj);
229   return hr;
230 }
231
232 // static
233 HRESULT MonikerPatch::BindToStorage(IMoniker_BindToStorage_Fn original,
234                                     IMoniker* me, IBindCtx* bind_ctx,
235                                     IMoniker* to_left, REFIID iid, void** obj) {
236   DCHECK(to_left == NULL);
237
238   // Report a crash if the crash is in our own module.
239   ExceptionBarrierReportOnlyModule barrier;
240
241   HRESULT hr = S_OK;
242   scoped_refptr<BSCBStorageBind> auto_release_callback;
243   CComObject<BSCBStorageBind>* callback = NULL;
244   if (ShouldWrapCallback(me, iid, bind_ctx)) {
245     hr = CComObject<BSCBStorageBind>::CreateInstance(&callback);
246     DCHECK(SUCCEEDED(hr));
247     auto_release_callback = callback;
248     DCHECK_EQ(callback->m_dwRef, 1);
249     hr = callback->Initialize(me, bind_ctx);
250     DCHECK(SUCCEEDED(hr));
251   }
252
253   hr = original(me, bind_ctx, to_left, iid, obj);
254
255   // If the binding terminates before the data could be played back
256   // now is the chance. Sometimes OnStopBinding happens after this returns
257   // and then it's too late.
258   if ((S_OK == hr) && callback)
259     callback->MayPlayBack(BSCF_LASTDATANOTIFICATION);
260
261   return hr;
262 }
263