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