Home | History | Annotate | Download | only in ready_mode
      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
    401