1// Copyright (c) 2011 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/ui/webui/plugins_ui.h"
6
7#include <algorithm>
8#include <string>
9#include <vector>
10
11#include "base/memory/singleton.h"
12#include "base/message_loop.h"
13#include "base/path_service.h"
14#include "base/utf_string_conversions.h"
15#include "base/values.h"
16#include "chrome/browser/plugin_updater.h"
17#include "chrome/browser/prefs/pref_member.h"
18#include "chrome/browser/prefs/pref_service.h"
19#include "chrome/browser/profiles/profile.h"
20#include "chrome/browser/ui/browser.h"
21#include "chrome/browser/ui/browser_window.h"
22#include "chrome/browser/ui/webui/chrome_url_data_manager.h"
23#include "chrome/common/chrome_content_client.h"
24#include "chrome/common/chrome_paths.h"
25#include "chrome/common/jstemplate_builder.h"
26#include "chrome/common/pref_names.h"
27#include "chrome/common/url_constants.h"
28#include "content/browser/browser_thread.h"
29#include "content/browser/tab_contents/tab_contents.h"
30#include "content/common/notification_service.h"
31#include "grit/browser_resources.h"
32#include "grit/generated_resources.h"
33#include "grit/theme_resources.h"
34#include "ui/base/l10n/l10n_util.h"
35#include "ui/base/resource/resource_bundle.h"
36#include "webkit/plugins/npapi/plugin_list.h"
37
38namespace {
39
40///////////////////////////////////////////////////////////////////////////////
41//
42// PluginsHTMLSource
43//
44///////////////////////////////////////////////////////////////////////////////
45
46class PluginsUIHTMLSource : public ChromeURLDataManager::DataSource {
47 public:
48  PluginsUIHTMLSource()
49      : DataSource(chrome::kChromeUIPluginsHost, MessageLoop::current()) {}
50
51  // Called when the network layer has requested a resource underneath
52  // the path we registered.
53  virtual void StartDataRequest(const std::string& path,
54                                bool is_incognito,
55                                int request_id);
56  virtual std::string GetMimeType(const std::string&) const {
57    return "text/html";
58  }
59
60 private:
61  ~PluginsUIHTMLSource() {}
62
63  DISALLOW_COPY_AND_ASSIGN(PluginsUIHTMLSource);
64};
65
66void PluginsUIHTMLSource::StartDataRequest(const std::string& path,
67                                           bool is_incognito,
68                                           int request_id) {
69  // Strings used in the JsTemplate file.
70  DictionaryValue localized_strings;
71  localized_strings.SetString("pluginsTitle",
72      l10n_util::GetStringUTF16(IDS_PLUGINS_TITLE));
73  localized_strings.SetString("pluginsDetailsModeLink",
74      l10n_util::GetStringUTF16(IDS_PLUGINS_DETAILS_MODE_LINK));
75  localized_strings.SetString("pluginsNoneInstalled",
76      l10n_util::GetStringUTF16(IDS_PLUGINS_NONE_INSTALLED));
77  localized_strings.SetString("pluginDisabled",
78      l10n_util::GetStringUTF16(IDS_PLUGINS_DISABLED_PLUGIN));
79  localized_strings.SetString("pluginDisabledByPolicy",
80      l10n_util::GetStringUTF16(IDS_PLUGINS_DISABLED_BY_POLICY_PLUGIN));
81  localized_strings.SetString("pluginCannotBeEnabledDueToPolicy",
82      l10n_util::GetStringUTF16(IDS_PLUGINS_CANNOT_ENABLE_DUE_TO_POLICY));
83  localized_strings.SetString("pluginEnabledByPolicy",
84      l10n_util::GetStringUTF16(IDS_PLUGINS_ENABLED_BY_POLICY_PLUGIN));
85  localized_strings.SetString("pluginCannotBeDisabledDueToPolicy",
86      l10n_util::GetStringUTF16(IDS_PLUGINS_CANNOT_DISABLE_DUE_TO_POLICY));
87  localized_strings.SetString("pluginDownload",
88      l10n_util::GetStringUTF16(IDS_PLUGINS_DOWNLOAD));
89  localized_strings.SetString("pluginName",
90      l10n_util::GetStringUTF16(IDS_PLUGINS_NAME));
91  localized_strings.SetString("pluginVersion",
92      l10n_util::GetStringUTF16(IDS_PLUGINS_VERSION));
93  localized_strings.SetString("pluginDescription",
94      l10n_util::GetStringUTF16(IDS_PLUGINS_DESCRIPTION));
95  localized_strings.SetString("pluginPath",
96      l10n_util::GetStringUTF16(IDS_PLUGINS_PATH));
97  localized_strings.SetString("pluginMimeTypes",
98      l10n_util::GetStringUTF16(IDS_PLUGINS_MIME_TYPES));
99  localized_strings.SetString("pluginMimeTypesMimeType",
100      l10n_util::GetStringUTF16(IDS_PLUGINS_MIME_TYPES_MIME_TYPE));
101  localized_strings.SetString("pluginMimeTypesDescription",
102      l10n_util::GetStringUTF16(IDS_PLUGINS_MIME_TYPES_DESCRIPTION));
103  localized_strings.SetString("pluginMimeTypesFileExtensions",
104      l10n_util::GetStringUTF16(IDS_PLUGINS_MIME_TYPES_FILE_EXTENSIONS));
105  localized_strings.SetString("disable",
106      l10n_util::GetStringUTF16(IDS_PLUGINS_DISABLE));
107  localized_strings.SetString("enable",
108      l10n_util::GetStringUTF16(IDS_PLUGINS_ENABLE));
109  localized_strings.SetString("noPlugins",
110      l10n_util::GetStringUTF16(IDS_PLUGINS_NO_PLUGINS));
111
112  ChromeURLDataManager::DataSource::SetFontAndTextDirection(&localized_strings);
113
114  static const base::StringPiece plugins_html(
115      ResourceBundle::GetSharedInstance().GetRawDataResource(IDR_PLUGINS_HTML));
116  std::string full_html(plugins_html.data(), plugins_html.size());
117  jstemplate_builder::AppendJsonHtml(&localized_strings, &full_html);
118  jstemplate_builder::AppendI18nTemplateSourceHtml(&full_html);
119  jstemplate_builder::AppendI18nTemplateProcessHtml(&full_html);
120  jstemplate_builder::AppendJsTemplateSourceHtml(&full_html);
121
122  scoped_refptr<RefCountedBytes> html_bytes(new RefCountedBytes);
123  html_bytes->data.resize(full_html.size());
124  std::copy(full_html.begin(), full_html.end(), html_bytes->data.begin());
125
126  SendResponse(request_id, html_bytes);
127}
128
129////////////////////////////////////////////////////////////////////////////////
130//
131// PluginsDOMHandler
132//
133////////////////////////////////////////////////////////////////////////////////
134
135// The handler for Javascript messages for the chrome://plugins/ page.
136// TODO(viettrungluu): Make plugin list updates notify, and then observe
137// changes; maybe replumb plugin list through plugin service?
138// <http://crbug.com/39101>
139class PluginsDOMHandler : public WebUIMessageHandler,
140                          public NotificationObserver {
141 public:
142  explicit PluginsDOMHandler();
143  virtual ~PluginsDOMHandler() {}
144
145  // WebUIMessageHandler implementation.
146  virtual WebUIMessageHandler* Attach(WebUI* web_ui);
147  virtual void RegisterMessages();
148
149  // Callback for the "requestPluginsData" message.
150  void HandleRequestPluginsData(const ListValue* args);
151
152  // Callback for the "enablePlugin" message.
153  void HandleEnablePluginMessage(const ListValue* args);
154
155  // Callback for the "showTermsOfService" message. This really just opens a new
156  // window with about:terms. Flash can't link directly to about:terms due to
157  // the security model.
158  void HandleShowTermsOfServiceMessage(const ListValue* args);
159
160  // Callback for the "saveShowDetailsToPrefs" message.
161  void HandleSaveShowDetailsToPrefs(const ListValue* args);
162
163  // Calback for the "getShowDetails" message.
164  void HandleGetShowDetails(const ListValue* args);
165
166  // NotificationObserver method overrides
167  void Observe(NotificationType type,
168               const NotificationSource& source,
169               const NotificationDetails& details);
170
171 private:
172  // This extra wrapper is used to ensure we don't leak the ListValue* pointer
173  // if the PluginsDOMHandler object goes away before the task on the UI thread
174  // to give it the plugin list runs.
175  struct ListWrapper {
176    ListValue* list;
177  };
178  // Loads the plugins on the FILE thread.
179  static void LoadPluginsOnFileThread(ListWrapper* wrapper, Task* task);
180
181  // Used in conjunction with ListWrapper to avoid any memory leaks.
182  static void EnsureListDeleted(ListWrapper* wrapper);
183
184  // Call this to start getting the plugins on the UI thread.
185  void LoadPlugins();
186
187  // Called on the UI thread when the plugin information is ready.
188  void PluginsLoaded(ListWrapper* wrapper);
189
190  NotificationRegistrar registrar_;
191
192  ScopedRunnableMethodFactory<PluginsDOMHandler> get_plugins_factory_;
193
194  // This pref guards the value whether about:plugins is in the details mode or
195  // not.
196  BooleanPrefMember show_details_;
197
198  DISALLOW_COPY_AND_ASSIGN(PluginsDOMHandler);
199};
200
201PluginsDOMHandler::PluginsDOMHandler()
202    : ALLOW_THIS_IN_INITIALIZER_LIST(get_plugins_factory_(this)) {
203  registrar_.Add(this,
204                 NotificationType::PLUGIN_ENABLE_STATUS_CHANGED,
205                 NotificationService::AllSources());
206}
207
208WebUIMessageHandler* PluginsDOMHandler::Attach(WebUI* web_ui) {
209  PrefService* prefs = web_ui->GetProfile()->GetPrefs();
210
211  show_details_.Init(prefs::kPluginsShowDetails, prefs, this);
212
213  return WebUIMessageHandler::Attach(web_ui);
214}
215
216void PluginsDOMHandler::RegisterMessages() {
217  web_ui_->RegisterMessageCallback("requestPluginsData",
218      NewCallback(this, &PluginsDOMHandler::HandleRequestPluginsData));
219  web_ui_->RegisterMessageCallback("enablePlugin",
220      NewCallback(this, &PluginsDOMHandler::HandleEnablePluginMessage));
221  web_ui_->RegisterMessageCallback("showTermsOfService",
222      NewCallback(this, &PluginsDOMHandler::HandleShowTermsOfServiceMessage));
223  web_ui_->RegisterMessageCallback("saveShowDetailsToPrefs",
224      NewCallback(this, &PluginsDOMHandler::HandleSaveShowDetailsToPrefs));
225  web_ui_->RegisterMessageCallback("getShowDetails",
226      NewCallback(this, &PluginsDOMHandler::HandleGetShowDetails));
227}
228
229void PluginsDOMHandler::HandleRequestPluginsData(const ListValue* args) {
230  LoadPlugins();
231}
232
233void PluginsDOMHandler::HandleEnablePluginMessage(const ListValue* args) {
234  // Be robust in accepting badness since plug-ins display HTML (hence
235  // JavaScript).
236  if (args->GetSize() != 3)
237    return;
238
239  std::string enable_str;
240  std::string is_group_str;
241  if (!args->GetString(1, &enable_str) || !args->GetString(2, &is_group_str))
242    return;
243  bool enable = enable_str == "true";
244
245  PluginUpdater* plugin_updater = PluginUpdater::GetInstance();
246  if (is_group_str == "true") {
247    string16 group_name;
248    if (!args->GetString(0, &group_name))
249      return;
250
251    plugin_updater->EnablePluginGroup(enable, group_name);
252    if (enable) {
253      // See http://crbug.com/50105 for background.
254      string16 adobereader = ASCIIToUTF16(
255          webkit::npapi::PluginGroup::kAdobeReaderGroupName);
256      string16 internalpdf =
257          ASCIIToUTF16(chrome::ChromeContentClient::kPDFPluginName);
258      if (group_name == adobereader) {
259        plugin_updater->EnablePluginGroup(false, internalpdf);
260      } else if (group_name == internalpdf) {
261        plugin_updater->EnablePluginGroup(false, adobereader);
262      }
263    }
264  } else {
265    FilePath::StringType file_path;
266    if (!args->GetString(0, &file_path))
267      return;
268
269    plugin_updater->EnablePlugin(enable, file_path);
270  }
271
272  // TODO(viettrungluu): We might also want to ensure that the plugins
273  // list is always written to prefs even when the user hasn't disabled a
274  // plugin. <http://crbug.com/39101>
275  plugin_updater->UpdatePreferences(web_ui_->GetProfile(), 0);
276}
277
278void PluginsDOMHandler::HandleShowTermsOfServiceMessage(const ListValue* args) {
279  // Show it in a new browser window....
280  Browser* browser = Browser::Create(web_ui_->GetProfile());
281  browser->OpenURL(GURL(chrome::kAboutTermsURL),
282                   GURL(), NEW_FOREGROUND_TAB, PageTransition::LINK);
283  browser->window()->Show();
284}
285
286void PluginsDOMHandler::HandleSaveShowDetailsToPrefs(const ListValue* args) {
287  std::string details_mode;
288  if (!args->GetString(0, &details_mode)) {
289    NOTREACHED();
290    return;
291  }
292  show_details_.SetValue(details_mode == "true");
293}
294
295void PluginsDOMHandler::HandleGetShowDetails(const ListValue* args) {
296  FundamentalValue show_details(show_details_.GetValue());
297  web_ui_->CallJavascriptFunction("loadShowDetailsFromPrefs", show_details);
298}
299
300void PluginsDOMHandler::Observe(NotificationType type,
301                                const NotificationSource& source,
302                                const NotificationDetails& details) {
303  DCHECK_EQ(NotificationType::PLUGIN_ENABLE_STATUS_CHANGED, type.value);
304  LoadPlugins();
305}
306
307void PluginsDOMHandler::LoadPluginsOnFileThread(ListWrapper* wrapper,
308                                                Task* task) {
309  wrapper->list = PluginUpdater::GetInstance()->GetPluginGroupsData();
310  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, task);
311  BrowserThread::PostTask(
312      BrowserThread::UI,
313      FROM_HERE,
314      NewRunnableFunction(&PluginsDOMHandler::EnsureListDeleted, wrapper));
315}
316
317void PluginsDOMHandler::EnsureListDeleted(ListWrapper* wrapper) {
318  delete wrapper->list;
319  delete wrapper;
320}
321
322void PluginsDOMHandler::LoadPlugins() {
323  if (!get_plugins_factory_.empty())
324    return;
325
326  ListWrapper* wrapper = new ListWrapper;
327  wrapper->list = NULL;
328  Task* task = get_plugins_factory_.NewRunnableMethod(
329          &PluginsDOMHandler::PluginsLoaded, wrapper);
330
331  BrowserThread::PostTask(
332      BrowserThread::FILE,
333      FROM_HERE,
334      NewRunnableFunction(
335          &PluginsDOMHandler::LoadPluginsOnFileThread, wrapper, task));
336}
337
338void PluginsDOMHandler::PluginsLoaded(ListWrapper* wrapper) {
339  DictionaryValue results;
340  results.Set("plugins", wrapper->list);
341  wrapper->list = NULL;  // So it doesn't get deleted.
342  web_ui_->CallJavascriptFunction("returnPluginsData", results);
343}
344
345}  // namespace
346
347///////////////////////////////////////////////////////////////////////////////
348//
349// PluginsUI
350//
351///////////////////////////////////////////////////////////////////////////////
352
353PluginsUI::PluginsUI(TabContents* contents) : WebUI(contents) {
354  AddMessageHandler((new PluginsDOMHandler())->Attach(this));
355
356  PluginsUIHTMLSource* html_source = new PluginsUIHTMLSource();
357
358  // Set up the chrome://plugins/ source.
359  contents->profile()->GetChromeURLDataManager()->AddDataSource(html_source);
360}
361
362
363// static
364RefCountedMemory* PluginsUI::GetFaviconResourceBytes() {
365  return ResourceBundle::GetSharedInstance().
366      LoadDataResourceBytes(IDR_PLUGIN);
367}
368
369// static
370void PluginsUI::RegisterUserPrefs(PrefService* prefs) {
371  FilePath internal_dir;
372  PathService::Get(chrome::DIR_INTERNAL_PLUGINS, &internal_dir);
373  prefs->RegisterFilePathPref(prefs::kPluginsLastInternalDirectory,
374                              internal_dir);
375
376  prefs->RegisterListPref(prefs::kPluginsDisabledPlugins);
377  prefs->RegisterListPref(prefs::kPluginsDisabledPluginsExceptions);
378  prefs->RegisterListPref(prefs::kPluginsEnabledPlugins);
379  prefs->RegisterListPref(prefs::kPluginsPluginsList);
380  prefs->RegisterBooleanPref(prefs::kPluginsEnabledInternalPDF, false);
381  prefs->RegisterBooleanPref(prefs::kPluginsShowDetails, false);
382  prefs->RegisterBooleanPref(prefs::kPluginsShowSetReaderDefaultInfobar, true);
383}
384