chromeos: dbus: add Bluetooth properties support
[chromium-blink-merge.git] / chrome_frame / bho.cc
blobf6b87a44cf44c3b7fb3332e0e9115d9df864f871
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.
5 #include "chrome_frame/bho.h"
7 #include <shlguid.h>
9 #include "base/file_path.h"
10 #include "base/logging.h"
11 #include "base/path_service.h"
12 #include "base/string_util.h"
13 #include "base/stringprintf.h"
14 #include "base/win/scoped_bstr.h"
15 #include "chrome_frame/buggy_bho_handling.h"
16 #include "chrome_frame/crash_reporting/crash_metrics.h"
17 #include "chrome_frame/extra_system_apis.h"
18 #include "chrome_frame/html_utils.h"
19 #include "chrome_frame/http_negotiate.h"
20 #include "chrome_frame/metrics_service.h"
21 #include "chrome_frame/protocol_sink_wrap.h"
22 #include "chrome_frame/ready_mode/ready_mode.h"
23 #include "chrome_frame/urlmon_moniker.h"
24 #include "chrome_frame/utils.h"
25 #include "chrome_frame/vtable_patch_manager.h"
27 static const int kIBrowserServiceOnHttpEquivIndex = 30;
28 static const DWORD kMaxHttpConnections = 6;
30 PatchHelper g_patch_helper;
32 BEGIN_VTABLE_PATCHES(IBrowserService)
33 VTABLE_PATCH_ENTRY(kIBrowserServiceOnHttpEquivIndex, Bho::OnHttpEquiv)
34 END_VTABLE_PATCHES()
36 _ATL_FUNC_INFO Bho::kBeforeNavigate2Info = {
37 CC_STDCALL, VT_EMPTY, 7, {
38 VT_DISPATCH,
39 VT_VARIANT | VT_BYREF,
40 VT_VARIANT | VT_BYREF,
41 VT_VARIANT | VT_BYREF,
42 VT_VARIANT | VT_BYREF,
43 VT_VARIANT | VT_BYREF,
44 VT_BOOL | VT_BYREF
48 _ATL_FUNC_INFO Bho::kNavigateComplete2Info = {
49 CC_STDCALL, VT_EMPTY, 2, {
50 VT_DISPATCH,
51 VT_VARIANT | VT_BYREF
55 _ATL_FUNC_INFO Bho::kDocumentCompleteInfo = {
56 CC_STDCALL, VT_EMPTY, 2, {
57 VT_DISPATCH,
58 VT_VARIANT | VT_BYREF
62 Bho::Bho() {
65 HRESULT Bho::FinalConstruct() {
66 return S_OK;
69 void Bho::FinalRelease() {
72 namespace {
74 // Allows Ready Mode to disable Chrome Frame by deactivating User Agent
75 // modification and X-UA-Compatible header/tag detection.
76 class ReadyModeDelegateImpl : public ready_mode::Delegate {
77 public:
78 ReadyModeDelegateImpl() {}
80 // ready_mode::Delegate implementation
81 virtual void DisableChromeFrame();
83 private:
84 DISALLOW_COPY_AND_ASSIGN(ReadyModeDelegateImpl);
85 }; // class ReadyModeDelegateImpl
87 void ReadyModeDelegateImpl::DisableChromeFrame() {
88 HttpNegotiatePatch::set_modify_user_agent(false);
89 ProtocolSinkWrap::set_ignore_xua(true);
92 } // namespace
94 STDMETHODIMP Bho::SetSite(IUnknown* site) {
95 HRESULT hr = S_OK;
96 if (site) {
97 base::win::ScopedComPtr<IWebBrowser2> web_browser2;
98 web_browser2.QueryFrom(site);
99 if (web_browser2) {
100 hr = DispEventAdvise(web_browser2, &DIID_DWebBrowserEvents2);
101 DCHECK(SUCCEEDED(hr)) << "DispEventAdvise failed. Error: " << hr;
103 ready_mode::Configure(new ReadyModeDelegateImpl(), web_browser2);
106 if (g_patch_helper.state() == PatchHelper::PATCH_IBROWSER) {
107 base::win::ScopedComPtr<IBrowserService> browser_service;
108 hr = DoQueryService(SID_SShellBrowser, site, browser_service.Receive());
109 DCHECK(browser_service) << "DoQueryService - SID_SShellBrowser failed."
110 << " Site: " << site << " Error: " << hr;
111 if (browser_service) {
112 g_patch_helper.PatchBrowserService(browser_service);
113 DCHECK(SUCCEEDED(hr)) << "vtable_patch::PatchInterfaceMethods failed."
114 << " Site: " << site << " Error: " << hr;
117 // Save away our BHO instance in TLS which enables it to be referenced by
118 // our active document/activex instances to query referrer and other
119 // information for a URL.
120 AddRef();
121 RegisterThreadInstance();
122 MetricsService::Start();
124 if (!IncreaseWinInetConnections(kMaxHttpConnections)) {
125 DLOG(WARNING) << "Failed to bump up HTTP connections. Error:"
126 << ::GetLastError();
128 } else {
129 UnregisterThreadInstance();
130 buggy_bho::BuggyBhoTls::DestroyInstance();
131 base::win::ScopedComPtr<IWebBrowser2> web_browser2;
132 web_browser2.QueryFrom(m_spUnkSite);
133 DispEventUnadvise(web_browser2, &DIID_DWebBrowserEvents2);
134 Release();
137 return IObjectWithSiteImpl<Bho>::SetSite(site);
140 STDMETHODIMP Bho::BeforeNavigate2(IDispatch* dispatch, VARIANT* url,
141 VARIANT* flags, VARIANT* target_frame_name, VARIANT* post_data,
142 VARIANT* headers, VARIANT_BOOL* cancel) {
143 if (!url || url->vt != VT_BSTR || url->bstrVal == NULL) {
144 DLOG(WARNING) << "Invalid URL passed in";
145 return S_OK;
148 base::win::ScopedComPtr<IWebBrowser2> web_browser2;
149 if (dispatch)
150 web_browser2.QueryFrom(dispatch);
152 if (!web_browser2) {
153 NOTREACHED() << "Can't find WebBrowser2 with given dispatch";
154 return S_OK;
157 DVLOG(1) << "BeforeNavigate2: " << url->bstrVal;
159 base::win::ScopedComPtr<IBrowserService> browser_service;
160 DoQueryService(SID_SShellBrowser, web_browser2, browser_service.Receive());
161 if (!browser_service || !CheckForCFNavigation(browser_service, false)) {
162 // TODO(tommi): Remove? Isn't this done below by calling set_referrer("")?
163 referrer_.clear();
166 VARIANT_BOOL is_top_level = VARIANT_FALSE;
167 web_browser2->get_TopLevelContainer(&is_top_level);
168 if (is_top_level) {
169 set_url(url->bstrVal);
170 set_referrer("");
171 set_post_data(post_data);
172 set_headers(headers);
174 return S_OK;
177 STDMETHODIMP_(void) Bho::NavigateComplete2(IDispatch* dispatch, VARIANT* url) {
178 DVLOG(1) << __FUNCTION__;
181 STDMETHODIMP_(void) Bho::DocumentComplete(IDispatch* dispatch, VARIANT* url) {
182 DVLOG(1) << __FUNCTION__;
184 base::win::ScopedComPtr<IWebBrowser2> web_browser2;
185 if (dispatch)
186 web_browser2.QueryFrom(dispatch);
188 if (web_browser2) {
189 VARIANT_BOOL is_top_level = VARIANT_FALSE;
190 web_browser2->get_TopLevelContainer(&is_top_level);
191 if (is_top_level) {
192 CrashMetricsReporter::GetInstance()->IncrementMetric(
193 CrashMetricsReporter::NAVIGATION_COUNT);
198 namespace {
200 // See comments in Bho::OnHttpEquiv for details.
201 void ClearDocumentContents(IUnknown* browser) {
202 base::win::ScopedComPtr<IWebBrowser2> web_browser2;
203 if (SUCCEEDED(DoQueryService(SID_SWebBrowserApp, browser,
204 web_browser2.Receive()))) {
205 base::win::ScopedComPtr<IDispatch> doc_disp;
206 web_browser2->get_Document(doc_disp.Receive());
207 base::win::ScopedComPtr<IHTMLDocument2> doc;
208 if (doc_disp && SUCCEEDED(doc.QueryFrom(doc_disp))) {
209 SAFEARRAY* sa = ::SafeArrayCreateVector(VT_UI1, 0, 0);
210 doc->write(sa);
211 ::SafeArrayDestroy(sa);
216 // Returns true if the currently loaded document in the browser has
217 // any embedded items such as a frame or an iframe.
218 bool DocumentHasEmbeddedItems(IUnknown* browser) {
219 bool has_embedded_items = false;
221 base::win::ScopedComPtr<IWebBrowser2> web_browser2;
222 base::win::ScopedComPtr<IDispatch> document;
223 if (SUCCEEDED(DoQueryService(SID_SWebBrowserApp, browser,
224 web_browser2.Receive())) &&
225 SUCCEEDED(web_browser2->get_Document(document.Receive()))) {
226 base::win::ScopedComPtr<IOleContainer> container;
227 if (SUCCEEDED(container.QueryFrom(document))) {
228 base::win::ScopedComPtr<IEnumUnknown> enumerator;
229 container->EnumObjects(OLECONTF_EMBEDDINGS, enumerator.Receive());
230 if (enumerator) {
231 base::win::ScopedComPtr<IUnknown> unk;
232 DWORD fetched = 0;
233 while (!has_embedded_items &&
234 SUCCEEDED(enumerator->Next(1, unk.Receive(), &fetched))
235 && fetched) {
236 // If a top level document has embedded iframes then the theory is
237 // that first the top level document finishes loading and then the
238 // iframes load. We should only treat an embedded element as an
239 // iframe if it supports the IWebBrowser interface.
240 base::win::ScopedComPtr<IWebBrowser2> embedded_web_browser2;
241 if (SUCCEEDED(embedded_web_browser2.QueryFrom(unk))) {
242 // If we initiate a top level navigation then at times MSHTML
243 // creates a temporary IWebBrowser2 interface which basically shows
244 // up as a temporary iframe in the parent document. It is not clear
245 // as to how we can detect this. I tried the usual stuff like
246 // getting to the parent IHTMLWindow2 interface. They all end up
247 // pointing to dummy tear off interfaces owned by MSHTML.
248 // As a temporary workaround, we found that the location url in
249 // this case is about:blank. We now check for the same and don't
250 // treat it as an iframe. This should be fine in most cases as we
251 // hit this code only when the actual page has a meta tag. However
252 // this would break for cases like the initial src url for an
253 // iframe pointing to about:blank and the page then writing to it
254 // via document.write.
255 // TODO(ananta)
256 // Revisit this and come up with a better approach.
257 base::win::ScopedBstr location_url;
258 embedded_web_browser2->get_LocationURL(location_url.Receive());
260 std::wstring location_url_string;
261 location_url_string.assign(location_url, location_url.Length());
263 if (!LowerCaseEqualsASCII(location_url_string, "about:blank")) {
264 has_embedded_items = true;
268 fetched = 0;
269 unk.Release();
275 return has_embedded_items;
278 } // end namespace
280 HRESULT Bho::OnHttpEquiv(IBrowserService_OnHttpEquiv_Fn original_httpequiv,
281 IBrowserService* browser, IShellView* shell_view, BOOL done,
282 VARIANT* in_arg, VARIANT* out_arg) {
283 DVLOG(1) << __FUNCTION__ << " done:" << done;
285 // OnHttpEquiv with 'done' set to TRUE is called for all pages.
286 // 0 or more calls with done set to FALSE are made.
287 // When done is FALSE, the current moniker may not represent the page
288 // being navigated to so we always have to wait for done to be TRUE
289 // before re-initiating the navigation.
291 if (!done && in_arg && VT_BSTR == V_VT(in_arg)) {
292 if (StrStrI(V_BSTR(in_arg), kChromeContentPrefix)) {
293 // OnHttpEquiv is invoked for meta tags within sub frames as well.
294 // We want to switch renderers only for the top level frame.
295 // The theory here is that if there are any existing embedded items
296 // (frames or iframes) in the current document, then the http-equiv
297 // notification is coming from those and not the top level document.
298 // The embedded items should only be created once the top level
299 // doc has been created.
300 if (!DocumentHasEmbeddedItems(browser)) {
301 NavigationManager* mgr = NavigationManager::GetThreadInstance();
302 DCHECK(mgr);
303 DVLOG(1) << "Found tag in page. Marking browser."
304 << base::StringPrintf(" tid=0x%08X", ::GetCurrentThreadId());
305 if (mgr) {
306 // TODO(tommi): See if we can't figure out a cleaner way to avoid
307 // this. For some documents we can hit a problem here. When we
308 // attempt to navigate the document again in CF, mshtml can "complete"
309 // the current navigation (if all data is available) and fire off
310 // script events such as onload and even render the page.
311 // This will happen inside NavigateBrowserToMoniker below.
312 // To work around this, we clear the contents of the document before
313 // opening it up in CF.
314 ClearDocumentContents(browser);
315 mgr->NavigateToCurrentUrlInCF(browser);
321 return original_httpequiv(browser, shell_view, done, in_arg, out_arg);
324 // static
325 void Bho::ProcessOptInUrls(IWebBrowser2* browser, BSTR url) {
326 if (!browser || !url) {
327 NOTREACHED();
328 return;
331 #ifndef NDEBUG
332 // This check must already have been made.
333 VARIANT_BOOL is_top_level = VARIANT_FALSE;
334 browser->get_TopLevelContainer(&is_top_level);
335 DCHECK(is_top_level);
336 #endif
338 std::wstring current_url(url, SysStringLen(url));
339 if (IsValidUrlScheme(GURL(current_url), false)) {
340 bool cf_protocol = StartsWith(current_url, kChromeProtocolPrefix, false);
341 if (!cf_protocol && IsChrome(RendererTypeForUrl(current_url))) {
342 DVLOG(1) << "Opt-in URL. Switching to cf.";
343 base::win::ScopedComPtr<IBrowserService> browser_service;
344 DoQueryService(SID_SShellBrowser, browser, browser_service.Receive());
345 DCHECK(browser_service) << "DoQueryService - SID_SShellBrowser failed.";
346 MarkBrowserOnThreadForCFNavigation(browser_service);
351 bool PatchHelper::InitializeAndPatchProtocolsIfNeeded() {
352 bool ret = false;
354 _pAtlModule->m_csStaticDataInitAndTypeInfo.Lock();
356 if (state_ == UNKNOWN) {
357 g_trans_hooks.InstallHooks();
358 HttpNegotiatePatch::Initialize();
359 state_ = PATCH_PROTOCOL;
360 ret = true;
363 _pAtlModule->m_csStaticDataInitAndTypeInfo.Unlock();
365 return ret;
368 void PatchHelper::PatchBrowserService(IBrowserService* browser_service) {
369 DCHECK(state_ == PATCH_IBROWSER);
370 if (!IS_PATCHED(IBrowserService)) {
371 vtable_patch::PatchInterfaceMethods(browser_service,
372 IBrowserService_PatchInfo);
376 void PatchHelper::UnpatchIfNeeded() {
377 if (state_ == PATCH_PROTOCOL) {
378 g_trans_hooks.RevertHooks();
379 HttpNegotiatePatch::Uninitialize();
381 state_ = UNKNOWN;