OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "chrome/browser/ui/webui/options2/extension_settings_handler.h" | |
6 | |
7 #include "base/auto_reset.h" | |
8 #include "base/base64.h" | |
9 #include "base/bind.h" | |
10 #include "base/bind_helpers.h" | |
11 #include "base/command_line.h" | |
12 #include "base/file_util.h" | |
13 #include "base/string_number_conversions.h" | |
14 #include "base/utf_string_conversions.h" | |
15 #include "base/values.h" | |
16 #include "base/version.h" | |
17 #include "chrome/browser/debugger/devtools_window.h" | |
18 #include "chrome/browser/extensions/crx_installer.h" | |
19 #include "chrome/browser/extensions/extension_disabled_infobar_delegate.h" | |
20 #include "chrome/browser/extensions/extension_service.h" | |
21 #include "chrome/browser/extensions/extension_updater.h" | |
22 #include "chrome/browser/extensions/extension_warning_set.h" | |
23 #include "chrome/browser/extensions/unpacked_installer.h" | |
24 #include "chrome/browser/google/google_util.h" | |
25 #include "chrome/browser/profiles/profile.h" | |
26 #include "chrome/browser/tab_contents/background_contents.h" | |
27 #include "chrome/browser/ui/webui/extension_icon_source.h" | |
28 #include "chrome/common/chrome_switches.h" | |
29 #include "chrome/common/chrome_view_type.h" | |
30 #include "chrome/common/extensions/extension.h" | |
31 #include "chrome/common/pref_names.h" | |
32 #include "chrome/common/url_constants.h" | |
33 #include "content/browser/browsing_instance.h" | |
34 #include "content/browser/renderer_host/render_view_host.h" | |
35 #include "content/browser/tab_contents/tab_contents.h" | |
36 #include "content/browser/tab_contents/tab_contents_view.h" | |
37 #include "content/public/browser/notification_service.h" | |
38 #include "content/public/browser/notification_types.h" | |
39 #include "grit/browser_resources.h" | |
40 #include "grit/chromium_strings.h" | |
41 #include "grit/generated_resources.h" | |
42 #include "grit/theme_resources.h" | |
43 #include "ui/base/l10n/l10n_util.h" | |
44 #include "ui/base/resource/resource_bundle.h" | |
45 | |
46 namespace { | |
47 | |
48 bool ShouldShowExtension(const Extension* extension) { | |
49 // Don't show themes since this page's UI isn't really useful for themes. | |
50 if (extension->is_theme()) | |
51 return false; | |
52 | |
53 // Don't show component extensions because they are only extensions as an | |
54 // implementation detail of Chrome. | |
55 if (extension->location() == Extension::COMPONENT && | |
56 !CommandLine::ForCurrentProcess()->HasSwitch( | |
57 switches::kShowComponentExtensionOptions)) | |
58 return false; | |
59 | |
60 // Always show unpacked extensions and apps. | |
61 if (extension->location() == Extension::LOAD) | |
62 return true; | |
63 | |
64 // Unless they are unpacked, never show hosted apps. | |
65 if (extension->is_hosted_app()) | |
66 return false; | |
67 | |
68 return true; | |
69 } | |
70 | |
71 } // namespace | |
72 | |
73 /////////////////////////////////////////////////////////////////////////////// | |
74 // | |
75 // ExtensionSettingsHandler | |
76 // | |
77 /////////////////////////////////////////////////////////////////////////////// | |
78 | |
79 ExtensionSettingsHandler::ExtensionSettingsHandler() | |
80 : extension_service_(NULL), | |
81 ignore_notifications_(false), | |
82 deleting_rvh_(NULL), | |
83 registered_for_notifications_(false) { | |
84 } | |
85 | |
86 ExtensionSettingsHandler::~ExtensionSettingsHandler() { | |
87 // There may be pending file dialogs, we need to tell them that we've gone | |
88 // away so they don't try and call back to us. | |
89 if (load_extension_dialog_.get()) | |
90 load_extension_dialog_->ListenerDestroyed(); | |
91 | |
92 registrar_.RemoveAll(); | |
93 } | |
94 | |
95 // static | |
96 void ExtensionSettingsHandler::RegisterUserPrefs(PrefService* prefs) { | |
97 prefs->RegisterBooleanPref(prefs::kExtensionsUIDeveloperMode, | |
98 false, | |
99 PrefService::SYNCABLE_PREF); | |
100 } | |
101 | |
102 void ExtensionSettingsHandler::RegisterMessages() { | |
103 web_ui_->RegisterMessageCallback("extensionSettingsRequestExtensionsData", | |
104 base::Bind(&ExtensionSettingsHandler::HandleRequestExtensionsData, | |
105 base::Unretained(this))); | |
106 web_ui_->RegisterMessageCallback("extensionSettingsToggleDeveloperMode", | |
107 base::Bind(&ExtensionSettingsHandler::HandleToggleDeveloperMode, | |
108 base::Unretained(this))); | |
109 web_ui_->RegisterMessageCallback("extensionSettingsInspect", | |
110 base::Bind(&ExtensionSettingsHandler::HandleInspectMessage, | |
111 base::Unretained(this))); | |
112 web_ui_->RegisterMessageCallback("extensionSettingsReload", | |
113 base::Bind(&ExtensionSettingsHandler::HandleReloadMessage, | |
114 base::Unretained(this))); | |
115 web_ui_->RegisterMessageCallback("extensionSettingsEnable", | |
116 base::Bind(&ExtensionSettingsHandler::HandleEnableMessage, | |
117 base::Unretained(this))); | |
118 web_ui_->RegisterMessageCallback("extensionSettingsEnableIncognito", | |
119 base::Bind(&ExtensionSettingsHandler::HandleEnableIncognitoMessage, | |
120 base::Unretained(this))); | |
121 web_ui_->RegisterMessageCallback("extensionSettingsAllowFileAccess", | |
122 base::Bind(&ExtensionSettingsHandler::HandleAllowFileAccessMessage, | |
123 base::Unretained(this))); | |
124 web_ui_->RegisterMessageCallback("extensionSettingsUninstall", | |
125 base::Bind(&ExtensionSettingsHandler::HandleUninstallMessage, | |
126 base::Unretained(this))); | |
127 web_ui_->RegisterMessageCallback("extensionSettingsOptions", | |
128 base::Bind(&ExtensionSettingsHandler::HandleOptionsMessage, | |
129 base::Unretained(this))); | |
130 web_ui_->RegisterMessageCallback("extensionSettingsShowButton", | |
131 base::Bind(&ExtensionSettingsHandler::HandleShowButtonMessage, | |
132 base::Unretained(this))); | |
133 web_ui_->RegisterMessageCallback("extensionSettingsLoad", | |
134 base::Bind(&ExtensionSettingsHandler::HandleLoadMessage, | |
135 base::Unretained(this))); | |
136 web_ui_->RegisterMessageCallback("extensionSettingsAutoupdate", | |
137 base::Bind(&ExtensionSettingsHandler::HandleAutoUpdateMessage, | |
138 base::Unretained(this))); | |
139 web_ui_->RegisterMessageCallback("extensionSettingsSelectFilePath", | |
140 base::Bind(&ExtensionSettingsHandler::HandleSelectFilePathMessage, | |
141 base::Unretained(this))); | |
142 } | |
143 | |
144 void ExtensionSettingsHandler::HandleRequestExtensionsData( | |
145 const ListValue* args) { | |
146 DictionaryValue results; | |
147 | |
148 // Add the extensions to the results structure. | |
149 ListValue *extensions_list = new ListValue(); | |
150 | |
151 ExtensionWarningSet* warnings = extension_service_->extension_warnings(); | |
152 | |
153 const ExtensionSet* extensions = extension_service_->extensions(); | |
154 for (ExtensionSet::const_iterator extension = extensions->begin(); | |
155 extension != extensions->end(); ++extension) { | |
156 if (ShouldShowExtension(*extension)) { | |
157 extensions_list->Append(CreateExtensionDetailValue( | |
158 extension_service_, | |
159 *extension, | |
160 GetActivePagesForExtension(*extension), | |
161 warnings, | |
162 true, false)); // enabled, terminated | |
163 } | |
164 } | |
165 extensions = extension_service_->disabled_extensions(); | |
166 for (ExtensionSet::const_iterator extension = extensions->begin(); | |
167 extension != extensions->end(); ++extension) { | |
168 if (ShouldShowExtension(*extension)) { | |
169 extensions_list->Append(CreateExtensionDetailValue( | |
170 extension_service_, | |
171 *extension, | |
172 GetActivePagesForExtension(*extension), | |
173 warnings, | |
174 false, false)); // enabled, terminated | |
175 } | |
176 } | |
177 extensions = extension_service_->terminated_extensions(); | |
178 std::vector<ExtensionPage> empty_pages; | |
179 for (ExtensionSet::const_iterator extension = extensions->begin(); | |
180 extension != extensions->end(); ++extension) { | |
181 if (ShouldShowExtension(*extension)) { | |
182 extensions_list->Append(CreateExtensionDetailValue( | |
183 extension_service_, | |
184 *extension, | |
185 empty_pages, // Terminated process has no active pages. | |
186 warnings, | |
187 false, true)); // enabled, terminated | |
188 } | |
189 } | |
190 results.Set("extensions", extensions_list); | |
191 | |
192 Profile* profile = Profile::FromWebUI(web_ui_); | |
193 bool developer_mode = | |
194 profile->GetPrefs()->GetBoolean(prefs::kExtensionsUIDeveloperMode); | |
195 results.SetBoolean("developerMode", developer_mode); | |
196 | |
197 web_ui_->CallJavascriptFunction("ExtensionSettings.returnExtensionsData", | |
198 results); | |
199 | |
200 MaybeRegisterForNotifications(); | |
201 } | |
202 | |
203 void ExtensionSettingsHandler::MaybeRegisterForNotifications() { | |
204 if (registered_for_notifications_) | |
205 return; | |
206 | |
207 registered_for_notifications_ = true; | |
208 Profile* profile = Profile::FromWebUI(web_ui_); | |
209 | |
210 // Register for notifications that we need to reload the page. | |
211 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED, | |
212 content::Source<Profile>(profile)); | |
213 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED, | |
214 content::Source<Profile>(profile)); | |
215 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UPDATE_DISABLED, | |
216 content::Source<Profile>(profile)); | |
217 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_WARNING_CHANGED, | |
218 content::Source<Profile>(profile)); | |
219 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_CREATED, | |
220 content::NotificationService::AllBrowserContextsAndSources()); | |
221 registrar_.Add(this, | |
222 content::NOTIFICATION_RENDER_VIEW_HOST_CREATED, | |
223 content::NotificationService::AllBrowserContextsAndSources()); | |
224 registrar_.Add(this, | |
225 content::NOTIFICATION_RENDER_VIEW_HOST_DELETED, | |
226 content::NotificationService::AllBrowserContextsAndSources()); | |
227 registrar_.Add(this, | |
228 chrome::NOTIFICATION_BACKGROUND_CONTENTS_NAVIGATED, | |
229 content::NotificationService::AllBrowserContextsAndSources()); | |
230 registrar_.Add(this, | |
231 chrome::NOTIFICATION_BACKGROUND_CONTENTS_DELETED, | |
232 content::NotificationService::AllBrowserContextsAndSources()); | |
233 registrar_.Add( | |
234 this, | |
235 chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED, | |
236 content::Source<ExtensionPrefs>(profile->GetExtensionService()-> | |
237 extension_prefs())); | |
238 } | |
239 | |
240 ExtensionUninstallDialog* | |
241 ExtensionSettingsHandler::GetExtensionUninstallDialog() { | |
242 if (!extension_uninstall_dialog_.get()) { | |
243 extension_uninstall_dialog_.reset( | |
244 ExtensionUninstallDialog::Create(Profile::FromWebUI(web_ui_), this)); | |
245 } | |
246 return extension_uninstall_dialog_.get(); | |
247 } | |
248 | |
249 void ExtensionSettingsHandler::HandleToggleDeveloperMode( | |
250 const ListValue* args) { | |
251 Profile* profile = Profile::FromWebUI(web_ui_); | |
252 bool developer_mode = | |
253 profile->GetPrefs()->GetBoolean(prefs::kExtensionsUIDeveloperMode); | |
254 profile->GetPrefs()->SetBoolean( | |
255 prefs::kExtensionsUIDeveloperMode, !developer_mode); | |
256 HandleRequestExtensionsData(NULL); | |
257 } | |
258 | |
259 void ExtensionSettingsHandler::HandleInspectMessage(const ListValue* args) { | |
260 std::string render_process_id_str; | |
261 std::string render_view_id_str; | |
262 int render_process_id; | |
263 int render_view_id; | |
264 CHECK_EQ(2U, args->GetSize()); | |
265 CHECK(args->GetString(0, &render_process_id_str)); | |
266 CHECK(args->GetString(1, &render_view_id_str)); | |
267 CHECK(base::StringToInt(render_process_id_str, &render_process_id)); | |
268 CHECK(base::StringToInt(render_view_id_str, &render_view_id)); | |
269 RenderViewHost* host = RenderViewHost::FromID(render_process_id, | |
270 render_view_id); | |
271 if (!host) { | |
272 // This can happen if the host has gone away since the page was displayed. | |
273 return; | |
274 } | |
275 | |
276 DevToolsWindow::OpenDevToolsWindow(host); | |
277 } | |
278 | |
279 void ExtensionSettingsHandler::HandleReloadMessage(const ListValue* args) { | |
280 std::string extension_id = UTF16ToUTF8(ExtractStringValue(args)); | |
281 CHECK(!extension_id.empty()); | |
282 extension_service_->ReloadExtension(extension_id); | |
283 } | |
284 | |
285 void ExtensionSettingsHandler::HandleEnableMessage(const ListValue* args) { | |
286 CHECK_EQ(2U, args->GetSize()); | |
287 std::string extension_id, enable_str; | |
288 CHECK(args->GetString(0, &extension_id)); | |
289 CHECK(args->GetString(1, &enable_str)); | |
290 | |
291 const Extension* extension = | |
292 extension_service_->GetExtensionById(extension_id, true); | |
293 if (!Extension::UserMayDisable(extension->location())) { | |
294 LOG(ERROR) << "Attempt to enable an extension that is non-usermanagable was" | |
295 << "made. Extension id: " << extension->id(); | |
296 return; | |
297 } | |
298 | |
299 if (enable_str == "true") { | |
300 ExtensionPrefs* prefs = extension_service_->extension_prefs(); | |
301 if (prefs->DidExtensionEscalatePermissions(extension_id)) { | |
302 ShowExtensionDisabledDialog(extension_service_, | |
303 Profile::FromWebUI(web_ui_), extension); | |
304 } else { | |
305 extension_service_->EnableExtension(extension_id); | |
306 } | |
307 } else { | |
308 extension_service_->DisableExtension(extension_id); | |
309 } | |
310 } | |
311 | |
312 void ExtensionSettingsHandler::HandleEnableIncognitoMessage( | |
313 const ListValue* args) { | |
314 CHECK_EQ(2U, args->GetSize()); | |
315 std::string extension_id, enable_str; | |
316 CHECK(args->GetString(0, &extension_id)); | |
317 CHECK(args->GetString(1, &enable_str)); | |
318 const Extension* extension = | |
319 extension_service_->GetExtensionById(extension_id, true); | |
320 DCHECK(extension); | |
321 | |
322 // Flipping the incognito bit will generate unload/load notifications for the | |
323 // extension, but we don't want to reload the page, because a) we've already | |
324 // updated the UI to reflect the change, and b) we want the yellow warning | |
325 // text to stay until the user has left the page. | |
326 // | |
327 // TODO(aa): This creates crapiness in some cases. For example, in a main | |
328 // window, when toggling this, the browser action will flicker because it gets | |
329 // unloaded, then reloaded. It would be better to have a dedicated | |
330 // notification for this case. | |
331 // | |
332 // Bug: http://crbug.com/41384 | |
333 AutoReset<bool> auto_reset_ignore_notifications(&ignore_notifications_, true); | |
334 extension_service_->SetIsIncognitoEnabled(extension->id(), | |
335 enable_str == "true"); | |
336 } | |
337 | |
338 void ExtensionSettingsHandler::HandleAllowFileAccessMessage( | |
339 const ListValue* args) { | |
340 CHECK_EQ(2U, args->GetSize()); | |
341 std::string extension_id, allow_str; | |
342 CHECK(args->GetString(0, &extension_id)); | |
343 CHECK(args->GetString(1, &allow_str)); | |
344 const Extension* extension = | |
345 extension_service_->GetExtensionById(extension_id, true); | |
346 DCHECK(extension); | |
347 | |
348 if (!Extension::UserMayDisable(extension->location())) { | |
349 LOG(ERROR) << "Attempt to change allow file access of an extension that is " | |
350 << "non-usermanagable was made. Extension id : " | |
351 << extension->id(); | |
352 return; | |
353 } | |
354 | |
355 extension_service_->SetAllowFileAccess(extension, allow_str == "true"); | |
356 } | |
357 | |
358 void ExtensionSettingsHandler::HandleUninstallMessage(const ListValue* args) { | |
359 std::string extension_id = UTF16ToUTF8(ExtractStringValue(args)); | |
360 CHECK(!extension_id.empty()); | |
361 const Extension* extension = | |
362 extension_service_->GetExtensionById(extension_id, true); | |
363 if (!extension) | |
364 extension = extension_service_->GetTerminatedExtension(extension_id); | |
365 if (!extension) | |
366 return; | |
367 | |
368 if (!Extension::UserMayDisable(extension->location())) { | |
369 LOG(ERROR) << "Attempt to uninstall an extension that is non-usermanagable " | |
370 << "was made. Extension id : " << extension->id(); | |
371 return; | |
372 } | |
373 | |
374 if (!extension_id_prompting_.empty()) | |
375 return; // Only one prompt at a time. | |
376 | |
377 extension_id_prompting_ = extension_id; | |
378 | |
379 GetExtensionUninstallDialog()->ConfirmUninstall(extension); | |
380 } | |
381 | |
382 void ExtensionSettingsHandler::ExtensionUninstallAccepted() { | |
383 DCHECK(!extension_id_prompting_.empty()); | |
384 | |
385 bool was_terminated = false; | |
386 | |
387 // The extension can be uninstalled in another window while the UI was | |
388 // showing. Do nothing in that case. | |
389 const Extension* extension = | |
390 extension_service_->GetExtensionById(extension_id_prompting_, true); | |
391 if (!extension) { | |
392 extension = extension_service_->GetTerminatedExtension( | |
393 extension_id_prompting_); | |
394 was_terminated = true; | |
395 } | |
396 if (!extension) | |
397 return; | |
398 | |
399 extension_service_->UninstallExtension(extension_id_prompting_, | |
400 false, // External uninstall. | |
401 NULL); // Error. | |
402 extension_id_prompting_ = ""; | |
403 | |
404 // There will be no EXTENSION_UNLOADED notification for terminated | |
405 // extensions as they were already unloaded. | |
406 if (was_terminated) | |
407 HandleRequestExtensionsData(NULL); | |
408 } | |
409 | |
410 void ExtensionSettingsHandler::ExtensionUninstallCanceled() { | |
411 extension_id_prompting_ = ""; | |
412 } | |
413 | |
414 void ExtensionSettingsHandler::HandleOptionsMessage(const ListValue* args) { | |
415 const Extension* extension = GetExtension(args); | |
416 if (!extension || extension->options_url().is_empty()) | |
417 return; | |
418 Profile::FromWebUI(web_ui_)->GetExtensionProcessManager()->OpenOptionsPage( | |
419 extension, NULL); | |
420 } | |
421 | |
422 void ExtensionSettingsHandler::HandleShowButtonMessage(const ListValue* args) { | |
423 const Extension* extension = GetExtension(args); | |
424 extension_service_->SetBrowserActionVisibility(extension, true); | |
425 } | |
426 | |
427 void ExtensionSettingsHandler::HandleLoadMessage(const ListValue* args) { | |
428 FilePath::StringType string_path; | |
429 CHECK_EQ(1U, args->GetSize()) << args->GetSize(); | |
430 CHECK(args->GetString(0, &string_path)); | |
431 extensions::UnpackedInstaller::Create(extension_service_)-> | |
432 Load(FilePath(string_path)); | |
433 } | |
434 | |
435 void ExtensionSettingsHandler::ShowAlert(const std::string& message) { | |
436 ListValue arguments; | |
437 arguments.Append(Value::CreateStringValue(message)); | |
438 web_ui_->CallJavascriptFunction("alert", arguments); | |
439 } | |
440 | |
441 void ExtensionSettingsHandler::HandleAutoUpdateMessage(const ListValue* args) { | |
442 ExtensionUpdater* updater = extension_service_->updater(); | |
443 if (updater) | |
444 updater->CheckNow(); | |
445 } | |
446 | |
447 void ExtensionSettingsHandler::HandleSelectFilePathMessage( | |
448 const ListValue* args) { | |
449 std::string select_type; | |
450 std::string operation; | |
451 CHECK_EQ(2U, args->GetSize()); | |
452 CHECK(args->GetString(0, &select_type)); | |
453 CHECK(args->GetString(1, &operation)); | |
454 | |
455 SelectFileDialog::Type type = SelectFileDialog::SELECT_FOLDER; | |
456 SelectFileDialog::FileTypeInfo info; | |
457 int file_type_index = 0; | |
458 if (select_type == "file") | |
459 type = SelectFileDialog::SELECT_OPEN_FILE; | |
460 | |
461 string16 select_title; | |
462 if (operation == "load") { | |
463 select_title = l10n_util::GetStringUTF16(IDS_EXTENSION_LOAD_FROM_DIRECTORY); | |
464 } else if (operation == "packRoot") { | |
465 select_title = l10n_util::GetStringUTF16( | |
466 IDS_EXTENSION_PACK_DIALOG_SELECT_ROOT); | |
467 } else if (operation == "pem") { | |
468 select_title = l10n_util::GetStringUTF16( | |
469 IDS_EXTENSION_PACK_DIALOG_SELECT_KEY); | |
470 info.extensions.push_back(std::vector<FilePath::StringType>()); | |
471 info.extensions.front().push_back(FILE_PATH_LITERAL("pem")); | |
472 info.extension_description_overrides.push_back( | |
473 l10n_util::GetStringUTF16( | |
474 IDS_EXTENSION_PACK_DIALOG_KEY_FILE_TYPE_DESCRIPTION)); | |
475 info.include_all_files = true; | |
476 file_type_index = 1; | |
477 } else { | |
478 NOTREACHED(); | |
479 return; | |
480 } | |
481 | |
482 load_extension_dialog_ = SelectFileDialog::Create(this); | |
483 load_extension_dialog_->SelectFile(type, select_title, FilePath(), &info, | |
484 file_type_index, FILE_PATH_LITERAL(""), web_ui_->tab_contents(), | |
485 web_ui_->tab_contents()->view()->GetTopLevelNativeWindow(), NULL); | |
486 } | |
487 | |
488 | |
489 void ExtensionSettingsHandler::FileSelected(const FilePath& path, int index, | |
490 void* params) { | |
491 // Add the extensions to the results structure. | |
492 ListValue results; | |
493 results.Append(Value::CreateStringValue(path.value())); | |
494 web_ui_->CallJavascriptFunction("window.handleFilePathSelected", results); | |
495 } | |
496 | |
497 void ExtensionSettingsHandler::MultiFilesSelected( | |
498 const std::vector<FilePath>& files, void* params) { | |
499 NOTREACHED(); | |
500 } | |
501 | |
502 void ExtensionSettingsHandler::GetLocalizedValues( | |
503 DictionaryValue* localized_strings) { | |
504 DCHECK(localized_strings); | |
505 | |
506 RegisterTitle(localized_strings, "extensionSettings", | |
507 IDS_MANAGE_EXTENSIONS_SETTING_WINDOWS_TITLE); | |
508 | |
509 localized_strings->SetString("extensionSettingsVisitWebsite", | |
510 l10n_util::GetStringUTF16(IDS_EXTENSIONS_VISIT_WEBSITE)); | |
511 | |
512 localized_strings->SetString("extensionSettingsDeveloperMode", | |
513 l10n_util::GetStringUTF16(IDS_EXTENSIONS_DEVELOPER_MODE_LINK)); | |
514 localized_strings->SetString("extensionSettingsNoExtensions", | |
515 l10n_util::GetStringUTF16(IDS_EXTENSIONS_NONE_INSTALLED)); | |
516 localized_strings->SetString("extensionSettingsSuggestGallery", | |
517 l10n_util::GetStringFUTF16(IDS_EXTENSIONS_NONE_INSTALLED_SUGGEST_GALLERY, | |
518 ASCIIToUTF16("<a href='") + | |
519 ASCIIToUTF16(google_util::AppendGoogleLocaleParam( | |
520 GURL(extension_urls::GetWebstoreLaunchURL())).spec()) + | |
521 ASCIIToUTF16("'>"), | |
522 ASCIIToUTF16("</a>"))); | |
523 localized_strings->SetString("extensionSettingsGetMoreExtensions", | |
524 ASCIIToUTF16("<a href='") + | |
525 ASCIIToUTF16(google_util::AppendGoogleLocaleParam( | |
526 GURL(extension_urls::GetWebstoreLaunchURL())).spec()) + | |
527 ASCIIToUTF16("'>") + | |
528 l10n_util::GetStringUTF16(IDS_GET_MORE_EXTENSIONS) + | |
529 ASCIIToUTF16("</a>")); | |
530 localized_strings->SetString("extensionSettingsExtensionId", | |
531 l10n_util::GetStringUTF16(IDS_EXTENSIONS_ID)); | |
532 localized_strings->SetString("extensionSettingsExtensionPath", | |
533 l10n_util::GetStringUTF16(IDS_EXTENSIONS_PATH)); | |
534 localized_strings->SetString("extensionSettingsInspectViews", | |
535 l10n_util::GetStringUTF16(IDS_EXTENSIONS_INSPECT_VIEWS)); | |
536 localized_strings->SetString("viewIncognito", | |
537 l10n_util::GetStringUTF16(IDS_EXTENSIONS_VIEW_INCOGNITO)); | |
538 localized_strings->SetString("extensionSettingsEnable", | |
539 l10n_util::GetStringUTF16(IDS_EXTENSIONS_ENABLE)); | |
540 localized_strings->SetString("extensionSettingsEnabled", | |
541 l10n_util::GetStringUTF16(IDS_EXTENSIONS_ENABLED)); | |
542 localized_strings->SetString("extensionSettingsRemove", | |
543 l10n_util::GetStringUTF16(IDS_EXTENSIONS_REMOVE)); | |
544 localized_strings->SetString("extensionSettingsEnableIncognito", | |
545 l10n_util::GetStringUTF16(IDS_EXTENSIONS_ENABLE_INCOGNITO)); | |
546 localized_strings->SetString("extensionSettingsAllowFileAccess", | |
547 l10n_util::GetStringUTF16(IDS_EXTENSIONS_ALLOW_FILE_ACCESS)); | |
548 localized_strings->SetString("extensionSettingsIncognitoWarning", | |
549 l10n_util::GetStringFUTF16(IDS_EXTENSIONS_INCOGNITO_WARNING, | |
550 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME))); | |
551 localized_strings->SetString("extensionSettingsReload", | |
552 l10n_util::GetStringUTF16(IDS_EXTENSIONS_RELOAD)); | |
553 localized_strings->SetString("extensionSettingsOptions", | |
554 l10n_util::GetStringUTF16(IDS_EXTENSIONS_OPTIONS)); | |
555 localized_strings->SetString("extensionSettingsPolicyControlled", | |
556 l10n_util::GetStringUTF16(IDS_EXTENSIONS_POLICY_CONTROLLED)); | |
557 localized_strings->SetString("extensionSettingsShowButton", | |
558 l10n_util::GetStringUTF16(IDS_EXTENSIONS_SHOW_BUTTON)); | |
559 localized_strings->SetString("extensionSettingsLoadUnpackedButton", | |
560 l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOAD_UNPACKED_BUTTON)); | |
561 localized_strings->SetString("extensionSettingsPackButton", | |
562 l10n_util::GetStringUTF16(IDS_EXTENSIONS_PACK_BUTTON)); | |
563 localized_strings->SetString("extensionSettingsUpdateButton", | |
564 l10n_util::GetStringUTF16(IDS_EXTENSIONS_UPDATE_BUTTON)); | |
565 localized_strings->SetString("extensionSettingsCrashMessage", | |
566 l10n_util::GetStringUTF16(IDS_EXTENSIONS_CRASHED_EXTENSION)); | |
567 localized_strings->SetString("extensionSettingsInDevelopment", | |
568 l10n_util::GetStringUTF16(IDS_EXTENSIONS_IN_DEVELOPMENT)); | |
569 localized_strings->SetString("extensionSettingsWarningsTitle", | |
570 l10n_util::GetStringUTF16(IDS_EXTENSION_WARNINGS_TITLE)); | |
571 localized_strings->SetString("extensionSettingsShowDetails", | |
572 l10n_util::GetStringUTF16(IDS_EXTENSIONS_SHOW_DETAILS)); | |
573 localized_strings->SetString("extensionSettingsHideDetails", | |
574 l10n_util::GetStringUTF16(IDS_EXTENSIONS_HIDE_DETAILS)); | |
575 } | |
576 | |
577 void ExtensionSettingsHandler::Initialize() { | |
578 } | |
579 | |
580 WebUIMessageHandler* ExtensionSettingsHandler::Attach(WebUI* web_ui) { | |
581 // Call through to superclass. | |
582 WebUIMessageHandler* handler = OptionsPage2UIHandler::Attach(web_ui); | |
583 | |
584 extension_service_ = Profile::FromWebUI(web_ui_) | |
585 ->GetOriginalProfile()->GetExtensionService(); | |
586 | |
587 // Return result from the superclass. | |
588 return handler; | |
589 } | |
590 | |
591 void ExtensionSettingsHandler::Observe( | |
592 int type, | |
593 const content::NotificationSource& source, | |
594 const content::NotificationDetails& details) { | |
595 Profile* profile = Profile::FromWebUI(web_ui_); | |
596 Profile* source_profile = NULL; | |
597 switch (type) { | |
598 // We listen for notifications that will result in the page being | |
599 // repopulated with data twice for the same event in certain cases. | |
600 // For instance, EXTENSION_LOADED & EXTENSION_HOST_CREATED because | |
601 // we don't know about the views for an extension at EXTENSION_LOADED, but | |
602 // if we only listen to EXTENSION_HOST_CREATED, we'll miss extensions | |
603 // that don't have a process at startup. | |
604 // | |
605 // Doing it this way gets everything but causes the page to be rendered | |
606 // more than we need. It doesn't seem to result in any noticeable flicker. | |
607 case content::NOTIFICATION_RENDER_VIEW_HOST_DELETED: | |
608 deleting_rvh_ = content::Source<RenderViewHost>(source).ptr(); | |
609 // Fall through. | |
610 case content::NOTIFICATION_RENDER_VIEW_HOST_CREATED: | |
611 source_profile = Profile::FromBrowserContext( | |
612 content::Source<RenderViewHost>(source)->site_instance()-> | |
613 browsing_instance()->browser_context()); | |
614 if (!profile->IsSameProfile(source_profile)) | |
615 return; | |
616 MaybeUpdateAfterNotification(); | |
617 break; | |
618 case chrome::NOTIFICATION_BACKGROUND_CONTENTS_DELETED: | |
619 deleting_rvh_ = content::Details<BackgroundContents>(details)-> | |
620 tab_contents()->render_view_host(); | |
621 // Fall through. | |
622 case chrome::NOTIFICATION_BACKGROUND_CONTENTS_NAVIGATED: | |
623 case chrome::NOTIFICATION_EXTENSION_HOST_CREATED: | |
624 source_profile = content::Source<Profile>(source).ptr(); | |
625 if (!profile->IsSameProfile(source_profile)) | |
626 return; | |
627 MaybeUpdateAfterNotification(); | |
628 break; | |
629 case chrome::NOTIFICATION_EXTENSION_LOADED: | |
630 case chrome::NOTIFICATION_EXTENSION_UNLOADED: | |
631 case chrome::NOTIFICATION_EXTENSION_UPDATE_DISABLED: | |
632 case chrome::NOTIFICATION_EXTENSION_WARNING_CHANGED: | |
633 case chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED: | |
634 MaybeUpdateAfterNotification(); | |
635 break; | |
636 default: | |
637 NOTREACHED(); | |
638 } | |
639 } | |
640 | |
641 const Extension* ExtensionSettingsHandler::GetExtension(const ListValue* args) { | |
642 std::string extension_id = UTF16ToUTF8(ExtractStringValue(args)); | |
643 CHECK(!extension_id.empty()); | |
644 return extension_service_->GetExtensionById(extension_id, true); | |
645 } | |
646 | |
647 void ExtensionSettingsHandler::MaybeUpdateAfterNotification() { | |
648 TabContents* contents = web_ui_->tab_contents(); | |
649 if (!ignore_notifications_ && contents && contents->render_view_host()) | |
650 HandleRequestExtensionsData(NULL); | |
651 deleting_rvh_ = NULL; | |
652 } | |
653 | |
654 // Static | |
655 DictionaryValue* ExtensionSettingsHandler::CreateExtensionDetailValue( | |
656 ExtensionService* service, const Extension* extension, | |
657 const std::vector<ExtensionPage>& pages, | |
658 const ExtensionWarningSet* warnings_set, | |
659 bool enabled, bool terminated) { | |
660 DictionaryValue* extension_data = new DictionaryValue(); | |
661 GURL icon = | |
662 ExtensionIconSource::GetIconURL(extension, | |
663 Extension::EXTENSION_ICON_MEDIUM, | |
664 ExtensionIconSet::MATCH_BIGGER, | |
665 !enabled, NULL); | |
666 extension_data->SetString("id", extension->id()); | |
667 extension_data->SetString("name", extension->name()); | |
668 extension_data->SetString("description", extension->description()); | |
669 if (extension->location() == Extension::LOAD) | |
670 extension_data->SetString("path", extension->path().value()); | |
671 extension_data->SetString("version", extension->version()->GetString()); | |
672 extension_data->SetString("icon", icon.spec()); | |
673 extension_data->SetBoolean("isUnpacked", | |
674 extension->location() == Extension::LOAD); | |
675 extension_data->SetBoolean("mayDisable", | |
676 Extension::UserMayDisable(extension->location())); | |
677 extension_data->SetBoolean("enabled", enabled); | |
678 extension_data->SetBoolean("terminated", terminated); | |
679 extension_data->SetBoolean("enabledIncognito", | |
680 service ? service->IsIncognitoEnabled(extension->id()) : false); | |
681 extension_data->SetBoolean("wantsFileAccess", extension->wants_file_access()); | |
682 extension_data->SetBoolean("allowFileAccess", | |
683 service ? service->AllowFileAccess(extension) : false); | |
684 extension_data->SetBoolean("allow_reload", | |
685 extension->location() == Extension::LOAD); | |
686 extension_data->SetBoolean("is_hosted_app", extension->is_hosted_app()); | |
687 | |
688 // Determine the sort order: Extensions loaded through --load-extensions show | |
689 // up at the top. Disabled extensions show up at the bottom. | |
690 if (extension->location() == Extension::LOAD) | |
691 extension_data->SetInteger("order", 1); | |
692 else | |
693 extension_data->SetInteger("order", 2); | |
694 | |
695 if (!extension->options_url().is_empty() && enabled) | |
696 extension_data->SetString("options_url", extension->options_url().spec()); | |
697 | |
698 if (service && !service->GetBrowserActionVisibility(extension)) | |
699 extension_data->SetBoolean("enable_show_button", true); | |
700 | |
701 // Add views | |
702 ListValue* views = new ListValue; | |
703 for (std::vector<ExtensionPage>::const_iterator iter = pages.begin(); | |
704 iter != pages.end(); ++iter) { | |
705 DictionaryValue* view_value = new DictionaryValue; | |
706 if (iter->url.scheme() == chrome::kExtensionScheme) { | |
707 // No leading slash. | |
708 view_value->SetString("path", iter->url.path().substr(1)); | |
709 } else { | |
710 // For live pages, use the full URL. | |
711 view_value->SetString("path", iter->url.spec()); | |
712 } | |
713 view_value->SetInteger("renderViewId", iter->render_view_id); | |
714 view_value->SetInteger("renderProcessId", iter->render_process_id); | |
715 view_value->SetBoolean("incognito", iter->incognito); | |
716 views->Append(view_value); | |
717 } | |
718 extension_data->Set("views", views); | |
719 extension_data->SetBoolean("hasPopupAction", | |
720 extension->browser_action() || extension->page_action()); | |
721 extension_data->SetString("homepageUrl", extension->GetHomepageURL().spec()); | |
722 | |
723 // Add warnings. | |
724 ListValue* warnings_list = new ListValue; | |
725 if (warnings_set) { | |
726 std::set<ExtensionWarningSet::WarningType> warnings; | |
727 warnings_set->GetWarningsAffectingExtension(extension->id(), &warnings); | |
728 | |
729 for (std::set<ExtensionWarningSet::WarningType>::const_iterator iter = | |
730 warnings.begin(); | |
731 iter != warnings.end(); | |
732 ++iter) { | |
733 string16 warning_string(ExtensionWarningSet::GetLocalizedWarning(*iter)); | |
734 warnings_list->Append(Value::CreateStringValue(warning_string)); | |
735 } | |
736 } | |
737 extension_data->Set("warnings", warnings_list); | |
738 | |
739 return extension_data; | |
740 } | |
741 | |
742 std::vector<ExtensionPage> ExtensionSettingsHandler::GetActivePagesForExtension( | |
743 const Extension* extension) { | |
744 std::vector<ExtensionPage> result; | |
745 | |
746 // Get the extension process's active views. | |
747 ExtensionProcessManager* process_manager = | |
748 extension_service_->profile()->GetExtensionProcessManager(); | |
749 GetActivePagesForExtensionProcess( | |
750 process_manager->GetRenderViewHostsForExtension( | |
751 extension->id()), &result); | |
752 | |
753 // Repeat for the incognito process, if applicable. | |
754 if (extension_service_->profile()->HasOffTheRecordProfile() && | |
755 extension->incognito_split_mode()) { | |
756 ExtensionProcessManager* process_manager = | |
757 extension_service_->profile()->GetOffTheRecordProfile()-> | |
758 GetExtensionProcessManager(); | |
759 GetActivePagesForExtensionProcess( | |
760 process_manager->GetRenderViewHostsForExtension( | |
761 extension->id()), &result); | |
762 } | |
763 | |
764 return result; | |
765 } | |
766 | |
767 void ExtensionSettingsHandler::GetActivePagesForExtensionProcess( | |
768 const std::set<RenderViewHost*>& views, | |
769 std::vector<ExtensionPage> *result) { | |
770 for (std::set<RenderViewHost*>::const_iterator iter = views.begin(); | |
771 iter != views.end(); ++iter) { | |
772 RenderViewHost* host = *iter; | |
773 int host_type = host->delegate()->GetRenderViewType(); | |
774 if (host == deleting_rvh_ || | |
775 chrome::VIEW_TYPE_EXTENSION_POPUP == host_type || | |
776 chrome::VIEW_TYPE_EXTENSION_DIALOG == host_type) | |
777 continue; | |
778 | |
779 GURL url = host->delegate()->GetURL(); | |
780 content::RenderProcessHost* process = host->process(); | |
781 result->push_back( | |
782 ExtensionPage(url, process->GetID(), host->routing_id(), | |
783 process->GetBrowserContext()->IsOffTheRecord())); | |
784 } | |
785 } | |
OLD | NEW |