Report errors from ChromiumEnv::GetChildren in Posix.
[chromium-blink-merge.git] / chrome_frame / ready_mode / ready_mode.cc
blob12853292dc960e6c21c9739def278970915eb782
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"
7 #include <atlbase.h>
8 #include <shlguid.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"
27 namespace {
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
35 // prompt.
36 class StateObserver : public RegistryReadyModeState::Observer {
37 public:
38 explicit StateObserver(const base::WeakPtr<BrowserObserver>& ready_mode_ui);
39 ~StateObserver();
41 // RegistryReadyModeState::Observer implementation
42 virtual void OnStateChange(ReadyModeStatus status);
44 private:
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
54 // GCF enabled.
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
60 // renderer.
61 class BrowserObserver : public ReadyModeWebBrowserAdapter::Observer {
62 public:
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);
72 private:
73 friend class StateObserver;
75 // Called by the StateObserver
76 void OnReadyModeDisabled();
77 void OnReadyModeAccepted();
79 // Helpers for showing infobar prompts
80 void ShowPrompt();
81 void Hide();
82 InfobarManager* GetInfobarManager();
84 GURL rendered_url_;
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 {
96 public:
97 explicit UrlLauncherImpl(IWebBrowser2* web_browser);
99 // UrlLauncher implementation
100 void LaunchUrl(const std::wstring& url);
102 private:
103 base::win::ScopedComPtr<IWebBrowser2> web_browser_;
104 }; // class UrlLaucherImpl
106 UrlLauncherImpl::UrlLauncherImpl(IWebBrowser2* web_browser) {
107 DCHECK(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. "
118 << "Error: " << hr;
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)
131 return;
133 switch (status) {
134 case READY_MODE_PERMANENTLY_DECLINED:
135 case READY_MODE_TEMPORARILY_DECLINED:
136 ready_mode_ui_->OnReadyModeDisabled();
137 break;
139 case READY_MODE_ACCEPTED:
140 ready_mode_ui_->OnReadyModeAccepted();
141 break;
143 case READY_MODE_ACTIVE:
144 break;
146 default:
147 NOTREACHED();
148 break;
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),
157 adapter_(adapter),
158 weak_ptr_factory_(this) {
161 void BrowserObserver::OnNavigateTo(const std::wstring& url) {
162 if (!net::registry_controlled_domains::SameDomainOrHost(
163 GURL(url),
164 rendered_url_,
165 net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES)) {
166 rendered_url_ = GURL();
167 Hide();
171 void BrowserObserver::OnRenderInChromeFrame(const std::wstring& url) {
172 ShowPrompt();
173 rendered_url_ = GURL(url);
176 void BrowserObserver::OnRenderInHost(const std::wstring& url) {
177 Hide();
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
184 // invocation.
185 base::win::ScopedComPtr<ReadyModeWebBrowserAdapter, NULL> reference(adapter_);
187 // adapter_->Uninitialize may delete us, so we should not refer to members
188 // after that point.
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;
202 if (SUCCEEDED(hr)) {
203 hr = web_browser->Navigate(location, &flags, NULL, NULL, NULL);
204 DLOG_IF(ERROR, FAILED(hr)) << "Failed to invoke Navigate on IWebBrowser2. "
205 << "Error: " << hr;
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
217 // Windows events.
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(
231 dist->GetStateKey(),
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();
248 if (infobar_manager)
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. "
259 << "Error: " << hr;
260 return NULL;
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. "
267 << "Error: " << hr;
268 return NULL;
271 return InfobarManager::Get(web_browserhwnd);
274 // Wraps an existing Delegate so that ownership may be shared.
275 class DelegateWrapper : public ready_mode::Delegate {
276 public:
277 explicit DelegateWrapper(linked_ptr<ready_mode::Delegate> wrapped);
279 // ready_mode::Delegate implementation
280 virtual void DisableChromeFrame();
282 private:
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) {
298 *pointer = NULL;
300 CComObject<ReadyModeWebBrowserAdapter>* com_object;
301 HRESULT hr =
302 CComObject<ReadyModeWebBrowserAdapter>::CreateInstance(&com_object);
304 if (FAILED(hr)) {
305 DLOG(ERROR) << "Failed to create instance of ReadyModeWebBrowserAdapter. "
306 << "Error: " << hr;
307 return false;
310 com_object->AddRef();
311 *pointer = com_object;
312 return true;
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
317 // permanently.
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()))
323 return false;
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) {
339 switch (status) {
340 case READY_MODE_PERMANENTLY_DECLINED:
341 case READY_MODE_TEMPORARILY_DECLINED:
342 case READY_MODE_TEMPORARY_DECLINE_EXPIRED:
343 return true;
345 case READY_MODE_ACCEPTED:
346 case READY_MODE_ACTIVE:
347 return false;
349 default:
350 NOTREACHED();
351 return true;
355 } // namespace
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);
366 chrome_frame = NULL;
367 BrowserDistribution* dist =
368 BrowserDistribution::GetSpecificDistribution(
369 BrowserDistribution::CHROME_BINARIES);
371 RegistryReadyModeState ready_mode_state(
372 dist->GetStateKey(),
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