OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "chrome_frame/ready_mode/ready_mode.h" | |
6 | |
7 #include <atlbase.h> | |
8 #include <shlguid.h> | |
9 | |
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" | |
26 | |
27 namespace { | |
28 | |
29 // Temporarily disable Ready Mode for 36 hours when the user so indicates. | |
30 const int kTemporaryDeclineDurationMinutes = 60 * 36; | |
31 | |
32 class BrowserObserver; | |
33 | |
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(); | |
40 | |
41 // RegistryReadyModeState::Observer implementation | |
42 virtual void OnStateChange(ReadyModeStatus status); | |
43 | |
44 private: | |
45 base::WeakPtr<BrowserObserver> ready_mode_ui_; | |
46 | |
47 DISALLOW_COPY_AND_ASSIGN(StateObserver); | |
48 }; // class StateObserver | |
49 | |
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. | |
55 // | |
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); | |
66 | |
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); | |
71 | |
72 private: | |
73 friend class StateObserver; | |
74 | |
75 // Called by the StateObserver | |
76 void OnReadyModeDisabled(); | |
77 void OnReadyModeAccepted(); | |
78 | |
79 // Helpers for showing infobar prompts | |
80 void ShowPrompt(); | |
81 void Hide(); | |
82 InfobarManager* GetInfobarManager(); | |
83 | |
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_; | |
90 | |
91 DISALLOW_COPY_AND_ASSIGN(BrowserObserver); | |
92 }; // class BrowserObserver | |
93 | |
94 // Implements launching of a URL in an instance of IWebBrowser2. | |
95 class UrlLauncherImpl : public UrlLauncher { | |
96 public: | |
97 explicit UrlLauncherImpl(IWebBrowser2* web_browser); | |
98 | |
99 // UrlLauncher implementation | |
100 void LaunchUrl(const std::wstring& url); | |
101 | |
102 private: | |
103 base::win::ScopedComPtr<IWebBrowser2> web_browser_; | |
104 }; // class UrlLaucherImpl | |
105 | |
106 UrlLauncherImpl::UrlLauncherImpl(IWebBrowser2* web_browser) { | |
107 DCHECK(web_browser); | |
108 web_browser_ = web_browser; | |
109 } | |
110 | |
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()); | |
115 | |
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; | |
119 } | |
120 | |
121 StateObserver::StateObserver( | |
122 const base::WeakPtr<BrowserObserver>& ready_mode_ui) | |
123 : ready_mode_ui_(ready_mode_ui) { | |
124 } | |
125 | |
126 StateObserver::~StateObserver() { | |
127 } | |
128 | |
129 void StateObserver::OnStateChange(ReadyModeStatus status) { | |
130 if (ready_mode_ui_ == NULL) | |
131 return; | |
132 | |
133 switch (status) { | |
134 case READY_MODE_PERMANENTLY_DECLINED: | |
135 case READY_MODE_TEMPORARILY_DECLINED: | |
136 ready_mode_ui_->OnReadyModeDisabled(); | |
137 break; | |
138 | |
139 case READY_MODE_ACCEPTED: | |
140 ready_mode_ui_->OnReadyModeAccepted(); | |
141 break; | |
142 | |
143 case READY_MODE_ACTIVE: | |
144 break; | |
145 | |
146 default: | |
147 NOTREACHED(); | |
148 break; | |
149 } | |
150 } | |
151 | |
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) { | |
159 } | |
160 | |
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(); | |
168 } | |
169 } | |
170 | |
171 void BrowserObserver::OnRenderInChromeFrame(const std::wstring& url) { | |
172 ShowPrompt(); | |
173 rendered_url_ = GURL(url); | |
174 } | |
175 | |
176 void BrowserObserver::OnRenderInHost(const std::wstring& url) { | |
177 Hide(); | |
178 rendered_url_ = GURL(url); | |
179 } | |
180 | |
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_); | |
186 | |
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_); | |
190 | |
191 chrome_frame_->DisableChromeFrame(); | |
192 adapter_->Uninitialize(); | |
193 | |
194 VARIANT flags = { VT_I4 }; | |
195 V_I4(&flags) = navNoHistory; | |
196 base::win::ScopedBstr location; | |
197 | |
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; | |
201 | |
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; | |
206 } | |
207 } | |
208 | |
209 void BrowserObserver::OnReadyModeAccepted() { | |
210 // See comment in OnReadyModeDisabled. | |
211 base::win::ScopedComPtr<ReadyModeWebBrowserAdapter, NULL> reference(adapter_); | |
212 adapter_->Uninitialize(); | |
213 } | |
214 | |
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(); | |
219 | |
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())); | |
224 | |
225 BrowserDistribution* dist = | |
226 BrowserDistribution::GetSpecificDistribution( | |
227 BrowserDistribution::CHROME_BINARIES); | |
228 | |
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())); | |
234 | |
235 // Owned by infobar_content | |
236 scoped_ptr<UrlLauncher> url_launcher(new UrlLauncherImpl(web_browser_)); | |
237 | |
238 // Owned by infobar_manager | |
239 scoped_ptr<InfobarContent> infobar_content(new ReadyPromptContent( | |
240 ready_mode_state.release(), url_launcher.release())); | |
241 | |
242 infobar_manager->Show(infobar_content.release(), TOP_INFOBAR); | |
243 } | |
244 } | |
245 | |
246 void BrowserObserver::Hide() { | |
247 InfobarManager* infobar_manager = GetInfobarManager(); | |
248 if (infobar_manager) | |
249 infobar_manager->HideAll(); | |
250 } | |
251 | |
252 InfobarManager* BrowserObserver::GetInfobarManager() { | |
253 HRESULT hr = NOERROR; | |
254 | |
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; | |
261 } | |
262 | |
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; | |
269 } | |
270 | |
271 return InfobarManager::Get(web_browserhwnd); | |
272 } | |
273 | |
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); | |
278 | |
279 // ready_mode::Delegate implementation | |
280 virtual void DisableChromeFrame(); | |
281 | |
282 private: | |
283 linked_ptr<ready_mode::Delegate> wrapped_; | |
284 | |
285 DISALLOW_COPY_AND_ASSIGN(DelegateWrapper); | |
286 }; // class DelegateWrapper | |
287 | |
288 DelegateWrapper::DelegateWrapper(linked_ptr<ready_mode::Delegate> wrapped) | |
289 : wrapped_(wrapped) { | |
290 } | |
291 | |
292 void DelegateWrapper::DisableChromeFrame() { | |
293 wrapped_->DisableChromeFrame(); | |
294 } | |
295 | |
296 // Attempts to create a ReadyModeWebBrowserAdapter instance. | |
297 bool CreateWebBrowserAdapter(ReadyModeWebBrowserAdapter** pointer) { | |
298 *pointer = NULL; | |
299 | |
300 CComObject<ReadyModeWebBrowserAdapter>* com_object; | |
301 HRESULT hr = | |
302 CComObject<ReadyModeWebBrowserAdapter>::CreateInstance(&com_object); | |
303 | |
304 if (FAILED(hr)) { | |
305 DLOG(ERROR) << "Failed to create instance of ReadyModeWebBrowserAdapter. " | |
306 << "Error: " << hr; | |
307 return false; | |
308 } | |
309 | |
310 com_object->AddRef(); | |
311 *pointer = com_object; | |
312 return true; | |
313 } | |
314 | |
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; | |
321 | |
322 if (!CreateWebBrowserAdapter(adapter.Receive())) | |
323 return false; | |
324 | |
325 // Wrap the original delegate so that we can share it with the | |
326 // ReadyModeWebBrowserAdapter | |
327 scoped_ptr<DelegateWrapper> delegate_wrapper(new DelegateWrapper(delegate)); | |
328 | |
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)); | |
332 | |
333 // Owns the BrowserObserver | |
334 return adapter->Initialize(web_browser, browser_observer.release()); | |
335 } | |
336 | |
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; | |
344 | |
345 case READY_MODE_ACCEPTED: | |
346 case READY_MODE_ACTIVE: | |
347 return false; | |
348 | |
349 default: | |
350 NOTREACHED(); | |
351 return true; | |
352 } | |
353 } | |
354 | |
355 } // namespace | |
356 | |
357 namespace ready_mode { | |
358 | |
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); | |
370 | |
371 RegistryReadyModeState ready_mode_state( | |
372 dist->GetStateKey(), | |
373 base::TimeDelta::FromMinutes(kTemporaryDeclineDurationMinutes), | |
374 NULL); // NULL => no observer required | |
375 | |
376 ReadyModeStatus status = ready_mode_state.GetStatus(); | |
377 | |
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(); | |
383 } | |
384 | |
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(); | |
391 } | |
392 } | |
393 | |
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(); | |
398 } | |
399 | |
400 } // namespace ready_mode | |
OLD | NEW |