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