OLD | NEW |
(Empty) | |
| 1 // Copyright 2013 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/turndown_prompt/turndown_prompt.h" |
| 6 |
| 7 #include <atlbase.h> |
| 8 #include <shlguid.h> |
| 9 |
| 10 #include "base/bind.h" |
| 11 #include "base/compiler_specific.h" |
| 12 #include "base/logging.h" |
| 13 #include "base/memory/scoped_ptr.h" |
| 14 #include "base/path_service.h" |
| 15 #include "base/process_util.h" |
| 16 #include "base/win/scoped_bstr.h" |
| 17 #include "base/win/scoped_comptr.h" |
| 18 #include "base/win/win_util.h" |
| 19 #include "chrome/installer/util/browser_distribution.h" |
| 20 #include "chrome/installer/util/google_update_settings.h" |
| 21 #include "chrome/installer/util/install_util.h" |
| 22 #include "chrome/installer/util/installation_state.h" |
| 23 #include "chrome/installer/util/util_constants.h" |
| 24 #include "chrome_frame/infobars/infobar_manager.h" |
| 25 #include "chrome_frame/policy_settings.h" |
| 26 #include "chrome_frame/ready_mode/internal/ready_mode_web_browser_adapter.h" |
| 27 #include "chrome_frame/ready_mode/internal/url_launcher.h" |
| 28 #include "chrome_frame/simple_resource_loader.h" |
| 29 #include "chrome_frame/turndown_prompt/reshow_state.h" |
| 30 #include "chrome_frame/turndown_prompt/turndown_prompt_content.h" |
| 31 #include "chrome_frame/utils.h" |
| 32 #include "grit/chromium_strings.h" |
| 33 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" |
| 34 |
| 35 namespace { |
| 36 |
| 37 // Time between showings of the turndown prompt. |
| 38 const int kTurndownPromptReshowDeltaMinutes = 60 * 24 * 7; |
| 39 |
| 40 void OnUninstallClicked(UrlLauncher* url_launcher); |
| 41 |
| 42 // Manages the Turndown UI in response to browsing ChromeFrame-rendered |
| 43 // pages. |
| 44 class BrowserObserver : public ReadyModeWebBrowserAdapter::Observer { |
| 45 public: |
| 46 BrowserObserver(IWebBrowser2* web_browser, |
| 47 ReadyModeWebBrowserAdapter* adapter); |
| 48 |
| 49 // ReadyModeWebBrowserAdapter::Observer implementation |
| 50 virtual void OnNavigateTo(const string16& url); |
| 51 virtual void OnRenderInChromeFrame(const string16& url); |
| 52 virtual void OnRenderInHost(const string16& url); |
| 53 |
| 54 private: |
| 55 // Shows the turndown prompt if it hasn't been seen since |
| 56 // kTurndownPromptReshowDeltaMinutes. |
| 57 void ShowPrompt(); |
| 58 void Hide(); |
| 59 // Returns a self-managed pointer that is not guaranteed to survive handling |
| 60 // of Windows events. For safety's sake, retrieve this pointer for each use |
| 61 // and do not store it for use outside of scope. |
| 62 InfobarManager* GetInfobarManager(); |
| 63 |
| 64 GURL rendered_url_; |
| 65 base::win::ScopedComPtr<IWebBrowser2> web_browser_; |
| 66 ReadyModeWebBrowserAdapter* adapter_; |
| 67 |
| 68 DISALLOW_COPY_AND_ASSIGN(BrowserObserver); |
| 69 }; |
| 70 |
| 71 // Implements launching of a URL in an instance of IWebBrowser2. |
| 72 class UrlLauncherImpl : public UrlLauncher { |
| 73 public: |
| 74 explicit UrlLauncherImpl(IWebBrowser2* web_browser); |
| 75 |
| 76 // UrlLauncher implementation |
| 77 void LaunchUrl(const string16& url); |
| 78 |
| 79 private: |
| 80 base::win::ScopedComPtr<IWebBrowser2> web_browser_; |
| 81 }; |
| 82 |
| 83 UrlLauncherImpl::UrlLauncherImpl(IWebBrowser2* web_browser) { |
| 84 DCHECK(web_browser); |
| 85 web_browser_ = web_browser; |
| 86 } |
| 87 |
| 88 void UrlLauncherImpl::LaunchUrl(const string16& url) { |
| 89 VARIANT flags = { VT_I4 }; |
| 90 V_I4(&flags) = navOpenInNewWindow; |
| 91 base::win::ScopedBstr location(url.c_str()); |
| 92 |
| 93 HRESULT hr = web_browser_->Navigate(location, &flags, NULL, NULL, NULL); |
| 94 DLOG_IF(ERROR, FAILED(hr)) << "Failed to invoke Navigate on IWebBrowser2. " |
| 95 << "Error: " << hr; |
| 96 } |
| 97 |
| 98 BrowserObserver::BrowserObserver(IWebBrowser2* web_browser, |
| 99 ReadyModeWebBrowserAdapter* adapter) |
| 100 : web_browser_(web_browser), |
| 101 adapter_(adapter) { |
| 102 } |
| 103 |
| 104 void BrowserObserver::OnNavigateTo(const string16& url) { |
| 105 if (!net::registry_controlled_domains::SameDomainOrHost( |
| 106 GURL(url), |
| 107 rendered_url_, |
| 108 net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES)) { |
| 109 rendered_url_ = GURL(); |
| 110 Hide(); |
| 111 } |
| 112 } |
| 113 |
| 114 void BrowserObserver::OnRenderInChromeFrame(const string16& url) { |
| 115 ShowPrompt(); |
| 116 rendered_url_ = GURL(url); |
| 117 } |
| 118 |
| 119 void BrowserObserver::OnRenderInHost(const string16& url) { |
| 120 Hide(); |
| 121 rendered_url_ = GURL(url); |
| 122 } |
| 123 |
| 124 void BrowserObserver::ShowPrompt() { |
| 125 turndown_prompt::ReshowState reshow_state( |
| 126 base::TimeDelta::FromMinutes(kTurndownPromptReshowDeltaMinutes)); |
| 127 |
| 128 // Short-circuit if the prompt shouldn't be shown again yet. |
| 129 if (!reshow_state.HasReshowDeltaExpired(base::Time::Now())) |
| 130 return; |
| 131 |
| 132 // This pointer is self-managed and not guaranteed to survive handling of |
| 133 // Windows events. For safety's sake, retrieve this pointer for each use and |
| 134 // do not store it for use outside of scope. |
| 135 InfobarManager* infobar_manager = GetInfobarManager(); |
| 136 |
| 137 if (infobar_manager) { |
| 138 // Owned by infobar_content |
| 139 scoped_ptr<UrlLauncher> url_launcher(new UrlLauncherImpl(web_browser_)); |
| 140 |
| 141 // Owned by infobar_manager |
| 142 scoped_ptr<InfobarContent> infobar_content(new TurndownPromptContent( |
| 143 url_launcher.release(), |
| 144 base::Bind(&OnUninstallClicked, |
| 145 base::Owned(new UrlLauncherImpl(web_browser_))))); |
| 146 |
| 147 if (infobar_manager->Show(infobar_content.release(), TOP_INFOBAR)) { |
| 148 // Update state in the registry that the prompt was shown. |
| 149 reshow_state.MarkShown(base::Time::Now()); |
| 150 } |
| 151 } |
| 152 } |
| 153 |
| 154 void BrowserObserver::Hide() { |
| 155 InfobarManager* infobar_manager = GetInfobarManager(); |
| 156 if (infobar_manager) |
| 157 infobar_manager->HideAll(); |
| 158 } |
| 159 |
| 160 InfobarManager* BrowserObserver::GetInfobarManager() { |
| 161 HRESULT hr = NOERROR; |
| 162 |
| 163 base::win::ScopedComPtr<IOleWindow> ole_window; |
| 164 hr = DoQueryService(SID_SShellBrowser, web_browser_, ole_window.Receive()); |
| 165 if (FAILED(hr) || ole_window == NULL) { |
| 166 DLOG(ERROR) << "Failed to query SID_SShellBrowser from IWebBrowser2. " |
| 167 << "Error: " << hr; |
| 168 return NULL; |
| 169 } |
| 170 |
| 171 HWND web_browser_hwnd = NULL; |
| 172 hr = ole_window->GetWindow(&web_browser_hwnd); |
| 173 if (FAILED(hr) || web_browser_hwnd == NULL) { |
| 174 DLOG(ERROR) << "Failed to query HWND from IOleWindow. " |
| 175 << "Error: " << hr; |
| 176 return NULL; |
| 177 } |
| 178 |
| 179 return InfobarManager::Get(web_browser_hwnd); |
| 180 } |
| 181 |
| 182 // Returns true if the module into which this code is linked is installed at |
| 183 // system-level. |
| 184 bool IsCurrentModuleSystemLevel() { |
| 185 base::FilePath dll_path; |
| 186 if (PathService::Get(base::DIR_MODULE, &dll_path)) |
| 187 return !InstallUtil::IsPerUserInstall(dll_path.value().c_str()); |
| 188 return false; |
| 189 } |
| 190 |
| 191 // Attempts to create a ReadyModeWebBrowserAdapter instance. |
| 192 bool CreateWebBrowserAdapter(ReadyModeWebBrowserAdapter** adapter) { |
| 193 *adapter = NULL; |
| 194 |
| 195 CComObject<ReadyModeWebBrowserAdapter>* com_object; |
| 196 HRESULT hr = |
| 197 CComObject<ReadyModeWebBrowserAdapter>::CreateInstance(&com_object); |
| 198 |
| 199 if (FAILED(hr)) { |
| 200 DLOG(ERROR) << "Failed to create instance of ReadyModeWebBrowserAdapter. " |
| 201 << "Error: " << hr; |
| 202 return false; |
| 203 } |
| 204 |
| 205 com_object->AddRef(); |
| 206 *adapter = com_object; |
| 207 return true; |
| 208 } |
| 209 |
| 210 // Attempts to install Turndown prompts in the provided web browser. |
| 211 bool InstallPrompts(IWebBrowser2* web_browser) { |
| 212 base::win::ScopedComPtr<ReadyModeWebBrowserAdapter, NULL> adapter; |
| 213 |
| 214 if (!CreateWebBrowserAdapter(adapter.Receive())) |
| 215 return false; |
| 216 |
| 217 // Pass ownership of our delegate to the BrowserObserver |
| 218 scoped_ptr<ReadyModeWebBrowserAdapter::Observer> browser_observer( |
| 219 new BrowserObserver(web_browser, adapter)); |
| 220 |
| 221 // Owns the BrowserObserver |
| 222 return adapter->Initialize(web_browser, browser_observer.release()); |
| 223 } |
| 224 |
| 225 // Launches the Chrome Frame uninstaller in response to user action. This |
| 226 // implementation should not be used to uninstall MSI-based versions of GCF, |
| 227 // since those must be removed by way of Windows Installer machinery. |
| 228 void LaunchChromeFrameUninstaller() { |
| 229 BrowserDistribution* dist = |
| 230 BrowserDistribution::GetSpecificDistribution( |
| 231 BrowserDistribution::CHROME_FRAME); |
| 232 const bool system_level = IsCurrentModuleSystemLevel(); |
| 233 installer::ProductState product_state; |
| 234 if (!product_state.Initialize(system_level, dist)) { |
| 235 DLOG(ERROR) << "Chrome frame isn't installed at " |
| 236 << (system_level ? "system" : "user") << " level."; |
| 237 return; |
| 238 } |
| 239 |
| 240 CommandLine uninstall_command(product_state.uninstall_command()); |
| 241 if (uninstall_command.GetProgram().empty()) { |
| 242 DLOG(ERROR) << "No uninstall command found in registry."; |
| 243 return; |
| 244 } |
| 245 |
| 246 // Force Uninstall silences the prompt to reboot to complete uninstall. |
| 247 uninstall_command.AppendSwitch(installer::switches::kForceUninstall); |
| 248 VLOG(1) << "Uninstalling Chrome Frame with command: " |
| 249 << uninstall_command.GetCommandLineString(); |
| 250 base::LaunchProcess(uninstall_command, base::LaunchOptions(), NULL); |
| 251 } |
| 252 |
| 253 void LaunchLearnMoreURL(UrlLauncher* url_launcher) { |
| 254 url_launcher->LaunchUrl(SimpleResourceLoader::Get( |
| 255 IDS_CHROME_FRAME_TURNDOWN_LEARN_MORE_URL)); |
| 256 } |
| 257 |
| 258 void OnUninstallClicked(UrlLauncher* url_launcher) { |
| 259 LaunchChromeFrameUninstaller(); |
| 260 LaunchLearnMoreURL(url_launcher); |
| 261 } |
| 262 |
| 263 } // namespace |
| 264 |
| 265 namespace turndown_prompt { |
| 266 |
| 267 bool IsPromptSuppressed() { |
| 268 // See if this is an MSI install of GCF or if updates have been disabled. |
| 269 BrowserDistribution* dist = |
| 270 BrowserDistribution::GetSpecificDistribution( |
| 271 BrowserDistribution::CHROME_FRAME); |
| 272 |
| 273 const bool system_level = IsCurrentModuleSystemLevel(); |
| 274 bool multi_install = false; |
| 275 |
| 276 installer::ProductState product_state; |
| 277 if (product_state.Initialize(system_level, dist)) { |
| 278 if (product_state.is_msi()) |
| 279 return true; |
| 280 multi_install = product_state.is_multi_install(); |
| 281 } |
| 282 |
| 283 if (multi_install) { |
| 284 dist = |
| 285 BrowserDistribution::GetSpecificDistribution( |
| 286 BrowserDistribution::CHROME_BINARIES); |
| 287 } |
| 288 if (GoogleUpdateSettings::GetAppUpdatePolicy(dist->GetAppGuid(), NULL) == |
| 289 GoogleUpdateSettings::UPDATES_DISABLED) { |
| 290 return true; |
| 291 } |
| 292 |
| 293 // See if the prompt is explicitly suppressed via GP. |
| 294 return PolicySettings::GetInstance()->suppress_turndown_prompt(); |
| 295 } |
| 296 |
| 297 void Configure(IWebBrowser2* web_browser) { |
| 298 if (!IsPromptSuppressed()) |
| 299 InstallPrompts(web_browser); |
| 300 } |
| 301 |
| 302 } // namespace turndown_prompt |
OLD | NEW |