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