1 // Copyright (c) 2012 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/ready_mode/ready_mode.h"
10 #include "base/compiler_specific.h"
11 #include "base/logging.h"
12 #include "base/memory/linked_ptr.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/memory/weak_ptr.h"
15 #include "base/win/scoped_bstr.h"
16 #include "base/win/scoped_comptr.h"
17 #include "base/win/win_util.h"
18 #include "chrome/installer/util/browser_distribution.h"
19 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
20 #include "chrome_frame/infobars/infobar_manager.h"
21 #include "chrome_frame/ready_mode/internal/ready_mode_web_browser_adapter.h"
22 #include "chrome_frame/ready_mode/internal/ready_prompt_content.h"
23 #include "chrome_frame/ready_mode/internal/registry_ready_mode_state.h"
24 #include "chrome_frame/ready_mode/internal/url_launcher.h"
25 #include "chrome_frame/utils.h"
29 // Temporarily disable Ready Mode for 36 hours when the user so indicates.
30 const int kTemporaryDeclineDurationMinutes
= 60 * 36;
32 class BrowserObserver
;
34 // A helper for BrowserObserver to observe the user's choice in the Ready Mode
36 class StateObserver
: public RegistryReadyModeState::Observer
{
38 explicit StateObserver(const base::WeakPtr
<BrowserObserver
>& ready_mode_ui
);
41 // RegistryReadyModeState::Observer implementation
42 virtual void OnStateChange(ReadyModeStatus status
);
45 base::WeakPtr
<BrowserObserver
> ready_mode_ui_
;
47 DISALLOW_COPY_AND_ASSIGN(StateObserver
);
48 }; // class StateObserver
50 // Manages the Ready Mode UI in response to browsing ChromeFrame- or Host-
51 // rendered pages. Shows the Ready Mode prompt when the user browses to a GCF-
52 // enabled page. Hides the prompt when the user begins navigating to a new
53 // domain or when they navigate to a new page in the same domain that is not
56 // Uses InstallerAdapter and RegistryReadyMode to query and update the
57 // installation state. Uninstalls the ReadyModeWebBrowserAdapter when the user
58 // temporarily or permanently exits Ready Mode (decline or accept Chrome Frame).
59 // If the user declines Chrome Frame, the current page is reloaded in the Host
61 class BrowserObserver
: public ReadyModeWebBrowserAdapter::Observer
{
63 BrowserObserver(ready_mode::Delegate
* chrome_frame
,
64 IWebBrowser2
* web_browser
,
65 ReadyModeWebBrowserAdapter
* adapter
);
67 // ReadyModeWebBrowserAdapter::Observer implementation
68 virtual void OnNavigateTo(const std::wstring
& url
);
69 virtual void OnRenderInChromeFrame(const std::wstring
& url
);
70 virtual void OnRenderInHost(const std::wstring
& url
);
73 friend class StateObserver
;
75 // Called by the StateObserver
76 void OnReadyModeDisabled();
77 void OnReadyModeAccepted();
79 // Helpers for showing infobar prompts
82 InfobarManager
* GetInfobarManager();
85 linked_ptr
<ready_mode::Delegate
> chrome_frame_
;
86 base::win::ScopedComPtr
<IWebBrowser2
> web_browser_
;
87 // The adapter owns us, so we use a weak reference
88 ReadyModeWebBrowserAdapter
* adapter_
;
89 base::WeakPtrFactory
<BrowserObserver
> weak_ptr_factory_
;
91 DISALLOW_COPY_AND_ASSIGN(BrowserObserver
);
92 }; // class BrowserObserver
94 // Implements launching of a URL in an instance of IWebBrowser2.
95 class UrlLauncherImpl
: public UrlLauncher
{
97 explicit UrlLauncherImpl(IWebBrowser2
* web_browser
);
99 // UrlLauncher implementation
100 void LaunchUrl(const std::wstring
& url
);
103 base::win::ScopedComPtr
<IWebBrowser2
> web_browser_
;
104 }; // class UrlLaucherImpl
106 UrlLauncherImpl::UrlLauncherImpl(IWebBrowser2
* web_browser
) {
108 web_browser_
= web_browser
;
111 void UrlLauncherImpl::LaunchUrl(const std::wstring
& url
) {
112 VARIANT flags
= { VT_I4
};
113 V_I4(&flags
) = navOpenInNewWindow
;
114 base::win::ScopedBstr
location(url
.c_str());
116 HRESULT hr
= web_browser_
->Navigate(location
, &flags
, NULL
, NULL
, NULL
);
117 DLOG_IF(ERROR
, FAILED(hr
)) << "Failed to invoke Navigate on IWebBrowser2. "
121 StateObserver::StateObserver(
122 const base::WeakPtr
<BrowserObserver
>& ready_mode_ui
)
123 : ready_mode_ui_(ready_mode_ui
) {
126 StateObserver::~StateObserver() {
129 void StateObserver::OnStateChange(ReadyModeStatus status
) {
130 if (ready_mode_ui_
== NULL
)
134 case READY_MODE_PERMANENTLY_DECLINED
:
135 case READY_MODE_TEMPORARILY_DECLINED
:
136 ready_mode_ui_
->OnReadyModeDisabled();
139 case READY_MODE_ACCEPTED
:
140 ready_mode_ui_
->OnReadyModeAccepted();
143 case READY_MODE_ACTIVE
:
152 BrowserObserver::BrowserObserver(ready_mode::Delegate
* chrome_frame
,
153 IWebBrowser2
* web_browser
,
154 ReadyModeWebBrowserAdapter
* adapter
)
155 : web_browser_(web_browser
),
156 chrome_frame_(chrome_frame
),
158 weak_ptr_factory_(this) {
161 void BrowserObserver::OnNavigateTo(const std::wstring
& url
) {
162 if (!net::registry_controlled_domains::SameDomainOrHost(
165 net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES
)) {
166 rendered_url_
= GURL();
171 void BrowserObserver::OnRenderInChromeFrame(const std::wstring
& url
) {
173 rendered_url_
= GURL(url
);
176 void BrowserObserver::OnRenderInHost(const std::wstring
& url
) {
178 rendered_url_
= GURL(url
);
181 void BrowserObserver::OnReadyModeDisabled() {
182 // We don't hold a reference to the adapter, since it owns us (in order to
183 // break circular dependency). But we should still AddRef it before
185 base::win::ScopedComPtr
<ReadyModeWebBrowserAdapter
, NULL
> reference(adapter_
);
187 // adapter_->Uninitialize may delete us, so we should not refer to members
189 base::win::ScopedComPtr
<IWebBrowser2
> web_browser(web_browser_
);
191 chrome_frame_
->DisableChromeFrame();
192 adapter_
->Uninitialize();
194 VARIANT flags
= { VT_I4
};
195 V_I4(&flags
) = navNoHistory
;
196 base::win::ScopedBstr location
;
198 HRESULT hr
= web_browser
->get_LocationURL(location
.Receive());
199 DLOG_IF(ERROR
, FAILED(hr
)) << "Failed to get current location from "
200 << "IWebBrowser2. Error: " << hr
;
203 hr
= web_browser
->Navigate(location
, &flags
, NULL
, NULL
, NULL
);
204 DLOG_IF(ERROR
, FAILED(hr
)) << "Failed to invoke Navigate on IWebBrowser2. "
209 void BrowserObserver::OnReadyModeAccepted() {
210 // See comment in OnReadyModeDisabled.
211 base::win::ScopedComPtr
<ReadyModeWebBrowserAdapter
, NULL
> reference(adapter_
);
212 adapter_
->Uninitialize();
215 void BrowserObserver::ShowPrompt() {
216 // This pointer is self-managed and not guaranteed to survive handling of
218 InfobarManager
* infobar_manager
= GetInfobarManager();
220 if (infobar_manager
) {
221 // Owned by ready_mode_state
222 scoped_ptr
<RegistryReadyModeState::Observer
> ready_mode_state_observer(
223 new StateObserver(weak_ptr_factory_
.GetWeakPtr()));
225 BrowserDistribution
* dist
=
226 BrowserDistribution::GetSpecificDistribution(
227 BrowserDistribution::CHROME_BINARIES
);
229 // Owned by infobar_content
230 scoped_ptr
<ReadyModeState
> ready_mode_state(new RegistryReadyModeState(
232 base::TimeDelta::FromMinutes(kTemporaryDeclineDurationMinutes
),
233 ready_mode_state_observer
.release()));
235 // Owned by infobar_content
236 scoped_ptr
<UrlLauncher
> url_launcher(new UrlLauncherImpl(web_browser_
));
238 // Owned by infobar_manager
239 scoped_ptr
<InfobarContent
> infobar_content(new ReadyPromptContent(
240 ready_mode_state
.release(), url_launcher
.release()));
242 infobar_manager
->Show(infobar_content
.release(), TOP_INFOBAR
);
246 void BrowserObserver::Hide() {
247 InfobarManager
* infobar_manager
= GetInfobarManager();
249 infobar_manager
->HideAll();
252 InfobarManager
* BrowserObserver::GetInfobarManager() {
253 HRESULT hr
= NOERROR
;
255 base::win::ScopedComPtr
<IOleWindow
> ole_window
;
256 hr
= DoQueryService(SID_SShellBrowser
, web_browser_
, ole_window
.Receive());
257 if (FAILED(hr
) || ole_window
== NULL
) {
258 DLOG(ERROR
) << "Failed to query SID_SShellBrowser from IWebBrowser2. "
263 HWND web_browserhwnd
= NULL
;
264 hr
= ole_window
->GetWindow(&web_browserhwnd
);
265 if (FAILED(hr
) || web_browserhwnd
== NULL
) {
266 DLOG(ERROR
) << "Failed to query HWND from IOleWindow. "
271 return InfobarManager::Get(web_browserhwnd
);
274 // Wraps an existing Delegate so that ownership may be shared.
275 class DelegateWrapper
: public ready_mode::Delegate
{
277 explicit DelegateWrapper(linked_ptr
<ready_mode::Delegate
> wrapped
);
279 // ready_mode::Delegate implementation
280 virtual void DisableChromeFrame();
283 linked_ptr
<ready_mode::Delegate
> wrapped_
;
285 DISALLOW_COPY_AND_ASSIGN(DelegateWrapper
);
286 }; // class DelegateWrapper
288 DelegateWrapper::DelegateWrapper(linked_ptr
<ready_mode::Delegate
> wrapped
)
289 : wrapped_(wrapped
) {
292 void DelegateWrapper::DisableChromeFrame() {
293 wrapped_
->DisableChromeFrame();
296 // Attempts to create a ReadyModeWebBrowserAdapter instance.
297 bool CreateWebBrowserAdapter(ReadyModeWebBrowserAdapter
** pointer
) {
300 CComObject
<ReadyModeWebBrowserAdapter
>* com_object
;
302 CComObject
<ReadyModeWebBrowserAdapter
>::CreateInstance(&com_object
);
305 DLOG(ERROR
) << "Failed to create instance of ReadyModeWebBrowserAdapter. "
310 com_object
->AddRef();
311 *pointer
= com_object
;
315 // Attempts to install Ready Mode prompts in the provided web browser. Will
316 // notify the provided Delegate if the user declines Chrome Frame temporarily or
318 bool InstallPrompts(linked_ptr
<ready_mode::Delegate
> delegate
,
319 IWebBrowser2
* web_browser
) {
320 base::win::ScopedComPtr
<ReadyModeWebBrowserAdapter
, NULL
> adapter
;
322 if (!CreateWebBrowserAdapter(adapter
.Receive()))
325 // Wrap the original delegate so that we can share it with the
326 // ReadyModeWebBrowserAdapter
327 scoped_ptr
<DelegateWrapper
> delegate_wrapper(new DelegateWrapper(delegate
));
329 // Pass ownership of our delegate to the BrowserObserver
330 scoped_ptr
<ReadyModeWebBrowserAdapter::Observer
> browser_observer(
331 new BrowserObserver(delegate_wrapper
.release(), web_browser
, adapter
));
333 // Owns the BrowserObserver
334 return adapter
->Initialize(web_browser
, browser_observer
.release());
337 // Checks if the provided status implies disabling Chrome Frame functionality.
338 bool ShouldDisableChromeFrame(ReadyModeStatus status
) {
340 case READY_MODE_PERMANENTLY_DECLINED
:
341 case READY_MODE_TEMPORARILY_DECLINED
:
342 case READY_MODE_TEMPORARY_DECLINE_EXPIRED
:
345 case READY_MODE_ACCEPTED
:
346 case READY_MODE_ACTIVE
:
357 namespace ready_mode
{
359 // Determines the current Ready Mode state. If it is active, attempts to set up
360 // prompting. If we cannot set up prompting, attempts to temporarily disable
361 // Ready Mode. In the end, if Ready Mode is disabled, pass that information on
362 // to the Delegate, so that it may disabled Chrome Frame functionality.
363 void Configure(Delegate
* chrome_frame
, IWebBrowser2
* web_browser
) {
364 // Take ownership of the delegate
365 linked_ptr
<Delegate
> delegate(chrome_frame
);
367 BrowserDistribution
* dist
=
368 BrowserDistribution::GetSpecificDistribution(
369 BrowserDistribution::CHROME_BINARIES
);
371 RegistryReadyModeState
ready_mode_state(
373 base::TimeDelta::FromMinutes(kTemporaryDeclineDurationMinutes
),
374 NULL
); // NULL => no observer required
376 ReadyModeStatus status
= ready_mode_state
.GetStatus();
378 // If the user temporarily declined Chrome Frame, but the timeout has elapsed,
379 // attempt to revert to active Ready Mode state.
380 if (status
== READY_MODE_TEMPORARY_DECLINE_EXPIRED
) {
381 ready_mode_state
.ExpireTemporaryDecline();
382 status
= ready_mode_state
.GetStatus();
385 // If Ready Mode is active, attempt to set up prompting.
386 if (status
== READY_MODE_ACTIVE
) {
387 if (!InstallPrompts(delegate
, web_browser
)) {
388 // Failed to set up prompting. Turn off Ready Mode for now.
389 ready_mode_state
.TemporarilyDeclineChromeFrame();
390 status
= ready_mode_state
.GetStatus();
394 // Depending on the state we finally end up in, tell our Delegate to disable
395 // Chrome Frame functionality.
396 if (ShouldDisableChromeFrame(status
))
397 delegate
->DisableChromeFrame();
400 } // namespace ready_mode