| 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 |