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