extension_settings_handler.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
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/extensions/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/prefs/pref_service.h"
13#include "base/string_util.h"
14#include "base/strings/string_number_conversions.h"
15#include "base/utf_string_conversions.h"
16#include "base/values.h"
17#include "base/version.h"
18#include "chrome/browser/browser_process.h"
19#include "chrome/browser/devtools/devtools_window.h"
20#include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
21#include "chrome/browser/extensions/crx_installer.h"
22#include "chrome/browser/extensions/extension_action_manager.h"
23#include "chrome/browser/extensions/extension_disabled_ui.h"
24#include "chrome/browser/extensions/extension_error_reporter.h"
25#include "chrome/browser/extensions/extension_host.h"
26#include "chrome/browser/extensions/extension_service.h"
27#include "chrome/browser/extensions/extension_system.h"
28#include "chrome/browser/extensions/extension_tab_util.h"
29#include "chrome/browser/extensions/extension_warning_set.h"
30#include "chrome/browser/extensions/lazy_background_task_queue.h"
31#include "chrome/browser/extensions/management_policy.h"
32#include "chrome/browser/extensions/shell_window_registry.h"
33#include "chrome/browser/extensions/unpacked_installer.h"
34#include "chrome/browser/extensions/updater/extension_updater.h"
35#include "chrome/browser/google/google_util.h"
36#include "chrome/browser/managed_mode/managed_user_service.h"
37#include "chrome/browser/managed_mode/managed_user_service_factory.h"
38#include "chrome/browser/profiles/profile.h"
39#include "chrome/browser/tab_contents/background_contents.h"
40#include "chrome/browser/ui/browser_finder.h"
41#include "chrome/browser/ui/chrome_select_file_policy.h"
42#include "chrome/browser/ui/extensions/application_launch.h"
43#include "chrome/browser/ui/extensions/shell_window.h"
44#include "chrome/browser/ui/webui/extensions/extension_icon_source.h"
45#include "chrome/common/chrome_notification_types.h"
46#include "chrome/common/chrome_switches.h"
47#include "chrome/common/extensions/background_info.h"
48#include "chrome/common/extensions/extension.h"
49#include "chrome/common/extensions/extension_constants.h"
50#include "chrome/common/extensions/extension_icon_set.h"
51#include "chrome/common/extensions/extension_set.h"
52#include "chrome/common/extensions/feature_switch.h"
53#include "chrome/common/extensions/incognito_handler.h"
54#include "chrome/common/extensions/manifest_url_handler.h"
55#include "chrome/common/pref_names.h"
56#include "chrome/common/url_constants.h"
57#include "components/user_prefs/pref_registry_syncable.h"
58#include "content/public/browser/notification_service.h"
59#include "content/public/browser/notification_source.h"
60#include "content/public/browser/notification_types.h"
61#include "content/public/browser/render_process_host.h"
62#include "content/public/browser/render_view_host.h"
63#include "content/public/browser/site_instance.h"
64#include "content/public/browser/web_contents.h"
65#include "content/public/browser/web_contents_view.h"
66#include "content/public/browser/web_ui.h"
67#include "content/public/browser/web_ui_data_source.h"
68#include "extensions/browser/view_type_utils.h"
69#include "extensions/common/constants.h"
70#include "grit/browser_resources.h"
71#include "grit/chromium_strings.h"
72#include "grit/generated_resources.h"
73#include "grit/theme_resources.h"
74#include "ui/base/l10n/l10n_util.h"
75#include "ui/base/resource/resource_bundle.h"
76
77
78using content::RenderViewHost;
79using content::WebContents;
80using extensions::Extension;
81using extensions::ExtensionUpdater;
82using extensions::ExtensionWarning;
83using extensions::ManagementPolicy;
84using extensions::Manifest;
85
86///////////////////////////////////////////////////////////////////////////////
87//
88// ExtensionSettingsHandler
89//
90///////////////////////////////////////////////////////////////////////////////
91
92ExtensionSettingsHandler::ExtensionSettingsHandler()
93    : extension_service_(NULL),
94      management_policy_(NULL),
95      ignore_notifications_(false),
96      deleting_rvh_(NULL),
97      registered_for_notifications_(false),
98      rvh_created_callback_(
99          base::Bind(&ExtensionSettingsHandler::RenderViewHostCreated,
100                     base::Unretained(this))),
101      warning_service_observer_(this) {
102}
103
104ExtensionSettingsHandler::~ExtensionSettingsHandler() {
105  content::RenderViewHost::RemoveCreatedCallback(rvh_created_callback_);
106
107  // There may be pending file dialogs, we need to tell them that we've gone
108  // away so they don't try and call back to us.
109  if (load_extension_dialog_)
110    load_extension_dialog_->ListenerDestroyed();
111}
112
113ExtensionSettingsHandler::ExtensionSettingsHandler(ExtensionService* service,
114                                                   ManagementPolicy* policy)
115    : extension_service_(service),
116      management_policy_(policy),
117      ignore_notifications_(false),
118      deleting_rvh_(NULL),
119      registered_for_notifications_(false),
120      warning_service_observer_(this) {
121}
122
123// static
124void ExtensionSettingsHandler::RegisterUserPrefs(
125    user_prefs::PrefRegistrySyncable* registry) {
126  registry->RegisterBooleanPref(
127      prefs::kExtensionsUIDeveloperMode,
128      false,
129      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
130}
131
132DictionaryValue* ExtensionSettingsHandler::CreateExtensionDetailValue(
133    const Extension* extension,
134    const std::vector<ExtensionPage>& pages,
135    const extensions::ExtensionWarningService* warning_service) {
136  DictionaryValue* extension_data = new DictionaryValue();
137  bool enabled = extension_service_->IsExtensionEnabled(extension->id());
138  extension->GetBasicInfo(enabled, extension_data);
139
140  extension_data->SetBoolean(
141      "userModifiable",
142      management_policy_->UserMayModifySettings(extension, NULL));
143
144  GURL icon =
145      ExtensionIconSource::GetIconURL(extension,
146                                      extension_misc::EXTENSION_ICON_MEDIUM,
147                                      ExtensionIconSet::MATCH_BIGGER,
148                                      !enabled, NULL);
149  if (Manifest::IsUnpackedLocation(extension->location()))
150    extension_data->SetString("path", extension->path().value());
151  extension_data->SetString("icon", icon.spec());
152  extension_data->SetBoolean("isUnpacked",
153      Manifest::IsUnpackedLocation(extension->location()));
154  extension_data->SetBoolean("terminated",
155      extension_service_->terminated_extensions()->Contains(extension->id()));
156  extension_data->SetBoolean("enabledIncognito",
157      extension_service_->IsIncognitoEnabled(extension->id()));
158  extension_data->SetBoolean("incognitoCanBeEnabled",
159                             extension->can_be_incognito_enabled());
160  extension_data->SetBoolean("wantsFileAccess", extension->wants_file_access());
161  extension_data->SetBoolean("allowFileAccess",
162                             extension_service_->AllowFileAccess(extension));
163  extension_data->SetBoolean("allow_activity",
164      enabled && CommandLine::ForCurrentProcess()->HasSwitch(
165          switches::kEnableExtensionActivityUI));
166  extension_data->SetBoolean("allow_reload",
167      Manifest::IsUnpackedLocation(extension->location()));
168  extension_data->SetBoolean("is_hosted_app", extension->is_hosted_app());
169  extension_data->SetBoolean("is_platform_app", extension->is_platform_app());
170  extension_data->SetBoolean("homepageProvided",
171      extensions::ManifestURL::GetHomepageURL(extension).is_valid());
172
173  string16 location_text;
174  if (extension->location() == Manifest::INTERNAL &&
175      !extension->UpdatesFromGallery()) {
176    location_text = l10n_util::GetStringUTF16(
177        IDS_OPTIONS_INSTALL_LOCATION_UNKNOWN);
178  } else if (extension->location() == Manifest::EXTERNAL_REGISTRY) {
179    location_text = l10n_util::GetStringUTF16(
180        IDS_OPTIONS_INSTALL_LOCATION_3RD_PARTY);
181  }
182  extension_data->SetString("locationText", location_text);
183
184  // Determine the sort order: Extensions loaded through --load-extensions show
185  // up at the top. Disabled extensions show up at the bottom.
186  if (Manifest::IsUnpackedLocation(extension->location()))
187    extension_data->SetInteger("order", 1);
188  else
189    extension_data->SetInteger("order", 2);
190
191  if (!extensions::ExtensionActionAPI::GetBrowserActionVisibility(
192          extension_service_->extension_prefs(), extension->id())) {
193    extension_data->SetBoolean("enable_show_button", true);
194  }
195
196  // Add views
197  ListValue* views = new ListValue;
198  for (std::vector<ExtensionPage>::const_iterator iter = pages.begin();
199       iter != pages.end(); ++iter) {
200    DictionaryValue* view_value = new DictionaryValue;
201    if (iter->url.scheme() == extensions::kExtensionScheme) {
202      // No leading slash.
203      view_value->SetString("path", iter->url.path().substr(1));
204    } else {
205      // For live pages, use the full URL.
206      view_value->SetString("path", iter->url.spec());
207    }
208    view_value->SetInteger("renderViewId", iter->render_view_id);
209    view_value->SetInteger("renderProcessId", iter->render_process_id);
210    view_value->SetBoolean("incognito", iter->incognito);
211    views->Append(view_value);
212  }
213  extension_data->Set("views", views);
214  extensions::ExtensionActionManager* extension_action_manager =
215      extensions::ExtensionActionManager::Get(extension_service_->profile());
216  extension_data->SetBoolean(
217      "hasPopupAction",
218      extension_action_manager->GetBrowserAction(*extension) ||
219      extension_action_manager->GetPageAction(*extension));
220
221  // Add warnings.
222  if (warning_service) {
223    std::vector<std::string> warnings =
224        warning_service->GetWarningMessagesForExtension(extension->id());
225
226    if (!warnings.empty()) {
227      ListValue* warnings_list = new ListValue;
228      for (std::vector<std::string>::const_iterator iter = warnings.begin();
229           iter != warnings.end(); ++iter) {
230        warnings_list->Append(Value::CreateStringValue(*iter));
231      }
232      extension_data->Set("warnings", warnings_list);
233    }
234  }
235
236  // Add install warnings (these are not the same as warnings!).
237  if (Manifest::IsUnpackedLocation(extension->location())) {
238    const std::vector<extensions::InstallWarning>& install_warnings =
239        extension->install_warnings();
240    if (!install_warnings.empty()) {
241      scoped_ptr<ListValue> list(new ListValue());
242      for (std::vector<extensions::InstallWarning>::const_iterator it =
243               install_warnings.begin(); it != install_warnings.end(); ++it) {
244        DictionaryValue* item = new DictionaryValue();
245        item->SetBoolean("isHTML",
246                         it->format == extensions::InstallWarning::FORMAT_HTML);
247        item->SetString("message", it->message);
248        list->Append(item);
249      }
250      extension_data->Set("installWarnings", list.release());
251    }
252  }
253
254  return extension_data;
255}
256
257void ExtensionSettingsHandler::GetLocalizedValues(
258    content::WebUIDataSource* source) {
259  source->AddString("extensionSettings",
260      l10n_util::GetStringUTF16(IDS_MANAGE_EXTENSIONS_SETTING_WINDOWS_TITLE));
261
262  source->AddString("extensionSettingsDeveloperMode",
263      l10n_util::GetStringUTF16(IDS_EXTENSIONS_DEVELOPER_MODE_LINK));
264  source->AddString("extensionSettingsNoExtensions",
265      l10n_util::GetStringUTF16(IDS_EXTENSIONS_NONE_INSTALLED));
266  source->AddString("extensionSettingsSuggestGallery",
267      l10n_util::GetStringFUTF16(IDS_EXTENSIONS_NONE_INSTALLED_SUGGEST_GALLERY,
268          ASCIIToUTF16(google_util::AppendGoogleLocaleParam(
269              GURL(extension_urls::GetExtensionGalleryURL())).spec())));
270  source->AddString("extensionSettingsGetMoreExtensions",
271      l10n_util::GetStringUTF16(IDS_GET_MORE_EXTENSIONS));
272  source->AddString("extensionSettingsGetMoreExtensionsUrl",
273      ASCIIToUTF16(google_util::AppendGoogleLocaleParam(
274          GURL(extension_urls::GetExtensionGalleryURL())).spec()));
275  source->AddString("extensionSettingsExtensionId",
276      l10n_util::GetStringUTF16(IDS_EXTENSIONS_ID));
277  source->AddString("extensionSettingsExtensionPath",
278      l10n_util::GetStringUTF16(IDS_EXTENSIONS_PATH));
279  source->AddString("extensionSettingsInspectViews",
280      l10n_util::GetStringUTF16(IDS_EXTENSIONS_INSPECT_VIEWS));
281  source->AddString("extensionSettingsInstallWarnings",
282      l10n_util::GetStringUTF16(IDS_EXTENSIONS_INSTALL_WARNINGS));
283  source->AddString("viewIncognito",
284      l10n_util::GetStringUTF16(IDS_EXTENSIONS_VIEW_INCOGNITO));
285  source->AddString("viewInactive",
286      l10n_util::GetStringUTF16(IDS_EXTENSIONS_VIEW_INACTIVE));
287  source->AddString("extensionSettingsEnable",
288      l10n_util::GetStringUTF16(IDS_EXTENSIONS_ENABLE));
289  source->AddString("extensionSettingsEnabled",
290      l10n_util::GetStringUTF16(IDS_EXTENSIONS_ENABLED));
291  source->AddString("extensionSettingsRemove",
292      l10n_util::GetStringUTF16(IDS_EXTENSIONS_REMOVE));
293  source->AddString("extensionSettingsEnableIncognito",
294      l10n_util::GetStringUTF16(IDS_EXTENSIONS_ENABLE_INCOGNITO));
295  source->AddString("extensionSettingsAllowFileAccess",
296      l10n_util::GetStringUTF16(IDS_EXTENSIONS_ALLOW_FILE_ACCESS));
297  source->AddString("extensionSettingsIncognitoWarning",
298      l10n_util::GetStringUTF16(IDS_EXTENSIONS_INCOGNITO_WARNING));
299  source->AddString("extensionSettingsReloadTerminated",
300      l10n_util::GetStringUTF16(IDS_EXTENSIONS_RELOAD_TERMINATED));
301  source->AddString("extensionSettingsLaunch",
302      l10n_util::GetStringUTF16(IDS_EXTENSIONS_LAUNCH));
303  source->AddString("extensionSettingsRestart",
304      l10n_util::GetStringUTF16(IDS_EXTENSIONS_RESTART));
305  source->AddString("extensionSettingsReloadUnpacked",
306      l10n_util::GetStringUTF16(IDS_EXTENSIONS_RELOAD_UNPACKED));
307  source->AddString("extensionSettingsOptions",
308      l10n_util::GetStringUTF16(IDS_EXTENSIONS_OPTIONS_LINK));
309  source->AddString("extensionSettingsPermissions",
310      l10n_util::GetStringUTF16(IDS_EXTENSIONS_PERMISSIONS_LINK));
311  source->AddString("extensionSettingsActivity",
312      l10n_util::GetStringUTF16(IDS_EXTENSIONS_ACTIVITY_LINK));
313  source->AddString("extensionSettingsVisitWebsite",
314      l10n_util::GetStringUTF16(IDS_EXTENSIONS_VISIT_WEBSITE));
315  source->AddString("extensionSettingsVisitWebStore",
316      l10n_util::GetStringUTF16(IDS_EXTENSIONS_VISIT_WEBSTORE));
317  source->AddString("extensionSettingsPolicyControlled",
318     l10n_util::GetStringUTF16(IDS_EXTENSIONS_POLICY_CONTROLLED));
319  source->AddString("extensionSettingsManagedMode",
320     l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOCKED_MANAGED_MODE));
321  source->AddString("sideloadWipeoutUrl",
322      chrome::kSideloadWipeoutHelpURL);
323  source->AddString("sideloadWipoutLearnMore",
324      l10n_util::GetStringUTF16(IDS_LEARN_MORE));
325  source->AddString("extensionSettingsShowButton",
326      l10n_util::GetStringUTF16(IDS_EXTENSIONS_SHOW_BUTTON));
327  source->AddString("extensionSettingsLoadUnpackedButton",
328      l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOAD_UNPACKED_BUTTON));
329  source->AddString("extensionSettingsPackButton",
330      l10n_util::GetStringUTF16(IDS_EXTENSIONS_PACK_BUTTON));
331  source->AddString("extensionSettingsCommandsLink",
332      l10n_util::GetStringUTF16(IDS_EXTENSIONS_COMMANDS_CONFIGURE));
333  source->AddString("extensionSettingsUpdateButton",
334      l10n_util::GetStringUTF16(IDS_EXTENSIONS_UPDATE_BUTTON));
335  source->AddString("extensionSettingsCrashMessage",
336      l10n_util::GetStringUTF16(IDS_EXTENSIONS_CRASHED_EXTENSION));
337  source->AddString("extensionSettingsInDevelopment",
338      l10n_util::GetStringUTF16(IDS_EXTENSIONS_IN_DEVELOPMENT));
339  source->AddString("extensionSettingsWarningsTitle",
340      l10n_util::GetStringUTF16(IDS_EXTENSION_WARNINGS_TITLE));
341  source->AddString("extensionSettingsShowDetails",
342      l10n_util::GetStringUTF16(IDS_EXTENSIONS_SHOW_DETAILS));
343  source->AddString("extensionSettingsHideDetails",
344      l10n_util::GetStringUTF16(IDS_EXTENSIONS_HIDE_DETAILS));
345
346  // TODO(estade): comb through the above strings to find ones no longer used in
347  // uber extensions.
348  source->AddString("extensionUninstall",
349      l10n_util::GetStringUTF16(IDS_EXTENSIONS_UNINSTALL));
350}
351
352void ExtensionSettingsHandler::RenderViewHostCreated(
353    content::RenderViewHost* render_view_host) {
354  Profile* source_profile = Profile::FromBrowserContext(
355      render_view_host->GetSiteInstance()->GetBrowserContext());
356  if (!Profile::FromWebUI(web_ui())->IsSameProfile(source_profile))
357    return;
358  MaybeUpdateAfterNotification();
359}
360
361void ExtensionSettingsHandler::RenderViewDeleted(
362    content::RenderViewHost* render_view_host) {
363  deleting_rvh_ = render_view_host;
364  Profile* source_profile = Profile::FromBrowserContext(
365      render_view_host->GetSiteInstance()->GetBrowserContext());
366  if (!Profile::FromWebUI(web_ui())->IsSameProfile(source_profile))
367    return;
368  MaybeUpdateAfterNotification();
369}
370
371void ExtensionSettingsHandler::NavigateToPendingEntry(const GURL& url,
372    content::NavigationController::ReloadType reload_type) {
373  if (reload_type != content::NavigationController::NO_RELOAD)
374    ReloadUnpackedExtensions();
375}
376
377void ExtensionSettingsHandler::RegisterMessages() {
378  // Don't override an |extension_service_| or |management_policy_| injected
379  // for testing.
380  if (!extension_service_) {
381    extension_service_ = Profile::FromWebUI(web_ui())->GetOriginalProfile()->
382        GetExtensionService();
383  }
384  if (!management_policy_) {
385    management_policy_ = extensions::ExtensionSystem::Get(
386        extension_service_->profile())->management_policy();
387  }
388
389  web_ui()->RegisterMessageCallback("extensionSettingsRequestExtensionsData",
390      base::Bind(&ExtensionSettingsHandler::HandleRequestExtensionsData,
391                 base::Unretained(this)));
392  web_ui()->RegisterMessageCallback("extensionSettingsToggleDeveloperMode",
393      base::Bind(&ExtensionSettingsHandler::HandleToggleDeveloperMode,
394                 base::Unretained(this)));
395  web_ui()->RegisterMessageCallback("extensionSettingsInspect",
396      base::Bind(&ExtensionSettingsHandler::HandleInspectMessage,
397                 base::Unretained(this)));
398  web_ui()->RegisterMessageCallback("extensionSettingsLaunch",
399      base::Bind(&ExtensionSettingsHandler::HandleLaunchMessage,
400                 base::Unretained(this)));
401  web_ui()->RegisterMessageCallback("extensionSettingsRestart",
402      base::Bind(&ExtensionSettingsHandler::HandleRestartMessage,
403                 base::Unretained(this)));
404  web_ui()->RegisterMessageCallback("extensionSettingsReload",
405      base::Bind(&ExtensionSettingsHandler::HandleReloadMessage,
406                 base::Unretained(this)));
407  web_ui()->RegisterMessageCallback("extensionSettingsEnable",
408      base::Bind(&ExtensionSettingsHandler::HandleEnableMessage,
409                 base::Unretained(this)));
410  web_ui()->RegisterMessageCallback("extensionSettingsEnableIncognito",
411      base::Bind(&ExtensionSettingsHandler::HandleEnableIncognitoMessage,
412                 base::Unretained(this)));
413  web_ui()->RegisterMessageCallback("extensionSettingsAllowFileAccess",
414      base::Bind(&ExtensionSettingsHandler::HandleAllowFileAccessMessage,
415                 base::Unretained(this)));
416  web_ui()->RegisterMessageCallback("extensionSettingsUninstall",
417      base::Bind(&ExtensionSettingsHandler::HandleUninstallMessage,
418                 base::Unretained(this)));
419  web_ui()->RegisterMessageCallback("extensionSettingsOptions",
420      base::Bind(&ExtensionSettingsHandler::HandleOptionsMessage,
421                 base::Unretained(this)));
422  web_ui()->RegisterMessageCallback("extensionSettingsPermissions",
423      base::Bind(&ExtensionSettingsHandler::HandlePermissionsMessage,
424                 base::Unretained(this)));
425  web_ui()->RegisterMessageCallback("extensionSettingsShowButton",
426      base::Bind(&ExtensionSettingsHandler::HandleShowButtonMessage,
427                 base::Unretained(this)));
428  web_ui()->RegisterMessageCallback("extensionSettingsAutoupdate",
429      base::Bind(&ExtensionSettingsHandler::HandleAutoUpdateMessage,
430                 base::Unretained(this)));
431  web_ui()->RegisterMessageCallback("extensionSettingsLoadUnpackedExtension",
432      base::Bind(&ExtensionSettingsHandler::HandleLoadUnpackedExtensionMessage,
433                 base::Unretained(this)));
434}
435
436void ExtensionSettingsHandler::FileSelected(const base::FilePath& path,
437                                            int index,
438                                            void* params) {
439  last_unpacked_directory_ = base::FilePath(path);
440  extensions::UnpackedInstaller::Create(extension_service_)->Load(path);
441}
442
443void ExtensionSettingsHandler::MultiFilesSelected(
444    const std::vector<base::FilePath>& files, void* params) {
445  NOTREACHED();
446}
447
448void ExtensionSettingsHandler::Observe(
449    int type,
450    const content::NotificationSource& source,
451    const content::NotificationDetails& details) {
452  Profile* profile = Profile::FromWebUI(web_ui());
453  Profile* source_profile = NULL;
454  switch (type) {
455    // We listen for notifications that will result in the page being
456    // repopulated with data twice for the same event in certain cases.
457    // For instance, EXTENSION_LOADED & EXTENSION_HOST_CREATED because
458    // we don't know about the views for an extension at EXTENSION_LOADED, but
459    // if we only listen to EXTENSION_HOST_CREATED, we'll miss extensions
460    // that don't have a process at startup.
461    //
462    // Doing it this way gets everything but causes the page to be rendered
463    // more than we need. It doesn't seem to result in any noticeable flicker.
464    case chrome::NOTIFICATION_BACKGROUND_CONTENTS_DELETED:
465      deleting_rvh_ = content::Details<BackgroundContents>(details)->
466          web_contents()->GetRenderViewHost();
467      // Fall through.
468    case chrome::NOTIFICATION_BACKGROUND_CONTENTS_NAVIGATED:
469    case chrome::NOTIFICATION_EXTENSION_HOST_CREATED:
470      source_profile = content::Source<Profile>(source).ptr();
471      if (!profile->IsSameProfile(source_profile))
472          return;
473      MaybeUpdateAfterNotification();
474      break;
475    case chrome::NOTIFICATION_EXTENSION_LOADED:
476    case chrome::NOTIFICATION_EXTENSION_UNLOADED:
477    case chrome::NOTIFICATION_EXTENSION_UPDATE_DISABLED:
478    case chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED:
479      MaybeUpdateAfterNotification();
480      break;
481    default:
482      NOTREACHED();
483  }
484}
485
486void ExtensionSettingsHandler::ExtensionUninstallAccepted() {
487  DCHECK(!extension_id_prompting_.empty());
488
489  bool was_terminated = false;
490
491  // The extension can be uninstalled in another window while the UI was
492  // showing. Do nothing in that case.
493  const Extension* extension =
494      extension_service_->GetExtensionById(extension_id_prompting_, true);
495  if (!extension) {
496    extension = extension_service_->GetTerminatedExtension(
497        extension_id_prompting_);
498    was_terminated = true;
499  }
500  if (!extension)
501    return;
502
503  extension_service_->UninstallExtension(extension_id_prompting_,
504                                         false,  // External uninstall.
505                                         NULL);  // Error.
506  extension_id_prompting_ = "";
507
508  // There will be no EXTENSION_UNLOADED notification for terminated
509  // extensions as they were already unloaded.
510  if (was_terminated)
511    HandleRequestExtensionsData(NULL);
512}
513
514void ExtensionSettingsHandler::ExtensionUninstallCanceled() {
515  extension_id_prompting_ = "";
516}
517
518void ExtensionSettingsHandler::ExtensionWarningsChanged() {
519  MaybeUpdateAfterNotification();
520}
521
522void ExtensionSettingsHandler::InstallUIProceed() {
523  // This should never happen. The dialog only has a cancel button.
524  NOTREACHED();
525}
526
527void ExtensionSettingsHandler::InstallUIAbort(bool user_initiated) {
528}
529
530void ExtensionSettingsHandler::ReloadUnpackedExtensions() {
531  const ExtensionSet* extensions = extension_service_->extensions();
532  std::vector<const Extension*> unpacked_extensions;
533  for (ExtensionSet::const_iterator extension = extensions->begin();
534       extension != extensions->end(); ++extension) {
535    if (Manifest::IsUnpackedLocation((*extension)->location()))
536      unpacked_extensions.push_back(*extension);
537  }
538
539  for (std::vector<const Extension*>::iterator iter =
540       unpacked_extensions.begin(); iter != unpacked_extensions.end(); ++iter) {
541    extension_service_->ReloadExtension((*iter)->id());
542  }
543}
544
545void ExtensionSettingsHandler::HandleRequestExtensionsData(
546    const ListValue* args) {
547  DictionaryValue results;
548
549  Profile* profile = Profile::FromWebUI(web_ui());
550
551  // Add the extensions to the results structure.
552  ListValue* extensions_list = new ListValue();
553
554  extensions::ExtensionWarningService* warnings =
555      extensions::ExtensionSystem::Get(profile)->warning_service();
556
557  const ExtensionSet* extensions = extension_service_->extensions();
558  for (ExtensionSet::const_iterator extension = extensions->begin();
559       extension != extensions->end(); ++extension) {
560    if ((*extension)->ShouldDisplayInExtensionSettings()) {
561      extensions_list->Append(CreateExtensionDetailValue(
562          *extension,
563          GetInspectablePagesForExtension(*extension, true),
564          warnings));
565    }
566  }
567  extensions = extension_service_->disabled_extensions();
568  for (ExtensionSet::const_iterator extension = extensions->begin();
569       extension != extensions->end(); ++extension) {
570    if ((*extension)->ShouldDisplayInExtensionSettings()) {
571      extensions_list->Append(CreateExtensionDetailValue(
572          *extension,
573          GetInspectablePagesForExtension(*extension, false),
574          warnings));
575    }
576  }
577  extensions = extension_service_->terminated_extensions();
578  std::vector<ExtensionPage> empty_pages;
579  for (ExtensionSet::const_iterator extension = extensions->begin();
580       extension != extensions->end(); ++extension) {
581    if ((*extension)->ShouldDisplayInExtensionSettings()) {
582      extensions_list->Append(CreateExtensionDetailValue(
583          *extension,
584          empty_pages,  // Terminated process has no active pages.
585          warnings));
586    }
587  }
588  results.Set("extensions", extensions_list);
589
590  ManagedUserService* service =
591      ManagedUserServiceFactory::GetForProfile(profile);
592
593  bool is_managed = service->ProfileIsManaged();
594  bool developer_mode =
595      !is_managed &&
596      profile->GetPrefs()->GetBoolean(prefs::kExtensionsUIDeveloperMode);
597  results.SetBoolean("profileIsManaged", is_managed);
598  results.SetBoolean("developerMode", developer_mode);
599
600  bool load_unpacked_disabled =
601      extension_service_->extension_prefs()->ExtensionsBlacklistedByDefault();
602  results.SetBoolean("loadUnpackedDisabled", load_unpacked_disabled);
603
604  web_ui()->CallJavascriptFunction("ExtensionSettings.returnExtensionsData",
605                                   results);
606
607  MaybeRegisterForNotifications();
608}
609
610void ExtensionSettingsHandler::HandleToggleDeveloperMode(
611    const ListValue* args) {
612  Profile* profile = Profile::FromWebUI(web_ui());
613  if (ManagedUserServiceFactory::GetForProfile(profile)->ProfileIsManaged())
614    return;
615
616  bool developer_mode =
617      profile->GetPrefs()->GetBoolean(prefs::kExtensionsUIDeveloperMode);
618  profile->GetPrefs()->SetBoolean(
619      prefs::kExtensionsUIDeveloperMode, !developer_mode);
620}
621
622void ExtensionSettingsHandler::HandleInspectMessage(const ListValue* args) {
623  std::string extension_id;
624  std::string render_process_id_str;
625  std::string render_view_id_str;
626  int render_process_id;
627  int render_view_id;
628  bool incognito;
629  CHECK_EQ(4U, args->GetSize());
630  CHECK(args->GetString(0, &extension_id));
631  CHECK(args->GetString(1, &render_process_id_str));
632  CHECK(args->GetString(2, &render_view_id_str));
633  CHECK(args->GetBoolean(3, &incognito));
634  CHECK(base::StringToInt(render_process_id_str, &render_process_id));
635  CHECK(base::StringToInt(render_view_id_str, &render_view_id));
636
637  if (render_process_id == -1) {
638    // This message is for a lazy background page. Start the page if necessary.
639    const Extension* extension =
640        extension_service_->extensions()->GetByID(extension_id);
641    DCHECK(extension);
642
643    ExtensionService* service = extension_service_;
644    if (incognito)
645      service = extensions::ExtensionSystem::Get(extension_service_->
646          profile()->GetOffTheRecordProfile())->extension_service();
647
648    service->InspectBackgroundPage(extension);
649    return;
650  }
651
652  RenderViewHost* host = RenderViewHost::FromID(render_process_id,
653                                                render_view_id);
654  if (!host) {
655    // This can happen if the host has gone away since the page was displayed.
656    return;
657  }
658
659  DevToolsWindow::OpenDevToolsWindow(host);
660}
661
662void ExtensionSettingsHandler::HandleLaunchMessage(const ListValue* args) {
663  CHECK_EQ(1U, args->GetSize());
664  std::string extension_id;
665  CHECK(args->GetString(0, &extension_id));
666  const extensions::Extension* extension =
667      extension_service_->GetExtensionById(extension_id, false);
668  chrome::OpenApplication(chrome::AppLaunchParams(extension_service_->profile(),
669                                                  extension,
670                                                  extension_misc::LAUNCH_WINDOW,
671                                                  NEW_WINDOW));
672}
673
674void ExtensionSettingsHandler::HandleRestartMessage(const ListValue* args) {
675  CHECK_EQ(1U, args->GetSize());
676  std::string extension_id;
677  CHECK(args->GetString(0, &extension_id));
678  extension_service_->RestartExtension(extension_id);
679}
680
681void ExtensionSettingsHandler::HandleReloadMessage(const ListValue* args) {
682  std::string extension_id = UTF16ToUTF8(ExtractStringValue(args));
683  CHECK(!extension_id.empty());
684  extension_service_->ReloadExtension(extension_id);
685}
686
687void ExtensionSettingsHandler::HandleEnableMessage(const ListValue* args) {
688  CHECK_EQ(2U, args->GetSize());
689  std::string extension_id, enable_str;
690  CHECK(args->GetString(0, &extension_id));
691  CHECK(args->GetString(1, &enable_str));
692
693  const Extension* extension =
694      extension_service_->GetInstalledExtension(extension_id);
695  if (!extension ||
696      !management_policy_->UserMayModifySettings(extension, NULL)) {
697    LOG(ERROR) << "Attempt to enable an extension that is non-usermanagable was"
698               << "made. Extension id: " << extension->id();
699    return;
700  }
701
702  if (enable_str == "true") {
703    extensions::ExtensionPrefs* prefs = extension_service_->extension_prefs();
704    if (prefs->DidExtensionEscalatePermissions(extension_id)) {
705      extensions::ShowExtensionDisabledDialog(
706          extension_service_, web_ui()->GetWebContents(), extension);
707    } else if ((prefs->GetDisableReasons(extension_id) &
708                   Extension::DISABLE_UNSUPPORTED_REQUIREMENT) &&
709               !requirements_checker_.get()) {
710      // Recheck the requirements.
711      scoped_refptr<const Extension> extension =
712          extension_service_->GetExtensionById(extension_id,
713                                               true /* include disabled */);
714      requirements_checker_.reset(new extensions::RequirementsChecker());
715      requirements_checker_->Check(
716          extension,
717          base::Bind(&ExtensionSettingsHandler::OnRequirementsChecked,
718                     AsWeakPtr(), extension_id));
719    } else {
720      extension_service_->EnableExtension(extension_id);
721
722      // Make sure any browser action contained within it is not hidden.
723      extensions::ExtensionActionAPI::SetBrowserActionVisibility(
724          prefs, extension->id(), true);
725    }
726  } else {
727    extension_service_->DisableExtension(
728        extension_id, Extension::DISABLE_USER_ACTION);
729  }
730}
731
732void ExtensionSettingsHandler::HandleEnableIncognitoMessage(
733    const ListValue* args) {
734  CHECK_EQ(2U, args->GetSize());
735  std::string extension_id, enable_str;
736  CHECK(args->GetString(0, &extension_id));
737  CHECK(args->GetString(1, &enable_str));
738  const Extension* extension =
739      extension_service_->GetInstalledExtension(extension_id);
740  if (!extension)
741    return;
742
743  // Flipping the incognito bit will generate unload/load notifications for the
744  // extension, but we don't want to reload the page, because a) we've already
745  // updated the UI to reflect the change, and b) we want the yellow warning
746  // text to stay until the user has left the page.
747  //
748  // TODO(aa): This creates crappiness in some cases. For example, in a main
749  // window, when toggling this, the browser action will flicker because it gets
750  // unloaded, then reloaded. It would be better to have a dedicated
751  // notification for this case.
752  //
753  // Bug: http://crbug.com/41384
754  base::AutoReset<bool> auto_reset_ignore_notifications(
755      &ignore_notifications_, true);
756  extension_service_->SetIsIncognitoEnabled(extension->id(),
757                                            enable_str == "true");
758}
759
760void ExtensionSettingsHandler::HandleAllowFileAccessMessage(
761    const ListValue* args) {
762  CHECK_EQ(2U, args->GetSize());
763  std::string extension_id, allow_str;
764  CHECK(args->GetString(0, &extension_id));
765  CHECK(args->GetString(1, &allow_str));
766  const Extension* extension =
767      extension_service_->GetInstalledExtension(extension_id);
768  if (!extension)
769    return;
770
771  if (!management_policy_->UserMayModifySettings(extension, NULL)) {
772    LOG(ERROR) << "Attempt to change allow file access of an extension that is "
773               << "non-usermanagable was made. Extension id : "
774               << extension->id();
775    return;
776  }
777
778  extension_service_->SetAllowFileAccess(extension, allow_str == "true");
779}
780
781void ExtensionSettingsHandler::HandleUninstallMessage(const ListValue* args) {
782  CHECK_EQ(1U, args->GetSize());
783  std::string extension_id;
784  CHECK(args->GetString(0, &extension_id));
785  const Extension* extension =
786      extension_service_->GetInstalledExtension(extension_id);
787  if (!extension)
788    return;
789
790  if (!management_policy_->UserMayModifySettings(extension, NULL)) {
791    LOG(ERROR) << "Attempt to uninstall an extension that is non-usermanagable "
792               << "was made. Extension id : " << extension->id();
793    return;
794  }
795
796  if (!extension_id_prompting_.empty())
797    return;  // Only one prompt at a time.
798
799  extension_id_prompting_ = extension_id;
800
801  GetExtensionUninstallDialog()->ConfirmUninstall(extension);
802}
803
804void ExtensionSettingsHandler::HandleOptionsMessage(const ListValue* args) {
805  const Extension* extension = GetActiveExtension(args);
806  if (!extension ||
807      extensions::ManifestURL::GetOptionsPage(extension).is_empty())
808    return;
809  ExtensionTabUtil::OpenOptionsPage(extension,
810      chrome::FindBrowserWithWebContents(web_ui()->GetWebContents()));
811}
812
813void ExtensionSettingsHandler::HandlePermissionsMessage(const ListValue* args) {
814  std::string extension_id(UTF16ToUTF8(ExtractStringValue(args)));
815  CHECK(!extension_id.empty());
816  const Extension* extension =
817      extension_service_->GetExtensionById(extension_id, true);
818  if (!extension)
819    return;
820
821  prompt_.reset(new ExtensionInstallPrompt(web_contents()));
822  prompt_->ReviewPermissions(this, extension);
823}
824
825void ExtensionSettingsHandler::HandleShowButtonMessage(const ListValue* args) {
826  const Extension* extension = GetActiveExtension(args);
827  if (!extension)
828    return;
829  extensions::ExtensionActionAPI::SetBrowserActionVisibility(
830      extension_service_->extension_prefs(), extension->id(), true);
831}
832
833void ExtensionSettingsHandler::HandleAutoUpdateMessage(const ListValue* args) {
834  ExtensionUpdater* updater = extension_service_->updater();
835  if (updater) {
836    ExtensionUpdater::CheckParams params;
837    params.install_immediately = true;
838    updater->CheckNow(params);
839  }
840}
841
842void ExtensionSettingsHandler::HandleLoadUnpackedExtensionMessage(
843    const ListValue* args) {
844  DCHECK(args->empty());
845
846  string16 select_title =
847      l10n_util::GetStringUTF16(IDS_EXTENSION_LOAD_FROM_DIRECTORY);
848
849  const int kFileTypeIndex = 0;  // No file type information to index.
850  const ui::SelectFileDialog::Type kSelectType =
851      ui::SelectFileDialog::SELECT_FOLDER;
852  load_extension_dialog_ = ui::SelectFileDialog::Create(
853      this, new ChromeSelectFilePolicy(web_ui()->GetWebContents()));
854  load_extension_dialog_->SelectFile(
855      kSelectType,
856      select_title,
857      last_unpacked_directory_,
858      NULL,
859      kFileTypeIndex,
860      base::FilePath::StringType(),
861      web_ui()->GetWebContents()->GetView()->GetTopLevelNativeWindow(),
862      NULL);
863}
864
865void ExtensionSettingsHandler::ShowAlert(const std::string& message) {
866  ListValue arguments;
867  arguments.Append(Value::CreateStringValue(message));
868  web_ui()->CallJavascriptFunction("alert", arguments);
869}
870
871const Extension* ExtensionSettingsHandler::GetActiveExtension(
872    const ListValue* args) {
873  std::string extension_id = UTF16ToUTF8(ExtractStringValue(args));
874  CHECK(!extension_id.empty());
875  return extension_service_->GetExtensionById(extension_id, false);
876}
877
878void ExtensionSettingsHandler::MaybeUpdateAfterNotification() {
879  WebContents* contents = web_ui()->GetWebContents();
880  if (!ignore_notifications_ && contents && contents->GetRenderViewHost())
881    HandleRequestExtensionsData(NULL);
882  deleting_rvh_ = NULL;
883}
884
885void ExtensionSettingsHandler::MaybeRegisterForNotifications() {
886  if (registered_for_notifications_)
887    return;
888
889  registered_for_notifications_  = true;
890  Profile* profile = Profile::FromWebUI(web_ui());
891
892  // Register for notifications that we need to reload the page.
893  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
894                 content::Source<Profile>(profile));
895  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
896                 content::Source<Profile>(profile));
897  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UPDATE_DISABLED,
898                 content::Source<Profile>(profile));
899  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_CREATED,
900                 content::NotificationService::AllBrowserContextsAndSources());
901  registrar_.Add(this,
902                 chrome::NOTIFICATION_BACKGROUND_CONTENTS_NAVIGATED,
903                 content::NotificationService::AllBrowserContextsAndSources());
904  registrar_.Add(this,
905                 chrome::NOTIFICATION_BACKGROUND_CONTENTS_DELETED,
906                 content::NotificationService::AllBrowserContextsAndSources());
907  registrar_.Add(
908      this,
909      chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED,
910      content::Source<extensions::ExtensionPrefs>(
911          profile->GetExtensionService()->extension_prefs()));
912
913  content::RenderViewHost::AddCreatedCallback(rvh_created_callback_);
914
915  content::WebContentsObserver::Observe(web_ui()->GetWebContents());
916
917  warning_service_observer_.Add(
918      extensions::ExtensionSystem::Get(profile)->warning_service());
919
920  base::Closure callback = base::Bind(
921      &ExtensionSettingsHandler::MaybeUpdateAfterNotification,
922      base::Unretained(this));
923
924  pref_registrar_.Init(profile->GetPrefs());
925  pref_registrar_.Add(prefs::kExtensionInstallDenyList, callback);
926}
927
928std::vector<ExtensionPage>
929ExtensionSettingsHandler::GetInspectablePagesForExtension(
930    const Extension* extension, bool extension_is_enabled) {
931  std::vector<ExtensionPage> result;
932
933  // Get the extension process's active views.
934  ExtensionProcessManager* process_manager =
935      extensions::ExtensionSystem::Get(extension_service_->profile())->
936          process_manager();
937  GetInspectablePagesForExtensionProcess(
938      process_manager->GetRenderViewHostsForExtension(extension->id()),
939      &result);
940
941  // Get shell window views
942  GetShellWindowPagesForExtensionProfile(extension,
943      extension_service_->profile(), &result);
944
945  // Include a link to start the lazy background page, if applicable.
946  if (extensions::BackgroundInfo::HasLazyBackgroundPage(extension) &&
947      extension_is_enabled &&
948      !process_manager->GetBackgroundHostForExtension(extension->id())) {
949    result.push_back(
950        ExtensionPage(extensions::BackgroundInfo::GetBackgroundURL(extension),
951                      -1, -1, false));
952  }
953
954  // Repeat for the incognito process, if applicable. Don't try to get
955  // shell windows for incognito processes.
956  if (extension_service_->profile()->HasOffTheRecordProfile() &&
957      extensions::IncognitoInfo::IsSplitMode(extension)) {
958    ExtensionProcessManager* process_manager =
959        extensions::ExtensionSystem::Get(extension_service_->profile()->
960            GetOffTheRecordProfile())->process_manager();
961    GetInspectablePagesForExtensionProcess(
962        process_manager->GetRenderViewHostsForExtension(extension->id()),
963        &result);
964
965    if (extensions::BackgroundInfo::HasLazyBackgroundPage(extension)
966        && extension_is_enabled &&
967        !process_manager->GetBackgroundHostForExtension(extension->id())) {
968      result.push_back(
969          ExtensionPage(extensions::BackgroundInfo::GetBackgroundURL(extension),
970                        -1, -1, true));
971    }
972  }
973
974  return result;
975}
976
977void ExtensionSettingsHandler::GetInspectablePagesForExtensionProcess(
978    const std::set<RenderViewHost*>& views,
979    std::vector<ExtensionPage>* result) {
980  for (std::set<RenderViewHost*>::const_iterator iter = views.begin();
981       iter != views.end(); ++iter) {
982    RenderViewHost* host = *iter;
983    WebContents* web_contents = WebContents::FromRenderViewHost(host);
984    extensions::ViewType host_type = extensions::GetViewType(web_contents);
985    if (host == deleting_rvh_ ||
986        extensions::VIEW_TYPE_EXTENSION_POPUP == host_type ||
987        extensions::VIEW_TYPE_EXTENSION_DIALOG == host_type)
988      continue;
989
990    GURL url = web_contents->GetURL();
991    content::RenderProcessHost* process = host->GetProcess();
992    result->push_back(
993        ExtensionPage(url, process->GetID(), host->GetRoutingID(),
994                      process->GetBrowserContext()->IsOffTheRecord()));
995  }
996}
997
998void ExtensionSettingsHandler::GetShellWindowPagesForExtensionProfile(
999    const Extension* extension,
1000    Profile* profile,
1001    std::vector<ExtensionPage>* result) {
1002  extensions::ShellWindowRegistry* registry =
1003      extensions::ShellWindowRegistry::Get(profile);
1004  if (!registry) return;
1005
1006  const extensions::ShellWindowRegistry::ShellWindowList windows =
1007      registry->GetShellWindowsForApp(extension->id());
1008
1009  for (extensions::ShellWindowRegistry::const_iterator it = windows.begin();
1010       it != windows.end(); ++it) {
1011    WebContents* web_contents = (*it)->web_contents();
1012    RenderViewHost* host = web_contents->GetRenderViewHost();
1013    content::RenderProcessHost* process = host->GetProcess();
1014
1015    result->push_back(
1016        ExtensionPage(web_contents->GetURL(), process->GetID(),
1017                      host->GetRoutingID(),
1018                      process->GetBrowserContext()->IsOffTheRecord()));
1019  }
1020}
1021
1022ExtensionUninstallDialog*
1023ExtensionSettingsHandler::GetExtensionUninstallDialog() {
1024#if !defined(OS_ANDROID)
1025  if (!extension_uninstall_dialog_.get()) {
1026    Browser* browser = chrome::FindBrowserWithWebContents(
1027        web_ui()->GetWebContents());
1028    extension_uninstall_dialog_.reset(
1029        ExtensionUninstallDialog::Create(extension_service_->profile(),
1030                                         browser, this));
1031  }
1032  return extension_uninstall_dialog_.get();
1033#else
1034  return NULL;
1035#endif  // !defined(OS_ANDROID)
1036}
1037
1038void ExtensionSettingsHandler::OnRequirementsChecked(
1039    std::string extension_id,
1040    std::vector<std::string> requirement_errors) {
1041  if (requirement_errors.empty()) {
1042    extension_service_->EnableExtension(extension_id);
1043  } else {
1044    ExtensionErrorReporter::GetInstance()->ReportError(
1045        UTF8ToUTF16(JoinString(requirement_errors, ' ')),
1046        true /* be noisy */);
1047  }
1048  requirements_checker_.reset();
1049}
1050