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 |