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/browser/plugins/plugin_observer.h"
6
7 #include <utility>
8
9 #include "base/auto_reset.h"
10 #include "base/bind.h"
11 #include "base/debug/crash_logging.h"
12 #include "base/metrics/histogram_macros.h"
13 #include "build/build_config.h"
14 #include "chrome/app/vector_icons/vector_icons.h"
15 #include "chrome/browser/browser_process.h"
16 #include "chrome/browser/infobars/infobar_service.h"
17 #include "chrome/browser/lifetime/application_lifetime.h"
18 #include "chrome/browser/plugins/plugin_finder.h"
19 #include "chrome/browser/plugins/plugin_infobar_delegates.h"
20 #include "chrome/browser/plugins/plugin_installer.h"
21 #include "chrome/browser/plugins/plugin_installer_observer.h"
22 #include "chrome/browser/plugins/reload_plugin_infobar_delegate.h"
23 #include "chrome/browser/profiles/profile.h"
24 #include "chrome/browser/ui/tab_modal_confirm_dialog.h"
25 #include "chrome/browser/ui/tab_modal_confirm_dialog_delegate.h"
26 #include "chrome/common/buildflags.h"
27 #include "chrome/grit/generated_resources.h"
28 #include "components/component_updater/component_updater_service.h"
29 #include "components/content_settings/core/browser/host_content_settings_map.h"
30 #include "components/download/public/common/download_url_parameters.h"
31 #include "components/infobars/core/simple_alert_infobar_delegate.h"
32 #include "components/metrics_services_manager/metrics_services_manager.h"
33 #include "content/public/browser/browser_thread.h"
34 #include "content/public/browser/child_process_security_policy.h"
35 #include "content/public/browser/download_manager.h"
36 #include "content/public/browser/plugin_service.h"
37 #include "content/public/browser/render_frame_host.h"
38 #include "content/public/browser/render_process_host.h"
39 #include "content/public/browser/render_view_host.h"
40 #include "content/public/browser/web_contents.h"
41 #include "content/public/browser/web_contents_delegate.h"
42 #include "content/public/common/webplugininfo.h"
43 #include "extensions/browser/guest_view/web_view/web_view_guest.h"
44 #include "mojo/public/cpp/bindings/remote.h"
45 #include "net/traffic_annotation/network_traffic_annotation.h"
46 #include "ppapi/buildflags/buildflags.h"
47 #include "services/service_manager/public/cpp/interface_provider.h"
48 #include "ui/base/l10n/l10n_util.h"
49
50 using content::PluginService;
51
52 // PluginObserver -------------------------------------------------------------
53
54 class PluginObserver::PluginPlaceholderHost : public PluginInstallerObserver {
55 public:
PluginPlaceholderHost(PluginObserver * observer,base::string16 plugin_name,PluginInstaller * installer,mojo::PendingRemote<chrome::mojom::PluginRenderer> plugin_renderer_remote)56 PluginPlaceholderHost(
57 PluginObserver* observer,
58 base::string16 plugin_name,
59 PluginInstaller* installer,
60 mojo::PendingRemote<chrome::mojom::PluginRenderer> plugin_renderer_remote)
61 : PluginInstallerObserver(installer),
62 observer_(observer),
63 plugin_renderer_remote_(std::move(plugin_renderer_remote)) {
64 plugin_renderer_remote_.set_disconnect_handler(
65 base::BindOnce(&PluginObserver::RemovePluginPlaceholderHost,
66 base::Unretained(observer_), this));
67 DCHECK(installer);
68 }
69
DownloadFinished()70 void DownloadFinished() override {
71 plugin_renderer_remote_->FinishedDownloading();
72 }
73
74 private:
75 PluginObserver* observer_;
76 mojo::Remote<chrome::mojom::PluginRenderer> plugin_renderer_remote_;
77 };
78
79 class PluginObserver::ComponentObserver
80 : public update_client::UpdateClient::Observer {
81 public:
82 using Events = update_client::UpdateClient::Observer::Events;
ComponentObserver(PluginObserver * observer,const std::string & component_id,mojo::PendingRemote<chrome::mojom::PluginRenderer> plugin_renderer_remote)83 ComponentObserver(
84 PluginObserver* observer,
85 const std::string& component_id,
86 mojo::PendingRemote<chrome::mojom::PluginRenderer> plugin_renderer_remote)
87 : observer_(observer),
88 component_id_(component_id),
89 plugin_renderer_remote_(std::move(plugin_renderer_remote)) {
90 plugin_renderer_remote_.set_disconnect_handler(
91 base::BindOnce(&PluginObserver::RemoveComponentObserver,
92 base::Unretained(observer_), this));
93 g_browser_process->component_updater()->AddObserver(this);
94 }
95
~ComponentObserver()96 ~ComponentObserver() override {
97 g_browser_process->component_updater()->RemoveObserver(this);
98 }
99
OnEvent(Events event,const std::string & id)100 void OnEvent(Events event, const std::string& id) override {
101 // TODO(lukasza): https://crbug.com/760637: |routing_id_| might live in a
102 // different process than the RenderViewHost - need to track and use
103 // placeholder's process when calling Send below.
104
105 if (id != component_id_)
106 return;
107 switch (event) {
108 case Events::COMPONENT_UPDATED:
109 plugin_renderer_remote_->UpdateSuccess();
110 observer_->RemoveComponentObserver(this);
111 break;
112 case Events::COMPONENT_UPDATE_FOUND:
113 plugin_renderer_remote_->UpdateDownloading();
114 break;
115 case Events::COMPONENT_NOT_UPDATED:
116 case Events::COMPONENT_UPDATE_ERROR:
117 plugin_renderer_remote_->UpdateFailure();
118 observer_->RemoveComponentObserver(this);
119 break;
120 default:
121 // No message to send.
122 break;
123 }
124 }
125
126 private:
127 PluginObserver* observer_;
128 std::string component_id_;
129 mojo::Remote<chrome::mojom::PluginRenderer> plugin_renderer_remote_;
130 DISALLOW_COPY_AND_ASSIGN(ComponentObserver);
131 };
132
PluginObserver(content::WebContents * web_contents)133 PluginObserver::PluginObserver(content::WebContents* web_contents)
134 : content::WebContentsObserver(web_contents),
135 plugin_host_receivers_(web_contents, this) {}
136
~PluginObserver()137 PluginObserver::~PluginObserver() {
138 }
139
PluginCrashed(const base::FilePath & plugin_path,base::ProcessId plugin_pid)140 void PluginObserver::PluginCrashed(const base::FilePath& plugin_path,
141 base::ProcessId plugin_pid) {
142 DCHECK(!plugin_path.value().empty());
143
144 base::string16 plugin_name =
145 PluginService::GetInstance()->GetPluginDisplayNameByPath(plugin_path);
146 base::string16 infobar_text;
147 #if defined(OS_WIN)
148 // Find out whether the plugin process is still alive.
149 // Note: Although the chances are slim, it is possible that after the plugin
150 // process died, |plugin_pid| has been reused by a new process. The
151 // consequence is that we will display |IDS_PLUGIN_DISCONNECTED_PROMPT| rather
152 // than |IDS_PLUGIN_CRASHED_PROMPT| to the user, which seems acceptable.
153 base::Process plugin_process =
154 base::Process::OpenWithAccess(plugin_pid,
155 PROCESS_QUERY_INFORMATION | SYNCHRONIZE);
156 bool is_running = false;
157 if (plugin_process.IsValid()) {
158 int unused_exit_code = 0;
159 is_running = base::GetTerminationStatus(plugin_process.Handle(),
160 &unused_exit_code) ==
161 base::TERMINATION_STATUS_STILL_RUNNING;
162 plugin_process.Close();
163 }
164
165 if (is_running) {
166 infobar_text = l10n_util::GetStringFUTF16(IDS_PLUGIN_DISCONNECTED_PROMPT,
167 plugin_name);
168 UMA_HISTOGRAM_COUNTS_1M("Plugin.ShowDisconnectedInfobar", 1);
169 } else {
170 infobar_text = l10n_util::GetStringFUTF16(IDS_PLUGIN_CRASHED_PROMPT,
171 plugin_name);
172 UMA_HISTOGRAM_COUNTS_1M("Plugin.ShowCrashedInfobar", 1);
173 }
174 #else
175 // Calling the POSIX version of base::GetTerminationStatus() may affect other
176 // code which is interested in the process termination status. (Please see the
177 // comment of the function.) Therefore, a better way is needed to distinguish
178 // disconnections from crashes.
179 infobar_text = l10n_util::GetStringFUTF16(IDS_PLUGIN_CRASHED_PROMPT,
180 plugin_name);
181 UMA_HISTOGRAM_COUNTS_1M("Plugin.ShowCrashedInfobar", 1);
182 #endif
183
184 ReloadPluginInfoBarDelegate::Create(
185 InfoBarService::FromWebContents(web_contents()),
186 &web_contents()->GetController(),
187 infobar_text);
188 }
189
190 // static
CreatePluginObserverInfoBar(InfoBarService * infobar_service,const base::string16 & plugin_name)191 void PluginObserver::CreatePluginObserverInfoBar(
192 InfoBarService* infobar_service,
193 const base::string16& plugin_name) {
194 SimpleAlertInfoBarDelegate::Create(
195 infobar_service,
196 infobars::InfoBarDelegate::PLUGIN_OBSERVER_INFOBAR_DELEGATE,
197 &kExtensionCrashedIcon,
198 l10n_util::GetStringFUTF16(IDS_PLUGIN_INITIALIZATION_ERROR_PROMPT,
199 plugin_name));
200 }
201
BlockedOutdatedPlugin(mojo::PendingRemote<chrome::mojom::PluginRenderer> plugin_renderer,const std::string & identifier)202 void PluginObserver::BlockedOutdatedPlugin(
203 mojo::PendingRemote<chrome::mojom::PluginRenderer> plugin_renderer,
204 const std::string& identifier) {
205 PluginFinder* finder = PluginFinder::GetInstance();
206 // Find plugin to update.
207 PluginInstaller* installer = NULL;
208 std::unique_ptr<PluginMetadata> plugin;
209 if (finder->FindPluginWithIdentifier(identifier, &installer, &plugin)) {
210 auto plugin_placeholder = std::make_unique<PluginPlaceholderHost>(
211 this, plugin->name(), installer, std::move(plugin_renderer));
212 plugin_placeholders_[plugin_placeholder.get()] =
213 std::move(plugin_placeholder);
214
215 OutdatedPluginInfoBarDelegate::Create(
216 InfoBarService::FromWebContents(web_contents()), installer,
217 std::move(plugin));
218 } else {
219 NOTREACHED();
220 }
221 }
222
BlockedComponentUpdatedPlugin(mojo::PendingRemote<chrome::mojom::PluginRenderer> plugin_renderer,const std::string & identifier)223 void PluginObserver::BlockedComponentUpdatedPlugin(
224 mojo::PendingRemote<chrome::mojom::PluginRenderer> plugin_renderer,
225 const std::string& identifier) {
226 auto component_observer = std::make_unique<ComponentObserver>(
227 this, identifier, std::move(plugin_renderer));
228 component_observers_[component_observer.get()] =
229 std::move(component_observer);
230 g_browser_process->component_updater()->GetOnDemandUpdater().OnDemandUpdate(
231 identifier, component_updater::OnDemandUpdater::Priority::FOREGROUND,
232 component_updater::Callback());
233 }
234
RemoveComponentObserver(ComponentObserver * component_observer)235 void PluginObserver::RemoveComponentObserver(
236 ComponentObserver* component_observer) {
237 component_observers_.erase(component_observer);
238 }
239
RemovePluginPlaceholderHost(PluginPlaceholderHost * placeholder)240 void PluginObserver::RemovePluginPlaceholderHost(
241 PluginPlaceholderHost* placeholder) {
242 plugin_placeholders_.erase(placeholder);
243 }
244
ShowFlashPermissionBubble()245 void PluginObserver::ShowFlashPermissionBubble() {
246 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
247
248 // TODO(tommycli): This is a no-op now. Delete this method in a followup.
249 }
250
CouldNotLoadPlugin(const base::FilePath & plugin_path)251 void PluginObserver::CouldNotLoadPlugin(const base::FilePath& plugin_path) {
252 g_browser_process->GetMetricsServicesManager()->OnPluginLoadingError(
253 plugin_path);
254 base::string16 plugin_name =
255 PluginService::GetInstance()->GetPluginDisplayNameByPath(plugin_path);
256 CreatePluginObserverInfoBar(InfoBarService::FromWebContents(web_contents()),
257 plugin_name);
258 }
259
OpenPDF(const GURL & url)260 void PluginObserver::OpenPDF(const GURL& url) {
261 // WebViews should never trigger PDF downloads.
262 auto* guest_view = guest_view::GuestViewBase::FromWebContents(web_contents());
263 if (guest_view && guest_view->IsViewType(extensions::WebViewGuest::Type))
264 return;
265
266 content::RenderFrameHost* render_frame_host =
267 plugin_host_receivers_.GetCurrentTargetFrame();
268
269 if (!content::ChildProcessSecurityPolicy::GetInstance()->CanRequestURL(
270 render_frame_host->GetRoutingID(), url)) {
271 return;
272 }
273
274 content::Referrer referrer = content::Referrer::SanitizeForRequest(
275 url, content::Referrer(web_contents()->GetURL(),
276 network::mojom::ReferrerPolicy::kDefault));
277
278 #if BUILDFLAG(ENABLE_PLUGINS)
279 net::NetworkTrafficAnnotationTag traffic_annotation =
280 net::DefineNetworkTrafficAnnotation("pdf_plugin_placeholder", R"(
281 semantics {
282 sender: "PDF Plugin Placeholder"
283 description:
284 "When the PDF Viewer is unavailable, a placeholder is shown for "
285 "embedded PDFs. This placeholder allows the user to download and "
286 "open the PDF file via a button."
287 trigger:
288 "The user clicks the 'View PDF' button in the PDF placeholder."
289 data: "None."
290 destination: WEBSITE
291 }
292 policy {
293 cookies_allowed: NO
294 setting:
295 "This feature can be disabled via 'Download PDF files instead of "
296 "automatically opening them in Chrome' in settings under content. "
297 "The feature is disabled by default."
298 chrome_policy {
299 AlwaysOpenPdfExternally {
300 AlwaysOpenPdfExternally: false
301 }
302 }
303 })");
304 std::unique_ptr<download::DownloadUrlParameters> params =
305 std::make_unique<download::DownloadUrlParameters>(
306 url, render_frame_host->GetRenderViewHost()->GetProcess()->GetID(),
307 render_frame_host->GetRoutingID(), traffic_annotation);
308 params->set_referrer(referrer.url);
309 params->set_referrer_policy(
310 content::Referrer::ReferrerPolicyForUrlRequest(referrer.policy));
311
312 content::BrowserContext::GetDownloadManager(
313 web_contents()->GetBrowserContext())
314 ->DownloadUrl(std::move(params));
315
316 #else // !BUILDFLAG(ENABLE_PLUGINS)
317 content::OpenURLParams open_url_params(
318 url, referrer, WindowOpenDisposition::CURRENT_TAB,
319 ui::PAGE_TRANSITION_AUTO_BOOKMARK, false);
320 // On Android, PDFs downloaded with a user gesture are auto-opened.
321 open_url_params.user_gesture = true;
322 web_contents()->OpenURL(open_url_params);
323 #endif // BUILDFLAG(ENABLE_PLUGINS)
324 }
325
326 WEB_CONTENTS_USER_DATA_KEY_IMPL(PluginObserver)
327