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