Chromium Code Reviews| 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 std::wstring& url); | |
| 51 virtual void OnRenderInChromeFrame(const std::wstring& url); | |
| 52 virtual void OnRenderInHost(const std::wstring& 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 std::wstring& 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 std::wstring& 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 std::wstring& 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 std::wstring& url) { | |
| 115 ShowPrompt(); | |
| 116 rendered_url_ = GURL(url); | |
| 117 } | |
| 118 | |
| 119 void BrowserObserver::OnRenderInHost(const std::wstring& url) { | |
| 120 Hide(); | |
| 121 rendered_url_ = GURL(url); | |
| 122 } | |
| 123 | |
| 124 void BrowserObserver::ShowPrompt() { | |
| 125 BrowserDistribution* dist = | |
| 126 BrowserDistribution::GetSpecificDistribution( | |
| 127 BrowserDistribution::CHROME_FRAME); | |
| 128 turndown_prompt::ReshowState reshow_state( | |
| 129 dist->GetStateKey(), | |
| 130 base::TimeDelta::FromMinutes(kTurndownPromptReshowDeltaMinutes)); | |
| 131 | |
| 132 // Short-circuit if the prompt shouldn't be shown again yet. | |
| 133 if (!reshow_state.HasReshowDeltaExpired(base::Time::Now())) | |
| 134 return; | |
| 135 | |
| 136 // This pointer is self-managed and not guaranteed to survive handling of | |
| 137 // Windows events. For safety's sake, retrieve this pointer for each use and | |
| 138 // do not store it for use outside of scope. | |
| 139 InfobarManager* infobar_manager = GetInfobarManager(); | |
| 140 | |
| 141 if (infobar_manager) { | |
| 142 // Owned by infobar_content | |
| 143 scoped_ptr<UrlLauncher> url_launcher(new UrlLauncherImpl(web_browser_)); | |
| 144 | |
| 145 // Owned by infobar_manager | |
| 146 scoped_ptr<InfobarContent> infobar_content(new TurndownPromptContent( | |
| 147 url_launcher.release(), | |
| 148 base::Bind(&OnUninstallClicked, | |
| 149 base::Owned(new UrlLauncherImpl(web_browser_))))); | |
| 150 | |
| 151 if (infobar_manager->Show(infobar_content.release(), TOP_INFOBAR)) { | |
| 152 // Update state in the registry that the prompt was shown. | |
| 153 reshow_state.MarkShown(base::Time::Now()); | |
| 154 } | |
| 155 } | |
| 156 } | |
| 157 | |
| 158 void BrowserObserver::Hide() { | |
| 159 InfobarManager* infobar_manager = GetInfobarManager(); | |
| 160 if (infobar_manager) | |
| 161 infobar_manager->HideAll(); | |
| 162 } | |
| 163 | |
| 164 InfobarManager* BrowserObserver::GetInfobarManager() { | |
| 165 HRESULT hr = NOERROR; | |
| 166 | |
| 167 base::win::ScopedComPtr<IOleWindow> ole_window; | |
| 168 hr = DoQueryService(SID_SShellBrowser, web_browser_, ole_window.Receive()); | |
| 169 if (FAILED(hr) || ole_window == NULL) { | |
| 170 DLOG(ERROR) << "Failed to query SID_SShellBrowser from IWebBrowser2. " | |
| 171 << "Error: " << hr; | |
| 172 return NULL; | |
| 173 } | |
| 174 | |
| 175 HWND web_browser_hwnd = NULL; | |
| 176 hr = ole_window->GetWindow(&web_browser_hwnd); | |
| 177 if (FAILED(hr) || web_browser_hwnd == NULL) { | |
| 178 DLOG(ERROR) << "Failed to query HWND from IOleWindow. " | |
| 179 << "Error: " << hr; | |
| 180 return NULL; | |
| 181 } | |
| 182 | |
| 183 return InfobarManager::Get(web_browser_hwnd); | |
| 184 } | |
| 185 | |
| 186 // Returns true if the module into which this code is linked is installed at | |
| 187 // system-level. | |
| 188 bool IsCurrentModuleSystemLevel() { | |
| 189 base::FilePath dll_path; | |
| 190 if (PathService::Get(base::DIR_MODULE, &dll_path)) | |
| 191 return !InstallUtil::IsPerUserInstall(dll_path.value().c_str()); | |
| 192 return false; | |
| 193 } | |
| 194 | |
| 195 // Attempts to create a ReadyModeWebBrowserAdapter instance. | |
| 196 bool CreateWebBrowserAdapter(ReadyModeWebBrowserAdapter** adapter) { | |
| 197 *adapter = NULL; | |
| 198 | |
| 199 CComObject<ReadyModeWebBrowserAdapter>* com_object; | |
| 200 HRESULT hr = | |
| 201 CComObject<ReadyModeWebBrowserAdapter>::CreateInstance(&com_object); | |
| 202 | |
| 203 if (FAILED(hr)) { | |
| 204 DLOG(ERROR) << "Failed to create instance of ReadyModeWebBrowserAdapter. " | |
| 205 << "Error: " << hr; | |
| 206 return false; | |
| 207 } | |
| 208 | |
| 209 com_object->AddRef(); | |
| 210 *adapter = com_object; | |
| 211 return true; | |
| 212 } | |
| 213 | |
| 214 // Attempts to install Turnown prompts in the provided web browser. | |
|
robertshield
2013/06/21 20:06:45
Turndown
grt (UTC plus 2)
2013/06/21 20:10:40
Done.
| |
| 215 bool InstallPrompts(IWebBrowser2* web_browser) { | |
| 216 base::win::ScopedComPtr<ReadyModeWebBrowserAdapter, NULL> adapter; | |
| 217 | |
| 218 if (!CreateWebBrowserAdapter(adapter.Receive())) | |
| 219 return false; | |
| 220 | |
| 221 // Pass ownership of our delegate to the BrowserObserver | |
| 222 scoped_ptr<ReadyModeWebBrowserAdapter::Observer> browser_observer( | |
| 223 new BrowserObserver(web_browser, adapter)); | |
| 224 | |
| 225 // Owns the BrowserObserver | |
| 226 return adapter->Initialize(web_browser, browser_observer.release()); | |
| 227 } | |
| 228 | |
| 229 // Launches the Chrome Frame uninstaller in response to user action. This | |
| 230 // implementation should not be used to uninstall MSI-based versions of GCF, | |
| 231 // since those must be removed by way of Windows Installer machinery. | |
| 232 void LaunchChromeFrameUninstaller() { | |
| 233 BrowserDistribution* dist = | |
| 234 BrowserDistribution::GetSpecificDistribution( | |
| 235 BrowserDistribution::CHROME_FRAME); | |
| 236 const bool system_level = IsCurrentModuleSystemLevel(); | |
| 237 installer::ProductState product_state; | |
| 238 if (!product_state.Initialize(system_level, dist)) { | |
| 239 DLOG(ERROR) << "Chrome frame isn't installed at " | |
| 240 << (system_level ? "system" : "user") << " level."; | |
| 241 return; | |
| 242 } | |
| 243 | |
| 244 CommandLine uninstall_command(product_state.uninstall_command()); | |
| 245 if (uninstall_command.GetProgram().empty()) { | |
| 246 DLOG(ERROR) << "No uninstall command found in registry."; | |
| 247 return; | |
| 248 } | |
| 249 | |
| 250 // Force Uninstall silences the prompt to reboot to complete uninstall. | |
| 251 uninstall_command.AppendSwitch(installer::switches::kForceUninstall); | |
| 252 VLOG(1) << "Uninstalling Chrome Frame with command: " | |
| 253 << uninstall_command.GetCommandLineString(); | |
| 254 base::LaunchProcess(uninstall_command, base::LaunchOptions(), NULL); | |
| 255 } | |
| 256 | |
| 257 void LaunchLearnMoreURL(UrlLauncher* url_launcher) { | |
| 258 url_launcher->LaunchUrl(SimpleResourceLoader::Get( | |
| 259 IDS_CHROME_FRAME_TURNDOWN_LEARN_MORE_URL)); | |
| 260 } | |
| 261 | |
| 262 void OnUninstallClicked(UrlLauncher* url_launcher) { | |
| 263 LaunchChromeFrameUninstaller(); | |
| 264 LaunchLearnMoreURL(url_launcher); | |
| 265 } | |
| 266 | |
| 267 } // namespace | |
| 268 | |
| 269 namespace turndown_prompt { | |
| 270 | |
| 271 bool IsPromptSuppressed() { | |
| 272 // See if this is an MSI install of GCF or if updates have been disabled. | |
| 273 BrowserDistribution* dist = | |
| 274 BrowserDistribution::GetSpecificDistribution( | |
| 275 BrowserDistribution::CHROME_FRAME); | |
| 276 | |
| 277 const bool system_level = IsCurrentModuleSystemLevel(); | |
| 278 bool multi_install = false; | |
| 279 | |
| 280 installer::ProductState product_state; | |
| 281 if (product_state.Initialize(system_level, dist)) { | |
| 282 if (product_state.is_msi()) | |
| 283 return true; | |
| 284 multi_install = product_state.is_multi_install(); | |
| 285 } | |
| 286 | |
| 287 if (multi_install) { | |
| 288 dist = | |
| 289 BrowserDistribution::GetSpecificDistribution( | |
| 290 BrowserDistribution::CHROME_BINARIES); | |
| 291 } | |
| 292 if (GoogleUpdateSettings::GetAppUpdatePolicy(dist->GetAppGuid(), NULL) == | |
| 293 GoogleUpdateSettings::UPDATES_DISABLED) { | |
| 294 return true; | |
| 295 } | |
| 296 | |
| 297 // See if the prompt is explicitly suppressed via GP. | |
| 298 return PolicySettings::GetInstance()->suppress_turndown_prompt(); | |
| 299 } | |
| 300 | |
| 301 void Configure(IWebBrowser2* web_browser) { | |
| 302 if (!IsPromptSuppressed()) | |
| 303 InstallPrompts(web_browser); | |
| 304 } | |
| 305 | |
| 306 } // namespace turndown_prompt | |
| OLD | NEW |