• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/extensions/extensions_ui.h"
6 
7 #include <algorithm>
8 
9 #include "base/base64.h"
10 #include "base/callback.h"
11 #include "base/file_util.h"
12 #include "base/memory/singleton.h"
13 #include "base/string_number_conversions.h"
14 #include "base/string_util.h"
15 #include "base/threading/thread.h"
16 #include "base/utf_string_conversions.h"
17 #include "base/version.h"
18 #include "chrome/browser/debugger/devtools_manager.h"
19 #include "chrome/browser/debugger/devtools_toggle_action.h"
20 #include "chrome/browser/extensions/crx_installer.h"
21 #include "chrome/browser/extensions/extension_disabled_infobar_delegate.h"
22 #include "chrome/browser/extensions/extension_error_reporter.h"
23 #include "chrome/browser/extensions/extension_function_dispatcher.h"
24 #include "chrome/browser/extensions/extension_host.h"
25 #include "chrome/browser/extensions/extension_message_service.h"
26 #include "chrome/browser/extensions/extension_service.h"
27 #include "chrome/browser/extensions/extension_updater.h"
28 #include "chrome/browser/google/google_util.h"
29 #include "chrome/browser/prefs/pref_service.h"
30 #include "chrome/browser/profiles/profile.h"
31 #include "chrome/browser/tab_contents/background_contents.h"
32 #include "chrome/browser/ui/browser_list.h"
33 #include "chrome/browser/ui/webui/extension_icon_source.h"
34 #include "chrome/common/extensions/extension.h"
35 #include "chrome/common/extensions/extension_icon_set.h"
36 #include "chrome/common/extensions/url_pattern.h"
37 #include "chrome/common/extensions/user_script.h"
38 #include "chrome/common/jstemplate_builder.h"
39 #include "chrome/common/pref_names.h"
40 #include "chrome/common/url_constants.h"
41 #include "content/browser/renderer_host/render_process_host.h"
42 #include "content/browser/renderer_host/render_view_host.h"
43 #include "content/browser/renderer_host/render_widget_host.h"
44 #include "content/browser/tab_contents/tab_contents.h"
45 #include "content/browser/tab_contents/tab_contents_view.h"
46 #include "content/common/notification_service.h"
47 #include "content/common/notification_type.h"
48 #include "googleurl/src/gurl.h"
49 #include "grit/browser_resources.h"
50 #include "grit/chromium_strings.h"
51 #include "grit/generated_resources.h"
52 #include "grit/theme_resources.h"
53 #include "net/base/net_util.h"
54 #include "ui/base/l10n/l10n_util.h"
55 #include "ui/base/resource/resource_bundle.h"
56 
57 namespace {
58 
ShouldShowExtension(const Extension * extension)59 bool ShouldShowExtension(const Extension* extension) {
60   // Don't show themes since this page's UI isn't really useful for themes.
61   if (extension->is_theme())
62     return false;
63 
64   // Don't show component extensions because they are only extensions as an
65   // implementation detail of Chrome.
66   if (extension->location() == Extension::COMPONENT)
67     return false;
68 
69   // Always show unpacked extensions and apps.
70   if (extension->location() == Extension::LOAD)
71     return true;
72 
73   // Unless they are unpacked, never show hosted apps.
74   if (extension->is_hosted_app())
75     return false;
76 
77   return true;
78 }
79 
80 }  // namespace
81 
82 ////////////////////////////////////////////////////////////////////////////////
83 //
84 // ExtensionsHTMLSource
85 //
86 ////////////////////////////////////////////////////////////////////////////////
87 
ExtensionsUIHTMLSource()88 ExtensionsUIHTMLSource::ExtensionsUIHTMLSource()
89     : DataSource(chrome::kChromeUIExtensionsHost, MessageLoop::current()) {
90 }
91 
StartDataRequest(const std::string & path,bool is_incognito,int request_id)92 void ExtensionsUIHTMLSource::StartDataRequest(const std::string& path,
93     bool is_incognito, int request_id) {
94   DictionaryValue localized_strings;
95   localized_strings.SetString("title",
96       l10n_util::GetStringUTF16(IDS_EXTENSIONS_TITLE));
97   localized_strings.SetString("devModeLink",
98       l10n_util::GetStringUTF16(IDS_EXTENSIONS_DEVELOPER_MODE_LINK));
99   localized_strings.SetString("devModePrefix",
100       l10n_util::GetStringUTF16(IDS_EXTENSIONS_DEVELOPER_MODE_PREFIX));
101   localized_strings.SetString("loadUnpackedButton",
102       l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOAD_UNPACKED_BUTTON));
103   localized_strings.SetString("packButton",
104       l10n_util::GetStringUTF16(IDS_EXTENSIONS_PACK_BUTTON));
105   localized_strings.SetString("updateButton",
106       l10n_util::GetStringUTF16(IDS_EXTENSIONS_UPDATE_BUTTON));
107   localized_strings.SetString("noExtensions",
108       l10n_util::GetStringUTF16(IDS_EXTENSIONS_NONE_INSTALLED));
109   localized_strings.SetString("suggestGallery",
110       l10n_util::GetStringFUTF16(IDS_EXTENSIONS_NONE_INSTALLED_SUGGEST_GALLERY,
111           ASCIIToUTF16("<a href='") +
112               ASCIIToUTF16(google_util::AppendGoogleLocaleParam(
113                   GURL(Extension::ChromeStoreLaunchURL())).spec()) +
114               ASCIIToUTF16("'>"),
115           ASCIIToUTF16("</a>")));
116   localized_strings.SetString("getMoreExtensions",
117       ASCIIToUTF16("<a href='") +
118       ASCIIToUTF16(google_util::AppendGoogleLocaleParam(
119           GURL(Extension::ChromeStoreLaunchURL())).spec()) +
120       ASCIIToUTF16("'>") +
121       l10n_util::GetStringUTF16(IDS_GET_MORE_EXTENSIONS) +
122       ASCIIToUTF16("</a>"));
123   localized_strings.SetString("extensionCrashed",
124       l10n_util::GetStringUTF16(IDS_EXTENSIONS_CRASHED_EXTENSION));
125   localized_strings.SetString("extensionDisabled",
126       l10n_util::GetStringUTF16(IDS_EXTENSIONS_DISABLED_EXTENSION));
127   localized_strings.SetString("inDevelopment",
128       l10n_util::GetStringUTF16(IDS_EXTENSIONS_IN_DEVELOPMENT));
129   localized_strings.SetString("viewIncognito",
130       l10n_util::GetStringUTF16(IDS_EXTENSIONS_VIEW_INCOGNITO));
131   localized_strings.SetString("extensionId",
132       l10n_util::GetStringUTF16(IDS_EXTENSIONS_ID));
133   localized_strings.SetString("extensionPath",
134       l10n_util::GetStringUTF16(IDS_EXTENSIONS_PATH));
135   localized_strings.SetString("extensionVersion",
136       l10n_util::GetStringUTF16(IDS_EXTENSIONS_VERSION));
137   localized_strings.SetString("inspectViews",
138       l10n_util::GetStringUTF16(IDS_EXTENSIONS_INSPECT_VIEWS));
139   localized_strings.SetString("inspectPopupsInstructions",
140       l10n_util::GetStringUTF16(IDS_EXTENSIONS_INSPECT_POPUPS_INSTRUCTIONS));
141   localized_strings.SetString("disable",
142       l10n_util::GetStringUTF16(IDS_EXTENSIONS_DISABLE));
143   localized_strings.SetString("enable",
144       l10n_util::GetStringUTF16(IDS_EXTENSIONS_ENABLE));
145   localized_strings.SetString("enableIncognito",
146       l10n_util::GetStringUTF16(IDS_EXTENSIONS_ENABLE_INCOGNITO));
147   localized_strings.SetString("allowFileAccess",
148       l10n_util::GetStringUTF16(IDS_EXTENSIONS_ALLOW_FILE_ACCESS));
149   localized_strings.SetString("incognitoWarning",
150       l10n_util::GetStringFUTF16(IDS_EXTENSIONS_INCOGNITO_WARNING,
151                                  l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)));
152   localized_strings.SetString("reload",
153       l10n_util::GetStringUTF16(IDS_EXTENSIONS_RELOAD));
154   localized_strings.SetString("uninstall",
155       l10n_util::GetStringUTF16(IDS_EXTENSIONS_UNINSTALL));
156   localized_strings.SetString("options",
157       l10n_util::GetStringUTF16(IDS_EXTENSIONS_OPTIONS));
158   localized_strings.SetString("policyControlled",
159       l10n_util::GetStringUTF16(IDS_EXTENSIONS_POLICY_CONTROLLED));
160   localized_strings.SetString("packDialogTitle",
161       l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_DIALOG_TITLE));
162   localized_strings.SetString("packDialogHeading",
163       l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_DIALOG_HEADING));
164   localized_strings.SetString("rootDirectoryLabel",
165       l10n_util::GetStringUTF16(
166           IDS_EXTENSION_PACK_DIALOG_ROOT_DIRECTORY_LABEL));
167   localized_strings.SetString("packDialogBrowse",
168       l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_DIALOG_BROWSE));
169   localized_strings.SetString("privateKeyLabel",
170       l10n_util::GetStringUTF16(IDS_EXTENSION_PACK_DIALOG_PRIVATE_KEY_LABEL));
171   localized_strings.SetString("okButton",
172       l10n_util::GetStringUTF16(IDS_OK));
173   localized_strings.SetString("cancelButton",
174       l10n_util::GetStringUTF16(IDS_CANCEL));
175   localized_strings.SetString("showButton",
176       l10n_util::GetStringUTF16(IDS_EXTENSIONS_SHOW_BUTTON));
177 
178   SetFontAndTextDirection(&localized_strings);
179 
180   static const base::StringPiece extensions_html(
181       ResourceBundle::GetSharedInstance().GetRawDataResource(
182           IDR_EXTENSIONS_UI_HTML));
183   std::string full_html(extensions_html.data(), extensions_html.size());
184   jstemplate_builder::AppendJsonHtml(&localized_strings, &full_html);
185   jstemplate_builder::AppendI18nTemplateSourceHtml(&full_html);
186   jstemplate_builder::AppendI18nTemplateProcessHtml(&full_html);
187   jstemplate_builder::AppendJsTemplateSourceHtml(&full_html);
188 
189   scoped_refptr<RefCountedBytes> html_bytes(new RefCountedBytes);
190   html_bytes->data.resize(full_html.size());
191   std::copy(full_html.begin(), full_html.end(), html_bytes->data.begin());
192 
193   SendResponse(request_id, html_bytes);
194 }
195 
GetMimeType(const std::string &) const196 std::string ExtensionsUIHTMLSource::GetMimeType(const std::string&) const {
197   return "text/html";
198 }
199 
200 ///////////////////////////////////////////////////////////////////////////////
201 //
202 // ExtensionsDOMHandler
203 //
204 ///////////////////////////////////////////////////////////////////////////////
205 
ExtensionsDOMHandler(ExtensionService * extension_service)206 ExtensionsDOMHandler::ExtensionsDOMHandler(ExtensionService* extension_service)
207     : extensions_service_(extension_service),
208       ignore_notifications_(false),
209       deleting_rvh_(NULL) {
210   RegisterForNotifications();
211 }
212 
RegisterMessages()213 void ExtensionsDOMHandler::RegisterMessages() {
214   web_ui_->RegisterMessageCallback("requestExtensionsData",
215       NewCallback(this, &ExtensionsDOMHandler::HandleRequestExtensionsData));
216   web_ui_->RegisterMessageCallback("toggleDeveloperMode",
217       NewCallback(this, &ExtensionsDOMHandler::HandleToggleDeveloperMode));
218   web_ui_->RegisterMessageCallback("inspect",
219       NewCallback(this, &ExtensionsDOMHandler::HandleInspectMessage));
220   web_ui_->RegisterMessageCallback("reload",
221       NewCallback(this, &ExtensionsDOMHandler::HandleReloadMessage));
222   web_ui_->RegisterMessageCallback("enable",
223       NewCallback(this, &ExtensionsDOMHandler::HandleEnableMessage));
224   web_ui_->RegisterMessageCallback("enableIncognito",
225       NewCallback(this, &ExtensionsDOMHandler::HandleEnableIncognitoMessage));
226   web_ui_->RegisterMessageCallback("allowFileAccess",
227       NewCallback(this, &ExtensionsDOMHandler::HandleAllowFileAccessMessage));
228   web_ui_->RegisterMessageCallback("uninstall",
229       NewCallback(this, &ExtensionsDOMHandler::HandleUninstallMessage));
230   web_ui_->RegisterMessageCallback("options",
231       NewCallback(this, &ExtensionsDOMHandler::HandleOptionsMessage));
232   web_ui_->RegisterMessageCallback("showButton",
233       NewCallback(this, &ExtensionsDOMHandler::HandleShowButtonMessage));
234   web_ui_->RegisterMessageCallback("load",
235       NewCallback(this, &ExtensionsDOMHandler::HandleLoadMessage));
236   web_ui_->RegisterMessageCallback("pack",
237       NewCallback(this, &ExtensionsDOMHandler::HandlePackMessage));
238   web_ui_->RegisterMessageCallback("autoupdate",
239       NewCallback(this, &ExtensionsDOMHandler::HandleAutoUpdateMessage));
240   web_ui_->RegisterMessageCallback("selectFilePath",
241       NewCallback(this, &ExtensionsDOMHandler::HandleSelectFilePathMessage));
242 }
243 
HandleRequestExtensionsData(const ListValue * args)244 void ExtensionsDOMHandler::HandleRequestExtensionsData(const ListValue* args) {
245   DictionaryValue results;
246 
247   // Add the extensions to the results structure.
248   ListValue* extensions_list = new ListValue();
249 
250   const ExtensionList* extensions = extensions_service_->extensions();
251   for (ExtensionList::const_iterator extension = extensions->begin();
252        extension != extensions->end(); ++extension) {
253     if (ShouldShowExtension(*extension)) {
254       extensions_list->Append(CreateExtensionDetailValue(
255           extensions_service_.get(),
256           *extension,
257           GetActivePagesForExtension(*extension),
258           true, false));  // enabled, terminated
259     }
260   }
261   extensions = extensions_service_->disabled_extensions();
262   for (ExtensionList::const_iterator extension = extensions->begin();
263        extension != extensions->end(); ++extension) {
264     if (ShouldShowExtension(*extension)) {
265       extensions_list->Append(CreateExtensionDetailValue(
266           extensions_service_.get(),
267           *extension,
268           GetActivePagesForExtension(*extension),
269           false, false));  // enabled, terminated
270     }
271   }
272   extensions = extensions_service_->terminated_extensions();
273   std::vector<ExtensionPage> empty_pages;
274   for (ExtensionList::const_iterator extension = extensions->begin();
275        extension != extensions->end(); ++extension) {
276     if (ShouldShowExtension(*extension)) {
277       extensions_list->Append(CreateExtensionDetailValue(
278           extensions_service_.get(),
279           *extension,
280           empty_pages,  // Terminated process has no active pages.
281           false, true));  // enabled, terminated
282     }
283   }
284   results.Set("extensions", extensions_list);
285 
286   bool developer_mode = web_ui_->GetProfile()->GetPrefs()
287       ->GetBoolean(prefs::kExtensionsUIDeveloperMode);
288   results.SetBoolean("developerMode", developer_mode);
289 
290   web_ui_->CallJavascriptFunction("returnExtensionsData", results);
291 }
292 
RegisterForNotifications()293 void ExtensionsDOMHandler::RegisterForNotifications() {
294   // Register for notifications that we need to reload the page.
295   registrar_.Add(this, NotificationType::EXTENSION_LOADED,
296       NotificationService::AllSources());
297   registrar_.Add(this, NotificationType::EXTENSION_PROCESS_CREATED,
298       NotificationService::AllSources());
299   registrar_.Add(this, NotificationType::EXTENSION_UNLOADED,
300       NotificationService::AllSources());
301   registrar_.Add(this, NotificationType::EXTENSION_UPDATE_DISABLED,
302       NotificationService::AllSources());
303   registrar_.Add(this, NotificationType::EXTENSION_FUNCTION_DISPATCHER_CREATED,
304       NotificationService::AllSources());
305   registrar_.Add(this,
306       NotificationType::EXTENSION_FUNCTION_DISPATCHER_DESTROYED,
307       NotificationService::AllSources());
308   registrar_.Add(this,
309       NotificationType::NAV_ENTRY_COMMITTED,
310       NotificationService::AllSources());
311   registrar_.Add(this,
312       NotificationType::RENDER_VIEW_HOST_DELETED,
313       NotificationService::AllSources());
314   registrar_.Add(this,
315       NotificationType::BACKGROUND_CONTENTS_NAVIGATED,
316       NotificationService::AllSources());
317   registrar_.Add(this,
318       NotificationType::BACKGROUND_CONTENTS_DELETED,
319       NotificationService::AllSources());
320   registrar_.Add(this,
321       NotificationType::EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED,
322       NotificationService::AllSources());
323 }
324 
GetExtensionUninstallDialog()325 ExtensionUninstallDialog* ExtensionsDOMHandler::GetExtensionUninstallDialog() {
326   if (!extension_uninstall_dialog_.get()) {
327     extension_uninstall_dialog_.reset(
328         new ExtensionUninstallDialog(web_ui_->GetProfile()));
329   }
330   return extension_uninstall_dialog_.get();
331 }
332 
HandleToggleDeveloperMode(const ListValue * args)333 void ExtensionsDOMHandler::HandleToggleDeveloperMode(const ListValue* args) {
334   bool developer_mode = web_ui_->GetProfile()->GetPrefs()
335       ->GetBoolean(prefs::kExtensionsUIDeveloperMode);
336   web_ui_->GetProfile()->GetPrefs()->SetBoolean(
337       prefs::kExtensionsUIDeveloperMode, !developer_mode);
338 }
339 
HandleInspectMessage(const ListValue * args)340 void ExtensionsDOMHandler::HandleInspectMessage(const ListValue* args) {
341   std::string render_process_id_str;
342   std::string render_view_id_str;
343   int render_process_id;
344   int render_view_id;
345   CHECK(args->GetSize() == 2);
346   CHECK(args->GetString(0, &render_process_id_str));
347   CHECK(args->GetString(1, &render_view_id_str));
348   CHECK(base::StringToInt(render_process_id_str, &render_process_id));
349   CHECK(base::StringToInt(render_view_id_str, &render_view_id));
350   RenderViewHost* host = RenderViewHost::FromID(render_process_id,
351                                                 render_view_id);
352   if (!host) {
353     // This can happen if the host has gone away since the page was displayed.
354     return;
355   }
356 
357   DevToolsManager::GetInstance()->OpenDevToolsWindow(host);
358 }
359 
HandleReloadMessage(const ListValue * args)360 void ExtensionsDOMHandler::HandleReloadMessage(const ListValue* args) {
361   std::string extension_id = UTF16ToASCII(ExtractStringValue(args));
362   CHECK(!extension_id.empty());
363   extensions_service_->ReloadExtension(extension_id);
364 }
365 
HandleEnableMessage(const ListValue * args)366 void ExtensionsDOMHandler::HandleEnableMessage(const ListValue* args) {
367   CHECK(args->GetSize() == 2);
368   std::string extension_id, enable_str;
369   CHECK(args->GetString(0, &extension_id));
370   CHECK(args->GetString(1, &enable_str));
371   const Extension* extension =
372       extensions_service_->GetExtensionById(extension_id, true);
373   DCHECK(extension);
374   if (!Extension::UserMayDisable(extension->location())) {
375     LOG(ERROR) << "Attempt to enable an extension that is non-usermanagable was"
376                << "made. Extension id: " << extension->id();
377     return;
378   }
379 
380   if (enable_str == "true") {
381     ExtensionPrefs* prefs = extensions_service_->extension_prefs();
382     if (prefs->DidExtensionEscalatePermissions(extension_id)) {
383       ShowExtensionDisabledDialog(extensions_service_,
384                                   web_ui_->GetProfile(), extension);
385     } else {
386       extensions_service_->EnableExtension(extension_id);
387     }
388   } else {
389     extensions_service_->DisableExtension(extension_id);
390   }
391 }
392 
HandleEnableIncognitoMessage(const ListValue * args)393 void ExtensionsDOMHandler::HandleEnableIncognitoMessage(const ListValue* args) {
394   CHECK(args->GetSize() == 2);
395   std::string extension_id, enable_str;
396   CHECK(args->GetString(0, &extension_id));
397   CHECK(args->GetString(1, &enable_str));
398   const Extension* extension =
399       extensions_service_->GetExtensionById(extension_id, true);
400   DCHECK(extension);
401 
402   // Flipping the incognito bit will generate unload/load notifications for the
403   // extension, but we don't want to reload the page, because a) we've already
404   // updated the UI to reflect the change, and b) we want the yellow warning
405   // text to stay until the user has left the page.
406   //
407   // TODO(aa): This creates crapiness in some cases. For example, in a main
408   // window, when toggling this, the browser action will flicker because it gets
409   // unloaded, then reloaded. It would be better to have a dedicated
410   // notification for this case.
411   //
412   // Bug: http://crbug.com/41384
413   ignore_notifications_ = true;
414   extensions_service_->SetIsIncognitoEnabled(extension_id,
415                                              enable_str == "true");
416   ignore_notifications_ = false;
417 }
418 
HandleAllowFileAccessMessage(const ListValue * args)419 void ExtensionsDOMHandler::HandleAllowFileAccessMessage(const ListValue* args) {
420   CHECK(args->GetSize() == 2);
421   std::string extension_id, allow_str;
422   CHECK(args->GetString(0, &extension_id));
423   CHECK(args->GetString(1, &allow_str));
424   const Extension* extension =
425       extensions_service_->GetExtensionById(extension_id, true);
426   DCHECK(extension);
427   if (!Extension::UserMayDisable(extension->location())) {
428     LOG(ERROR) << "Attempt to change allow file access of an extension that is "
429                << "non-usermanagable was made. Extension id : "
430                << extension->id();
431     return;
432   }
433 
434   extensions_service_->SetAllowFileAccess(extension, allow_str == "true");
435 }
436 
HandleUninstallMessage(const ListValue * args)437 void ExtensionsDOMHandler::HandleUninstallMessage(const ListValue* args) {
438   std::string extension_id = UTF16ToASCII(ExtractStringValue(args));
439   CHECK(!extension_id.empty());
440   const Extension* extension =
441       extensions_service_->GetExtensionById(extension_id, true);
442   if (!extension)
443     extension = extensions_service_->GetTerminatedExtension(extension_id);
444   if (!extension)
445     return;
446 
447   if (!Extension::UserMayDisable(extension->location())) {
448     LOG(ERROR) << "Attempt to uninstall an extension that is non-usermanagable "
449                << "was made. Extension id : " << extension->id();
450     return;
451   }
452 
453   if (!extension_id_prompting_.empty())
454     return;  // Only one prompt at a time.
455 
456   extension_id_prompting_ = extension_id;
457 
458   GetExtensionUninstallDialog()->ConfirmUninstall(this, extension);
459 }
460 
ExtensionDialogAccepted()461 void ExtensionsDOMHandler::ExtensionDialogAccepted() {
462   DCHECK(!extension_id_prompting_.empty());
463 
464   bool was_terminated = false;
465 
466   // The extension can be uninstalled in another window while the UI was
467   // showing. Do nothing in that case.
468   const Extension* extension =
469       extensions_service_->GetExtensionById(extension_id_prompting_, true);
470   if (!extension) {
471     extension = extensions_service_->GetTerminatedExtension(
472         extension_id_prompting_);
473     was_terminated = true;
474   }
475   if (!extension)
476     return;
477 
478   extensions_service_->UninstallExtension(extension_id_prompting_,
479                                           false /* external_uninstall */, NULL);
480   extension_id_prompting_ = "";
481 
482   // There will be no EXTENSION_UNLOADED notification for terminated
483   // extensions as they were already unloaded.
484   if (was_terminated)
485     HandleRequestExtensionsData(NULL);
486 }
487 
ExtensionDialogCanceled()488 void ExtensionsDOMHandler::ExtensionDialogCanceled() {
489   extension_id_prompting_ = "";
490 }
491 
HandleOptionsMessage(const ListValue * args)492 void ExtensionsDOMHandler::HandleOptionsMessage(const ListValue* args) {
493   const Extension* extension = GetExtension(args);
494   if (!extension || extension->options_url().is_empty())
495     return;
496   web_ui_->GetProfile()->GetExtensionProcessManager()->OpenOptionsPage(
497       extension, NULL);
498 }
499 
HandleShowButtonMessage(const ListValue * args)500 void ExtensionsDOMHandler::HandleShowButtonMessage(const ListValue* args) {
501   const Extension* extension = GetExtension(args);
502   extensions_service_->SetBrowserActionVisibility(extension, true);
503 }
504 
HandleLoadMessage(const ListValue * args)505 void ExtensionsDOMHandler::HandleLoadMessage(const ListValue* args) {
506   FilePath::StringType string_path;
507   CHECK(args->GetSize() == 1) << args->GetSize();
508   CHECK(args->GetString(0, &string_path));
509   extensions_service_->LoadExtension(FilePath(string_path));
510 }
511 
ShowAlert(const std::string & message)512 void ExtensionsDOMHandler::ShowAlert(const std::string& message) {
513   ListValue arguments;
514   arguments.Append(Value::CreateStringValue(message));
515   web_ui_->CallJavascriptFunction("alert", arguments);
516 }
517 
HandlePackMessage(const ListValue * args)518 void ExtensionsDOMHandler::HandlePackMessage(const ListValue* args) {
519   std::string extension_path;
520   std::string private_key_path;
521   CHECK(args->GetSize() == 2);
522   CHECK(args->GetString(0, &extension_path));
523   CHECK(args->GetString(1, &private_key_path));
524 
525   FilePath root_directory =
526       FilePath::FromWStringHack(UTF8ToWide(extension_path));
527   FilePath key_file = FilePath::FromWStringHack(UTF8ToWide(private_key_path));
528 
529   if (root_directory.empty()) {
530     if (extension_path.empty()) {
531       ShowAlert(l10n_util::GetStringUTF8(
532           IDS_EXTENSION_PACK_DIALOG_ERROR_ROOT_REQUIRED));
533     } else {
534       ShowAlert(l10n_util::GetStringUTF8(
535           IDS_EXTENSION_PACK_DIALOG_ERROR_ROOT_INVALID));
536     }
537 
538     return;
539   }
540 
541   if (!private_key_path.empty() && key_file.empty()) {
542     ShowAlert(l10n_util::GetStringUTF8(
543         IDS_EXTENSION_PACK_DIALOG_ERROR_KEY_INVALID));
544     return;
545   }
546 
547   pack_job_ = new PackExtensionJob(this, root_directory, key_file);
548   pack_job_->Start();
549 }
550 
OnPackSuccess(const FilePath & crx_file,const FilePath & pem_file)551 void ExtensionsDOMHandler::OnPackSuccess(const FilePath& crx_file,
552                                          const FilePath& pem_file) {
553   ShowAlert(UTF16ToUTF8(PackExtensionJob::StandardSuccessMessage(crx_file,
554                                                                  pem_file)));
555 
556   ListValue results;
557   web_ui_->CallJavascriptFunction("hidePackDialog", results);
558 }
559 
OnPackFailure(const std::string & error)560 void ExtensionsDOMHandler::OnPackFailure(const std::string& error) {
561   ShowAlert(error);
562 }
563 
HandleAutoUpdateMessage(const ListValue * args)564 void ExtensionsDOMHandler::HandleAutoUpdateMessage(const ListValue* args) {
565   ExtensionUpdater* updater = extensions_service_->updater();
566   if (updater)
567     updater->CheckNow();
568 }
569 
HandleSelectFilePathMessage(const ListValue * args)570 void ExtensionsDOMHandler::HandleSelectFilePathMessage(const ListValue* args) {
571   std::string select_type;
572   std::string operation;
573   CHECK(args->GetSize() == 2);
574   CHECK(args->GetString(0, &select_type));
575   CHECK(args->GetString(1, &operation));
576 
577   SelectFileDialog::Type type = SelectFileDialog::SELECT_FOLDER;
578   static SelectFileDialog::FileTypeInfo info;
579   int file_type_index = 0;
580   if (select_type == "file")
581     type = SelectFileDialog::SELECT_OPEN_FILE;
582 
583   string16 select_title;
584   if (operation == "load") {
585     select_title = l10n_util::GetStringUTF16(IDS_EXTENSION_LOAD_FROM_DIRECTORY);
586   } else if (operation == "packRoot") {
587     select_title = l10n_util::GetStringUTF16(
588         IDS_EXTENSION_PACK_DIALOG_SELECT_ROOT);
589   } else if (operation == "pem") {
590     select_title = l10n_util::GetStringUTF16(
591         IDS_EXTENSION_PACK_DIALOG_SELECT_KEY);
592     info.extensions.push_back(std::vector<FilePath::StringType>());
593         info.extensions.front().push_back(FILE_PATH_LITERAL("pem"));
594         info.extension_description_overrides.push_back(
595             l10n_util::GetStringUTF16(
596                 IDS_EXTENSION_PACK_DIALOG_KEY_FILE_TYPE_DESCRIPTION));
597         info.include_all_files = true;
598     file_type_index = 1;
599   } else {
600     NOTREACHED();
601     return;
602   }
603 
604   load_extension_dialog_ = SelectFileDialog::Create(this);
605   load_extension_dialog_->SelectFile(type, select_title, FilePath(), &info,
606       file_type_index, FILE_PATH_LITERAL(""), web_ui_->tab_contents(),
607       web_ui_->tab_contents()->view()->GetTopLevelNativeWindow(), NULL);
608 }
609 
610 
FileSelected(const FilePath & path,int index,void * params)611 void ExtensionsDOMHandler::FileSelected(const FilePath& path, int index,
612                                         void* params) {
613   // Add the extensions to the results structure.
614   ListValue results;
615   results.Append(Value::CreateStringValue(path.value()));
616   web_ui_->CallJavascriptFunction("window.handleFilePathSelected", results);
617 }
618 
MultiFilesSelected(const std::vector<FilePath> & files,void * params)619 void ExtensionsDOMHandler::MultiFilesSelected(
620     const std::vector<FilePath>& files, void* params) {
621   NOTREACHED();
622 }
623 
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)624 void ExtensionsDOMHandler::Observe(NotificationType type,
625                                    const NotificationSource& source,
626                                    const NotificationDetails& details) {
627   switch (type.value) {
628     // We listen for notifications that will result in the page being
629     // repopulated with data twice for the same event in certain cases.
630     // For instance, EXTENSION_LOADED & EXTENSION_PROCESS_CREATED because
631     // we don't know about the views for an extension at EXTENSION_LOADED, but
632     // if we only listen to EXTENSION_PROCESS_CREATED, we'll miss extensions
633     // that don't have a process at startup. Similarly, NAV_ENTRY_COMMITTED &
634     // EXTENSION_FUNCTION_DISPATCHER_CREATED because we want to handle both
635     // the case of live app pages (which don't have an EFD) and
636     // chrome-extension:// urls which are served in a TabContents.
637     //
638     // Doing it this way gets everything but causes the page to be rendered
639     // more than we need. It doesn't seem to result in any noticeable flicker.
640     case NotificationType::RENDER_VIEW_HOST_DELETED:
641       deleting_rvh_ = Source<RenderViewHost>(source).ptr();
642       MaybeUpdateAfterNotification();
643       break;
644     case NotificationType::BACKGROUND_CONTENTS_DELETED:
645       deleting_rvh_ = Details<BackgroundContents>(details)->render_view_host();
646       MaybeUpdateAfterNotification();
647       break;
648     case NotificationType::EXTENSION_LOADED:
649     case NotificationType::EXTENSION_PROCESS_CREATED:
650     case NotificationType::EXTENSION_UNLOADED:
651     case NotificationType::EXTENSION_UPDATE_DISABLED:
652     case NotificationType::EXTENSION_FUNCTION_DISPATCHER_CREATED:
653     case NotificationType::EXTENSION_FUNCTION_DISPATCHER_DESTROYED:
654     case NotificationType::NAV_ENTRY_COMMITTED:
655     case NotificationType::BACKGROUND_CONTENTS_NAVIGATED:
656     case NotificationType::EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED:
657       MaybeUpdateAfterNotification();
658       break;
659     default:
660       NOTREACHED();
661   }
662 }
663 
GetExtension(const ListValue * args)664 const Extension* ExtensionsDOMHandler::GetExtension(const ListValue* args) {
665   std::string extension_id = UTF16ToASCII(ExtractStringValue(args));
666   CHECK(!extension_id.empty());
667   return extensions_service_->GetExtensionById(extension_id, true);
668 }
669 
MaybeUpdateAfterNotification()670 void ExtensionsDOMHandler::MaybeUpdateAfterNotification() {
671   if (!ignore_notifications_ &&
672       web_ui_->tab_contents() &&
673       web_ui_->tab_contents()->render_view_host()) {
674     HandleRequestExtensionsData(NULL);
675   }
676   deleting_rvh_ = NULL;
677 }
678 
679 // Static
CreateExtensionDetailValue(ExtensionService * service,const Extension * extension,const std::vector<ExtensionPage> & pages,bool enabled,bool terminated)680 DictionaryValue* ExtensionsDOMHandler::CreateExtensionDetailValue(
681     ExtensionService* service, const Extension* extension,
682     const std::vector<ExtensionPage>& pages, bool enabled, bool terminated) {
683   DictionaryValue* extension_data = new DictionaryValue();
684   GURL icon =
685       ExtensionIconSource::GetIconURL(extension,
686                                       Extension::EXTENSION_ICON_MEDIUM,
687                                       ExtensionIconSet::MATCH_BIGGER,
688                                       !enabled);
689   extension_data->SetString("id", extension->id());
690   extension_data->SetString("name", extension->name());
691   extension_data->SetString("description", extension->description());
692   if (extension->location() == Extension::LOAD)
693     extension_data->SetString("path", extension->path().value());
694   extension_data->SetString("version", extension->version()->GetString());
695   extension_data->SetString("icon", icon.spec());
696   extension_data->SetBoolean("isUnpacked",
697                              extension->location() == Extension::LOAD);
698   extension_data->SetBoolean("mayDisable",
699                              Extension::UserMayDisable(extension->location()));
700   extension_data->SetBoolean("enabled", enabled);
701   extension_data->SetBoolean("terminated", terminated);
702   extension_data->SetBoolean("enabledIncognito",
703       service ? service->IsIncognitoEnabled(extension->id()) : false);
704   extension_data->SetBoolean("wantsFileAccess", extension->wants_file_access());
705   extension_data->SetBoolean("allowFileAccess",
706       service ? service->AllowFileAccess(extension) : false);
707   extension_data->SetBoolean("allow_reload",
708                              extension->location() == Extension::LOAD);
709   extension_data->SetBoolean("is_hosted_app", extension->is_hosted_app());
710 
711   // Determine the sort order: Extensions loaded through --load-extensions show
712   // up at the top. Disabled extensions show up at the bottom.
713   if (extension->location() == Extension::LOAD)
714     extension_data->SetInteger("order", 1);
715   else
716     extension_data->SetInteger("order", 2);
717 
718   if (!extension->options_url().is_empty())
719     extension_data->SetString("options_url", extension->options_url().spec());
720 
721   if (service && !service->GetBrowserActionVisibility(extension))
722     extension_data->SetBoolean("enable_show_button", true);
723 
724   // Add views
725   ListValue* views = new ListValue;
726   for (std::vector<ExtensionPage>::const_iterator iter = pages.begin();
727        iter != pages.end(); ++iter) {
728     DictionaryValue* view_value = new DictionaryValue;
729     if (iter->url.scheme() == chrome::kExtensionScheme) {
730       // No leading slash.
731       view_value->SetString("path", iter->url.path().substr(1));
732     } else {
733       // For live pages, use the full URL.
734       view_value->SetString("path", iter->url.spec());
735     }
736     view_value->SetInteger("renderViewId", iter->render_view_id);
737     view_value->SetInteger("renderProcessId", iter->render_process_id);
738     view_value->SetBoolean("incognito", iter->incognito);
739     views->Append(view_value);
740   }
741   extension_data->Set("views", views);
742   extension_data->SetBoolean("hasPopupAction",
743       extension->browser_action() || extension->page_action());
744   extension_data->SetString("homepageUrl", extension->GetHomepageURL().spec());
745 
746   return extension_data;
747 }
748 
GetActivePagesForExtension(const Extension * extension)749 std::vector<ExtensionPage> ExtensionsDOMHandler::GetActivePagesForExtension(
750     const Extension* extension) {
751   std::vector<ExtensionPage> result;
752 
753   // Get the extension process's active views.
754   ExtensionProcessManager* process_manager =
755       extensions_service_->profile()->GetExtensionProcessManager();
756   GetActivePagesForExtensionProcess(
757       process_manager->GetExtensionProcess(extension->url()),
758       extension, &result);
759 
760   // Repeat for the incognito process, if applicable.
761   if (extensions_service_->profile()->HasOffTheRecordProfile() &&
762       extension->incognito_split_mode()) {
763     ExtensionProcessManager* process_manager =
764         extensions_service_->profile()->GetOffTheRecordProfile()->
765             GetExtensionProcessManager();
766     GetActivePagesForExtensionProcess(
767         process_manager->GetExtensionProcess(extension->url()),
768         extension, &result);
769   }
770 
771   return result;
772 }
773 
GetActivePagesForExtensionProcess(RenderProcessHost * process,const Extension * extension,std::vector<ExtensionPage> * result)774 void ExtensionsDOMHandler::GetActivePagesForExtensionProcess(
775     RenderProcessHost* process,
776     const Extension* extension,
777     std::vector<ExtensionPage> *result) {
778   if (!process)
779     return;
780 
781   RenderProcessHost::listeners_iterator iter = process->ListenersIterator();
782   for (; !iter.IsAtEnd(); iter.Advance()) {
783     const RenderWidgetHost* widget =
784         static_cast<const RenderWidgetHost*>(iter.GetCurrentValue());
785     DCHECK(widget);
786     if (!widget || !widget->IsRenderView())
787       continue;
788     const RenderViewHost* host = static_cast<const RenderViewHost*>(widget);
789     if (host == deleting_rvh_ ||
790         ViewType::EXTENSION_POPUP == host->delegate()->GetRenderViewType())
791       continue;
792 
793     GURL url = host->delegate()->GetURL();
794     if (url.SchemeIs(chrome::kExtensionScheme)) {
795       if (url.host() != extension->id())
796         continue;
797     } else if (!extension->web_extent().ContainsURL(url)) {
798       continue;
799     }
800 
801     result->push_back(ExtensionPage(url, process->id(), host->routing_id(),
802                                     process->profile()->IsOffTheRecord()));
803   }
804 }
805 
~ExtensionsDOMHandler()806 ExtensionsDOMHandler::~ExtensionsDOMHandler() {
807   // There may be pending file dialogs, we need to tell them that we've gone
808   // away so they don't try and call back to us.
809   if (load_extension_dialog_.get())
810     load_extension_dialog_->ListenerDestroyed();
811 
812   if (pack_job_.get())
813     pack_job_->ClearClient();
814 
815   registrar_.RemoveAll();
816 }
817 
818 // ExtensionsDOMHandler, public: -----------------------------------------------
819 
ExtensionsUI(TabContents * contents)820 ExtensionsUI::ExtensionsUI(TabContents* contents) : WebUI(contents) {
821   ExtensionService *exstension_service =
822       GetProfile()->GetOriginalProfile()->GetExtensionService();
823 
824   ExtensionsDOMHandler* handler = new ExtensionsDOMHandler(exstension_service);
825   AddMessageHandler(handler);
826   handler->Attach(this);
827 
828   ExtensionsUIHTMLSource* html_source = new ExtensionsUIHTMLSource();
829 
830   // Set up the chrome://extensions/ source.
831   contents->profile()->GetChromeURLDataManager()->AddDataSource(html_source);
832 }
833 
834 // static
GetFaviconResourceBytes()835 RefCountedMemory* ExtensionsUI::GetFaviconResourceBytes() {
836   return ResourceBundle::GetSharedInstance().
837       LoadDataResourceBytes(IDR_PLUGIN);
838 }
839 
840 // static
RegisterUserPrefs(PrefService * prefs)841 void ExtensionsUI::RegisterUserPrefs(PrefService* prefs) {
842   prefs->RegisterBooleanPref(prefs::kExtensionsUIDeveloperMode, false);
843 }
844