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 "apps/app_load_service.h"
8#include "apps/app_restore_service.h"
9#include "apps/app_window.h"
10#include "apps/app_window_registry.h"
11#include "apps/saved_files_service.h"
12#include "base/auto_reset.h"
13#include "base/base64.h"
14#include "base/bind.h"
15#include "base/bind_helpers.h"
16#include "base/command_line.h"
17#include "base/location.h"
18#include "base/message_loop/message_loop.h"
19#include "base/metrics/histogram.h"
20#include "base/prefs/pref_service.h"
21#include "base/strings/string_number_conversions.h"
22#include "base/strings/string_util.h"
23#include "base/strings/utf_string_conversions.h"
24#include "base/values.h"
25#include "base/version.h"
26#include "chrome/browser/browser_process.h"
27#include "chrome/browser/chrome_notification_types.h"
28#include "chrome/browser/devtools/devtools_window.h"
29#include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
30#include "chrome/browser/extensions/component_loader.h"
31#include "chrome/browser/extensions/crx_installer.h"
32#include "chrome/browser/extensions/devtools_util.h"
33#include "chrome/browser/extensions/error_console/error_console.h"
34#include "chrome/browser/extensions/extension_action_manager.h"
35#include "chrome/browser/extensions/extension_disabled_ui.h"
36#include "chrome/browser/extensions/extension_error_reporter.h"
37#include "chrome/browser/extensions/extension_service.h"
38#include "chrome/browser/extensions/extension_tab_util.h"
39#include "chrome/browser/extensions/extension_ui_util.h"
40#include "chrome/browser/extensions/extension_util.h"
41#include "chrome/browser/extensions/extension_warning_set.h"
42#include "chrome/browser/extensions/install_verifier.h"
43#include "chrome/browser/extensions/updater/extension_updater.h"
44#include "chrome/browser/profiles/profile.h"
45#include "chrome/browser/supervised_user/supervised_user_service.h"
46#include "chrome/browser/supervised_user/supervised_user_service_factory.h"
47#include "chrome/browser/tab_contents/background_contents.h"
48#include "chrome/browser/ui/browser_finder.h"
49#include "chrome/browser/ui/extensions/application_launch.h"
50#include "chrome/browser/ui/webui/extensions/extension_basic_info.h"
51#include "chrome/browser/ui/webui/extensions/extension_icon_source.h"
52#include "chrome/common/chrome_switches.h"
53#include "chrome/common/chrome_version_info.h"
54#include "chrome/common/extensions/extension_constants.h"
55#include "chrome/common/extensions/features/feature_channel.h"
56#include "chrome/common/extensions/manifest_url_handler.h"
57#include "chrome/common/pref_names.h"
58#include "chrome/common/url_constants.h"
59#include "components/google/core/browser/google_util.h"
60#include "components/pref_registry/pref_registry_syncable.h"
61#include "content/public/browser/notification_service.h"
62#include "content/public/browser/notification_source.h"
63#include "content/public/browser/notification_types.h"
64#include "content/public/browser/render_process_host.h"
65#include "content/public/browser/render_view_host.h"
66#include "content/public/browser/site_instance.h"
67#include "content/public/browser/web_contents.h"
68#include "content/public/browser/web_ui.h"
69#include "content/public/browser/web_ui_data_source.h"
70#include "extensions/browser/blacklist_state.h"
71#include "extensions/browser/extension_error.h"
72#include "extensions/browser/extension_host.h"
73#include "extensions/browser/extension_registry.h"
74#include "extensions/browser/extension_system.h"
75#include "extensions/browser/lazy_background_task_queue.h"
76#include "extensions/browser/management_policy.h"
77#include "extensions/browser/pref_names.h"
78#include "extensions/browser/view_type_utils.h"
79#include "extensions/common/constants.h"
80#include "extensions/common/extension.h"
81#include "extensions/common/extension_icon_set.h"
82#include "extensions/common/extension_set.h"
83#include "extensions/common/feature_switch.h"
84#include "extensions/common/manifest.h"
85#include "extensions/common/manifest_handlers/background_info.h"
86#include "extensions/common/manifest_handlers/incognito_info.h"
87#include "extensions/common/permissions/permissions_data.h"
88#include "grit/browser_resources.h"
89#include "grit/chromium_strings.h"
90#include "grit/generated_resources.h"
91#include "grit/theme_resources.h"
92#include "ui/base/l10n/l10n_util.h"
93#include "ui/base/resource/resource_bundle.h"
94
95using base::DictionaryValue;
96using base::ListValue;
97using content::RenderViewHost;
98using content::WebContents;
99
100namespace {
101const char kAppsDeveloperToolsExtensionId[] =
102    "ohmmkhmmmpcnpikjeljgnaoabkaalbgc";
103}
104
105namespace extensions {
106
107ExtensionPage::ExtensionPage(const GURL& url,
108                             int render_process_id,
109                             int render_view_id,
110                             bool incognito,
111                             bool generated_background_page)
112    : url(url),
113      render_process_id(render_process_id),
114      render_view_id(render_view_id),
115      incognito(incognito),
116      generated_background_page(generated_background_page) {
117}
118
119// On Mac, the install prompt is not modal. This means that the user can
120// navigate while the dialog is up, causing the dialog handler to outlive the
121// ExtensionSettingsHandler. That's a problem because the dialog framework will
122// try to contact us back once the dialog is closed, which causes a crash.
123// This class is designed to broker the message between the two objects, while
124// managing its own lifetime so that it can outlive the ExtensionSettingsHandler
125// and (when doing so) gracefully ignore the message from the dialog.
126class BrokerDelegate : public ExtensionInstallPrompt::Delegate {
127 public:
128  explicit BrokerDelegate(
129      const base::WeakPtr<ExtensionSettingsHandler>& delegate)
130      : delegate_(delegate) {}
131
132  // ExtensionInstallPrompt::Delegate implementation.
133  virtual void InstallUIProceed() OVERRIDE {
134    if (delegate_)
135      delegate_->InstallUIProceed();
136    delete this;
137  };
138
139  virtual void InstallUIAbort(bool user_initiated) OVERRIDE {
140    if (delegate_)
141      delegate_->InstallUIAbort(user_initiated);
142    delete this;
143  };
144
145 private:
146  base::WeakPtr<ExtensionSettingsHandler> delegate_;
147
148  DISALLOW_COPY_AND_ASSIGN(BrokerDelegate);
149};
150
151///////////////////////////////////////////////////////////////////////////////
152//
153// ExtensionSettingsHandler
154//
155///////////////////////////////////////////////////////////////////////////////
156
157ExtensionSettingsHandler::ExtensionSettingsHandler()
158    : extension_service_(NULL),
159      management_policy_(NULL),
160      ignore_notifications_(false),
161      deleting_rvh_(NULL),
162      deleting_rwh_id_(-1),
163      deleting_rph_id_(-1),
164      registered_for_notifications_(false),
165      warning_service_observer_(this),
166      error_console_observer_(this),
167      extension_prefs_observer_(this),
168      extension_registry_observer_(this),
169      should_do_verification_check_(false) {
170}
171
172ExtensionSettingsHandler::~ExtensionSettingsHandler() {
173}
174
175ExtensionSettingsHandler::ExtensionSettingsHandler(ExtensionService* service,
176                                                   ManagementPolicy* policy)
177    : extension_service_(service),
178      management_policy_(policy),
179      ignore_notifications_(false),
180      deleting_rvh_(NULL),
181      deleting_rwh_id_(-1),
182      deleting_rph_id_(-1),
183      registered_for_notifications_(false),
184      warning_service_observer_(this),
185      error_console_observer_(this),
186      extension_prefs_observer_(this),
187      extension_registry_observer_(this),
188      should_do_verification_check_(false) {
189}
190
191// static
192void ExtensionSettingsHandler::RegisterProfilePrefs(
193    user_prefs::PrefRegistrySyncable* registry) {
194  registry->RegisterBooleanPref(
195      prefs::kExtensionsUIDeveloperMode,
196      false,
197      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
198  registry->RegisterBooleanPref(
199      prefs::kExtensionsUIDismissedADTPromo,
200      false,
201      user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
202}
203
204base::DictionaryValue* ExtensionSettingsHandler::CreateExtensionDetailValue(
205    const Extension* extension,
206    const std::vector<ExtensionPage>& pages,
207    const ExtensionWarningService* warning_service) {
208  base::DictionaryValue* extension_data = new base::DictionaryValue();
209  bool enabled = extension_service_->IsExtensionEnabled(extension->id());
210  GetExtensionBasicInfo(extension, enabled, extension_data);
211
212  ExtensionPrefs* prefs = ExtensionPrefs::Get(extension_service_->profile());
213  int disable_reasons = prefs->GetDisableReasons(extension->id());
214
215  bool suspicious_install =
216      (disable_reasons & Extension::DISABLE_NOT_VERIFIED) != 0;
217  extension_data->SetBoolean("suspiciousInstall", suspicious_install);
218  if (suspicious_install)
219    should_do_verification_check_ = true;
220
221  bool corrupt_install =
222      (disable_reasons & Extension::DISABLE_CORRUPTED) != 0;
223  extension_data->SetBoolean("corruptInstall", corrupt_install);
224
225  bool managed_install =
226      !management_policy_->UserMayModifySettings(extension, NULL);
227  extension_data->SetBoolean("managedInstall", managed_install);
228
229  // We should not get into a state where both are true.
230  DCHECK(!managed_install || !suspicious_install);
231
232  GURL icon =
233      ExtensionIconSource::GetIconURL(extension,
234                                      extension_misc::EXTENSION_ICON_MEDIUM,
235                                      ExtensionIconSet::MATCH_BIGGER,
236                                      !enabled, NULL);
237  if (Manifest::IsUnpackedLocation(extension->location()))
238    extension_data->SetString("path", extension->path().value());
239  extension_data->SetString("icon", icon.spec());
240  extension_data->SetBoolean("isUnpacked",
241      Manifest::IsUnpackedLocation(extension->location()));
242  ExtensionRegistry* registry =
243      ExtensionRegistry::Get(extension_service_->profile());
244  extension_data->SetBoolean(
245      "terminated",
246      registry->terminated_extensions().Contains(extension->id()));
247  extension_data->SetBoolean("enabledIncognito",
248      util::IsIncognitoEnabled(extension->id(), extension_service_->profile()));
249  extension_data->SetBoolean("incognitoCanBeEnabled",
250                             extension->can_be_incognito_enabled());
251  extension_data->SetBoolean("wantsFileAccess", extension->wants_file_access());
252  extension_data->SetBoolean("allowFileAccess",
253      util::AllowFileAccess(extension->id(), extension_service_->profile()));
254  extension_data->SetBoolean("allow_reload",
255      Manifest::IsUnpackedLocation(extension->location()));
256  extension_data->SetBoolean("is_hosted_app", extension->is_hosted_app());
257  extension_data->SetBoolean("is_platform_app", extension->is_platform_app());
258  extension_data->SetBoolean("homepageProvided",
259      ManifestURL::GetHomepageURL(extension).is_valid());
260
261  // Extensions only want all URL access if:
262  // - The feature is enabled.
263  // - The extension has access to enough urls that we can't just let it run
264  //   on those specified in the permissions.
265  bool wants_all_urls =
266      FeatureSwitch::scripts_require_action()->IsEnabled() &&
267      extension->permissions_data()->RequiresActionForScriptExecution(
268          extension);
269  extension_data->SetBoolean("wantsAllUrls", wants_all_urls);
270  extension_data->SetBoolean(
271      "allowAllUrls",
272      util::AllowedScriptingOnAllUrls(
273          extension->id(),
274          extension_service_->GetBrowserContext()));
275
276  base::string16 location_text;
277  if (Manifest::IsPolicyLocation(extension->location())) {
278    location_text = l10n_util::GetStringUTF16(
279        IDS_OPTIONS_INSTALL_LOCATION_ENTERPRISE);
280  } else if (extension->location() == Manifest::INTERNAL &&
281      !ManifestURL::UpdatesFromGallery(extension)) {
282    location_text = l10n_util::GetStringUTF16(
283        IDS_OPTIONS_INSTALL_LOCATION_UNKNOWN);
284  } else if (extension->location() == Manifest::EXTERNAL_REGISTRY) {
285    location_text = l10n_util::GetStringUTF16(
286        IDS_OPTIONS_INSTALL_LOCATION_3RD_PARTY);
287  }
288  extension_data->SetString("locationText", location_text);
289
290  base::string16 blacklist_text;
291  switch (prefs->GetExtensionBlacklistState(extension->id())) {
292    case BLACKLISTED_SECURITY_VULNERABILITY:
293      blacklist_text = l10n_util::GetStringUTF16(
294          IDS_OPTIONS_BLACKLISTED_SECURITY_VULNERABILITY);
295      break;
296
297    case BLACKLISTED_CWS_POLICY_VIOLATION:
298      blacklist_text = l10n_util::GetStringUTF16(
299          IDS_OPTIONS_BLACKLISTED_CWS_POLICY_VIOLATION);
300      break;
301
302    case BLACKLISTED_POTENTIALLY_UNWANTED:
303      blacklist_text = l10n_util::GetStringUTF16(
304          IDS_OPTIONS_BLACKLISTED_POTENTIALLY_UNWANTED);
305      break;
306
307    default:
308      break;
309  }
310  extension_data->SetString("blacklistText", blacklist_text);
311
312  // Force unpacked extensions to show at the top.
313  if (Manifest::IsUnpackedLocation(extension->location()))
314    extension_data->SetInteger("order", 1);
315  else
316    extension_data->SetInteger("order", 2);
317
318  if (!ExtensionActionAPI::GetBrowserActionVisibility(prefs, extension->id())) {
319    extension_data->SetBoolean("enable_show_button", true);
320  }
321
322  // Add views
323  base::ListValue* views = new base::ListValue;
324  for (std::vector<ExtensionPage>::const_iterator iter = pages.begin();
325       iter != pages.end(); ++iter) {
326    base::DictionaryValue* view_value = new base::DictionaryValue;
327    if (iter->url.scheme() == kExtensionScheme) {
328      // No leading slash.
329      view_value->SetString("path", iter->url.path().substr(1));
330    } else {
331      // For live pages, use the full URL.
332      view_value->SetString("path", iter->url.spec());
333    }
334    view_value->SetInteger("renderViewId", iter->render_view_id);
335    view_value->SetInteger("renderProcessId", iter->render_process_id);
336    view_value->SetBoolean("incognito", iter->incognito);
337    view_value->SetBoolean("generatedBackgroundPage",
338                           iter->generated_background_page);
339    views->Append(view_value);
340  }
341  extension_data->Set("views", views);
342  ExtensionActionManager* extension_action_manager =
343      ExtensionActionManager::Get(extension_service_->profile());
344  extension_data->SetBoolean(
345      "hasPopupAction",
346      extension_action_manager->GetBrowserAction(*extension) ||
347      extension_action_manager->GetPageAction(*extension));
348
349  // Add warnings.
350  if (warning_service) {
351    std::vector<std::string> warnings =
352        warning_service->GetWarningMessagesForExtension(extension->id());
353
354    if (!warnings.empty()) {
355      base::ListValue* warnings_list = new base::ListValue;
356      for (std::vector<std::string>::const_iterator iter = warnings.begin();
357           iter != warnings.end(); ++iter) {
358        warnings_list->Append(base::Value::CreateStringValue(*iter));
359      }
360      extension_data->Set("warnings", warnings_list);
361    }
362  }
363
364  // If the ErrorConsole is enabled and the extension is unpacked, use the more
365  // detailed errors from the ErrorConsole. Otherwise, use the install warnings
366  // (using both is redundant).
367  ErrorConsole* error_console =
368      ErrorConsole::Get(extension_service_->profile());
369  bool error_console_is_enabled =
370      error_console->IsEnabledForChromeExtensionsPage();
371  extension_data->SetBoolean("wantsErrorCollection", error_console_is_enabled);
372  if (error_console_is_enabled) {
373    extension_data->SetBoolean("errorCollectionEnabled",
374                               error_console->IsReportingEnabledForExtension(
375                                   extension->id()));
376    const ErrorList& errors =
377        error_console->GetErrorsForExtension(extension->id());
378    if (!errors.empty()) {
379      scoped_ptr<base::ListValue> manifest_errors(new base::ListValue);
380      scoped_ptr<base::ListValue> runtime_errors(new base::ListValue);
381      for (ErrorList::const_iterator iter = errors.begin();
382           iter != errors.end(); ++iter) {
383        if ((*iter)->type() == ExtensionError::MANIFEST_ERROR) {
384          manifest_errors->Append((*iter)->ToValue().release());
385        } else {  // Handle runtime error.
386          const RuntimeError* error = static_cast<const RuntimeError*>(*iter);
387          scoped_ptr<base::DictionaryValue> value = error->ToValue();
388          bool can_inspect =
389              !(deleting_rwh_id_ == error->render_view_id() &&
390                deleting_rph_id_ == error->render_process_id()) &&
391              RenderViewHost::FromID(error->render_process_id(),
392                                     error->render_view_id()) != NULL;
393          value->SetBoolean("canInspect", can_inspect);
394          runtime_errors->Append(value.release());
395        }
396      }
397      if (!manifest_errors->empty())
398        extension_data->Set("manifestErrors", manifest_errors.release());
399      if (!runtime_errors->empty())
400        extension_data->Set("runtimeErrors", runtime_errors.release());
401    }
402  } else if (Manifest::IsUnpackedLocation(extension->location())) {
403    const std::vector<InstallWarning>& install_warnings =
404        extension->install_warnings();
405    if (!install_warnings.empty()) {
406      scoped_ptr<base::ListValue> list(new base::ListValue());
407      for (std::vector<InstallWarning>::const_iterator it =
408               install_warnings.begin(); it != install_warnings.end(); ++it) {
409        base::DictionaryValue* item = new base::DictionaryValue();
410        item->SetString("message", it->message);
411        list->Append(item);
412      }
413      extension_data->Set("installWarnings", list.release());
414    }
415  }
416
417  return extension_data;
418}
419
420void ExtensionSettingsHandler::GetLocalizedValues(
421    content::WebUIDataSource* source) {
422  source->AddString("extensionSettings",
423      l10n_util::GetStringUTF16(IDS_MANAGE_EXTENSIONS_SETTING_WINDOWS_TITLE));
424
425  source->AddString("extensionSettingsDeveloperMode",
426      l10n_util::GetStringUTF16(IDS_EXTENSIONS_DEVELOPER_MODE_LINK));
427  source->AddString("extensionSettingsNoExtensions",
428      l10n_util::GetStringUTF16(IDS_EXTENSIONS_NONE_INSTALLED));
429  source->AddString(
430      "extensionSettingsSuggestGallery",
431      l10n_util::GetStringFUTF16(
432          IDS_EXTENSIONS_NONE_INSTALLED_SUGGEST_GALLERY,
433          base::ASCIIToUTF16(
434              google_util::AppendGoogleLocaleParam(
435                  GURL(extension_urls::GetExtensionGalleryURL()),
436                  g_browser_process->GetApplicationLocale()).spec())));
437  source->AddString("extensionSettingsGetMoreExtensions",
438      l10n_util::GetStringUTF16(IDS_GET_MORE_EXTENSIONS));
439  source->AddString("extensionSettingsGetMoreExtensionsUrl",
440                    base::ASCIIToUTF16(
441                        google_util::AppendGoogleLocaleParam(
442                            GURL(extension_urls::GetExtensionGalleryURL()),
443                            g_browser_process->GetApplicationLocale()).spec()));
444  source->AddString("extensionSettingsExtensionId",
445      l10n_util::GetStringUTF16(IDS_EXTENSIONS_ID));
446  source->AddString("extensionSettingsExtensionPath",
447      l10n_util::GetStringUTF16(IDS_EXTENSIONS_PATH));
448  source->AddString("extensionSettingsInspectViews",
449      l10n_util::GetStringUTF16(IDS_EXTENSIONS_INSPECT_VIEWS));
450  source->AddString("extensionSettingsInstallWarnings",
451      l10n_util::GetStringUTF16(IDS_EXTENSIONS_INSTALL_WARNINGS));
452  source->AddString("viewIncognito",
453      l10n_util::GetStringUTF16(IDS_EXTENSIONS_VIEW_INCOGNITO));
454  source->AddString("viewInactive",
455      l10n_util::GetStringUTF16(IDS_EXTENSIONS_VIEW_INACTIVE));
456  source->AddString("backgroundPage",
457      l10n_util::GetStringUTF16(IDS_EXTENSIONS_BACKGROUND_PAGE));
458  source->AddString("extensionSettingsEnable",
459      l10n_util::GetStringUTF16(IDS_EXTENSIONS_ENABLE));
460  source->AddString("extensionSettingsEnabled",
461      l10n_util::GetStringUTF16(IDS_EXTENSIONS_ENABLED));
462  source->AddString("extensionSettingsRemove",
463      l10n_util::GetStringUTF16(IDS_EXTENSIONS_REMOVE));
464  source->AddString("extensionSettingsEnableIncognito",
465      l10n_util::GetStringUTF16(IDS_EXTENSIONS_ENABLE_INCOGNITO));
466  source->AddString("extensionSettingsEnableErrorCollection",
467      l10n_util::GetStringUTF16(IDS_EXTENSIONS_ENABLE_ERROR_COLLECTION));
468  source->AddString("extensionSettingsAllowFileAccess",
469      l10n_util::GetStringUTF16(IDS_EXTENSIONS_ALLOW_FILE_ACCESS));
470  source->AddString("extensionSettingsAllowOnAllUrls",
471      l10n_util::GetStringUTF16(IDS_EXTENSIONS_ALLOW_ON_ALL_URLS));
472  source->AddString("extensionSettingsIncognitoWarning",
473      l10n_util::GetStringUTF16(IDS_EXTENSIONS_INCOGNITO_WARNING));
474  source->AddString("extensionSettingsReloadTerminated",
475      l10n_util::GetStringUTF16(IDS_EXTENSIONS_RELOAD_TERMINATED));
476  source->AddString("extensionSettingsLaunch",
477      l10n_util::GetStringUTF16(IDS_EXTENSIONS_LAUNCH));
478  source->AddString("extensionSettingsReloadUnpacked",
479      l10n_util::GetStringUTF16(IDS_EXTENSIONS_RELOAD_UNPACKED));
480  source->AddString("extensionSettingsOptions",
481      l10n_util::GetStringUTF16(IDS_EXTENSIONS_OPTIONS_LINK));
482  source->AddString("extensionSettingsPermissions",
483      l10n_util::GetStringUTF16(IDS_EXTENSIONS_PERMISSIONS_LINK));
484  source->AddString("extensionSettingsVisitWebsite",
485      l10n_util::GetStringUTF16(IDS_EXTENSIONS_VISIT_WEBSITE));
486  source->AddString("extensionSettingsVisitWebStore",
487      l10n_util::GetStringUTF16(IDS_EXTENSIONS_VISIT_WEBSTORE));
488  source->AddString("extensionSettingsPolicyControlled",
489      l10n_util::GetStringUTF16(IDS_EXTENSIONS_POLICY_CONTROLLED));
490  source->AddString("extensionSettingsManagedMode",
491      l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOCKED_MANAGED_USER));
492  source->AddString("extensionSettingsCorruptInstall",
493      l10n_util::GetStringUTF16(
494          IDS_EXTENSIONS_CORRUPTED_EXTENSION));
495  source->AddString("extensionSettingsSuspiciousInstall",
496      l10n_util::GetStringFUTF16(
497          IDS_EXTENSIONS_ADDED_WITHOUT_KNOWLEDGE,
498          l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE)));
499  source->AddString("extensionSettingsLearnMore",
500      l10n_util::GetStringUTF16(IDS_LEARN_MORE));
501  source->AddString("extensionSettingsCorruptInstallHelpUrl",
502                    base::ASCIIToUTF16(
503                        google_util::AppendGoogleLocaleParam(
504                            GURL(chrome::kCorruptExtensionURL),
505                            g_browser_process->GetApplicationLocale()).spec()));
506  source->AddString("extensionSettingsSuspiciousInstallHelpUrl",
507                    base::ASCIIToUTF16(
508                        google_util::AppendGoogleLocaleParam(
509                            GURL(chrome::kRemoveNonCWSExtensionURL),
510                            g_browser_process->GetApplicationLocale()).spec()));
511  source->AddString("extensionSettingsShowButton",
512      l10n_util::GetStringUTF16(IDS_EXTENSIONS_SHOW_BUTTON));
513  source->AddString("extensionSettingsLoadUnpackedButton",
514      l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOAD_UNPACKED_BUTTON));
515  source->AddString("extensionSettingsPackButton",
516      l10n_util::GetStringUTF16(IDS_EXTENSIONS_PACK_BUTTON));
517  source->AddString("extensionSettingsCommandsLink",
518      l10n_util::GetStringUTF16(IDS_EXTENSIONS_COMMANDS_CONFIGURE));
519  source->AddString("extensionSettingsUpdateButton",
520      l10n_util::GetStringUTF16(IDS_EXTENSIONS_UPDATE_BUTTON));
521  source->AddString(
522      "extensionSettingsAppsDevToolsPromoHTML",
523      l10n_util::GetStringFUTF16(
524          IDS_EXTENSIONS_APPS_DEV_TOOLS_PROMO_HTML,
525          base::ASCIIToUTF16(
526              google_util::AppendGoogleLocaleParam(
527                  GURL(extension_urls::GetWebstoreItemDetailURLPrefix() +
528                       kAppsDeveloperToolsExtensionId),
529                  g_browser_process->GetApplicationLocale()).spec())));
530  source->AddString(
531      "extensionSettingsAppDevToolsPromoClose",
532      l10n_util::GetStringUTF16(IDS_CLOSE));
533  source->AddString("extensionSettingsCrashMessage",
534      l10n_util::GetStringUTF16(IDS_EXTENSIONS_CRASHED_EXTENSION));
535  source->AddString("extensionSettingsInDevelopment",
536      l10n_util::GetStringUTF16(IDS_EXTENSIONS_IN_DEVELOPMENT));
537  source->AddString("extensionSettingsWarningsTitle",
538      l10n_util::GetStringUTF16(IDS_EXTENSION_WARNINGS_TITLE));
539  source->AddString("extensionSettingsShowDetails",
540      l10n_util::GetStringUTF16(IDS_EXTENSIONS_SHOW_DETAILS));
541  source->AddString("extensionSettingsHideDetails",
542      l10n_util::GetStringUTF16(IDS_EXTENSIONS_HIDE_DETAILS));
543
544  // TODO(estade): comb through the above strings to find ones no longer used in
545  // uber extensions.
546  source->AddString("extensionUninstall",
547      l10n_util::GetStringUTF16(IDS_EXTENSIONS_UNINSTALL));
548}
549
550void ExtensionSettingsHandler::RenderViewDeleted(
551    RenderViewHost* render_view_host) {
552  deleting_rvh_ = render_view_host;
553  Profile* source_profile = Profile::FromBrowserContext(
554      render_view_host->GetSiteInstance()->GetBrowserContext());
555  if (!Profile::FromWebUI(web_ui())->IsSameProfile(source_profile))
556    return;
557  MaybeUpdateAfterNotification();
558}
559
560void ExtensionSettingsHandler::DidStartNavigationToPendingEntry(
561    const GURL& url,
562    content::NavigationController::ReloadType reload_type) {
563  if (reload_type != content::NavigationController::NO_RELOAD)
564    ReloadUnpackedExtensions();
565}
566
567void ExtensionSettingsHandler::RegisterMessages() {
568  // Don't override an |extension_service_| or |management_policy_| injected
569  // for testing.
570  if (!extension_service_) {
571    extension_service_ = Profile::FromWebUI(web_ui())->GetOriginalProfile()->
572        GetExtensionService();
573  }
574  if (!management_policy_) {
575    management_policy_ = ExtensionSystem::Get(
576        extension_service_->profile())->management_policy();
577  }
578
579  web_ui()->RegisterMessageCallback("extensionSettingsRequestExtensionsData",
580      base::Bind(&ExtensionSettingsHandler::HandleRequestExtensionsData,
581                 AsWeakPtr()));
582  web_ui()->RegisterMessageCallback("extensionSettingsToggleDeveloperMode",
583      base::Bind(&ExtensionSettingsHandler::HandleToggleDeveloperMode,
584                 AsWeakPtr()));
585  web_ui()->RegisterMessageCallback("extensionSettingsInspect",
586      base::Bind(&ExtensionSettingsHandler::HandleInspectMessage,
587                 AsWeakPtr()));
588  web_ui()->RegisterMessageCallback("extensionSettingsLaunch",
589      base::Bind(&ExtensionSettingsHandler::HandleLaunchMessage,
590                 AsWeakPtr()));
591  web_ui()->RegisterMessageCallback("extensionSettingsReload",
592      base::Bind(&ExtensionSettingsHandler::HandleReloadMessage,
593                 AsWeakPtr()));
594  web_ui()->RegisterMessageCallback("extensionSettingsEnable",
595      base::Bind(&ExtensionSettingsHandler::HandleEnableMessage,
596                 AsWeakPtr()));
597  web_ui()->RegisterMessageCallback("extensionSettingsEnableIncognito",
598      base::Bind(&ExtensionSettingsHandler::HandleEnableIncognitoMessage,
599                 AsWeakPtr()));
600  web_ui()->RegisterMessageCallback("extensionSettingsEnableErrorCollection",
601      base::Bind(&ExtensionSettingsHandler::HandleEnableErrorCollectionMessage,
602                 AsWeakPtr()));
603  web_ui()->RegisterMessageCallback("extensionSettingsAllowFileAccess",
604      base::Bind(&ExtensionSettingsHandler::HandleAllowFileAccessMessage,
605                 AsWeakPtr()));
606  web_ui()->RegisterMessageCallback("extensionSettingsAllowOnAllUrls",
607      base::Bind(&ExtensionSettingsHandler::HandleAllowOnAllUrlsMessage,
608                 AsWeakPtr()));
609  web_ui()->RegisterMessageCallback("extensionSettingsUninstall",
610      base::Bind(&ExtensionSettingsHandler::HandleUninstallMessage,
611                 AsWeakPtr()));
612  web_ui()->RegisterMessageCallback("extensionSettingsOptions",
613      base::Bind(&ExtensionSettingsHandler::HandleOptionsMessage,
614                 AsWeakPtr()));
615  web_ui()->RegisterMessageCallback("extensionSettingsPermissions",
616      base::Bind(&ExtensionSettingsHandler::HandlePermissionsMessage,
617                 AsWeakPtr()));
618  web_ui()->RegisterMessageCallback("extensionSettingsShowButton",
619      base::Bind(&ExtensionSettingsHandler::HandleShowButtonMessage,
620                 AsWeakPtr()));
621  web_ui()->RegisterMessageCallback("extensionSettingsAutoupdate",
622      base::Bind(&ExtensionSettingsHandler::HandleAutoUpdateMessage,
623                 AsWeakPtr()));
624  web_ui()->RegisterMessageCallback("extensionSettingsDismissADTPromo",
625      base::Bind(&ExtensionSettingsHandler::HandleDismissADTPromoMessage,
626                 AsWeakPtr()));
627}
628
629void ExtensionSettingsHandler::OnErrorAdded(const ExtensionError* error) {
630  MaybeUpdateAfterNotification();
631}
632
633void ExtensionSettingsHandler::Observe(
634    int type,
635    const content::NotificationSource& source,
636    const content::NotificationDetails& details) {
637  Profile* profile = Profile::FromWebUI(web_ui());
638  Profile* source_profile = NULL;
639  switch (type) {
640    // We listen for notifications that will result in the page being
641    // repopulated with data twice for the same event in certain cases.
642    // For instance, EXTENSION_LOADED & EXTENSION_HOST_CREATED because
643    // we don't know about the views for an extension at EXTENSION_LOADED, but
644    // if we only listen to EXTENSION_HOST_CREATED, we'll miss extensions
645    // that don't have a process at startup.
646    //
647    // Doing it this way gets everything but causes the page to be rendered
648    // more than we need. It doesn't seem to result in any noticeable flicker.
649    case chrome::NOTIFICATION_BACKGROUND_CONTENTS_DELETED:
650      deleting_rvh_ = content::Details<BackgroundContents>(details)->
651          web_contents()->GetRenderViewHost();
652      // Fall through.
653    case chrome::NOTIFICATION_BACKGROUND_CONTENTS_NAVIGATED:
654    case chrome::NOTIFICATION_EXTENSION_HOST_CREATED:
655      source_profile = content::Source<Profile>(source).ptr();
656      if (!profile->IsSameProfile(source_profile))
657        return;
658      MaybeUpdateAfterNotification();
659      break;
660    case content::NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED: {
661      content::RenderWidgetHost* rwh =
662          content::Source<content::RenderWidgetHost>(source).ptr();
663      deleting_rwh_id_ = rwh->GetRoutingID();
664      deleting_rph_id_ = rwh->GetProcess()->GetID();
665      MaybeUpdateAfterNotification();
666      break;
667    }
668    case chrome::NOTIFICATION_EXTENSION_UPDATE_DISABLED:
669    case chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED:
670      MaybeUpdateAfterNotification();
671      break;
672    case chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED:
673       // This notification is sent when the extension host destruction begins,
674       // not when it finishes. We use PostTask to delay the update until after
675       // the destruction finishes.
676       base::MessageLoop::current()->PostTask(
677           FROM_HERE,
678           base::Bind(&ExtensionSettingsHandler::MaybeUpdateAfterNotification,
679                      AsWeakPtr()));
680       break;
681    default:
682      NOTREACHED();
683  }
684}
685
686void ExtensionSettingsHandler::OnExtensionLoaded(
687    content::BrowserContext* browser_context,
688    const Extension* extension) {
689  MaybeUpdateAfterNotification();
690}
691
692void ExtensionSettingsHandler::OnExtensionUnloaded(
693    content::BrowserContext* browser_context,
694    const Extension* extension,
695    UnloadedExtensionInfo::Reason reason) {
696  MaybeUpdateAfterNotification();
697}
698
699void ExtensionSettingsHandler::OnExtensionUninstalled(
700    content::BrowserContext* browser_context,
701    const Extension* extension) {
702  MaybeUpdateAfterNotification();
703}
704
705void ExtensionSettingsHandler::OnExtensionDisableReasonsChanged(
706    const std::string& extension_id, int disable_reasons) {
707  MaybeUpdateAfterNotification();
708}
709
710void ExtensionSettingsHandler::ExtensionUninstallAccepted() {
711  DCHECK(!extension_id_prompting_.empty());
712
713  bool was_terminated = false;
714
715  // The extension can be uninstalled in another window while the UI was
716  // showing. Do nothing in that case.
717  const Extension* extension =
718      extension_service_->GetExtensionById(extension_id_prompting_, true);
719  if (!extension) {
720    extension =
721        ExtensionRegistry::Get(Profile::FromWebUI(web_ui()))->GetExtensionById(
722            extension_id_prompting_, ExtensionRegistry::TERMINATED);
723    was_terminated = true;
724  }
725  if (!extension)
726    return;
727
728  extension_service_->UninstallExtension(extension_id_prompting_,
729                                         false,  // External uninstall.
730                                         NULL);  // Error.
731  extension_id_prompting_ = "";
732
733  // There will be no EXTENSION_UNLOADED notification for terminated
734  // extensions as they were already unloaded.
735  if (was_terminated)
736    HandleRequestExtensionsData(NULL);
737}
738
739void ExtensionSettingsHandler::ExtensionUninstallCanceled() {
740  extension_id_prompting_ = "";
741}
742
743void ExtensionSettingsHandler::ExtensionWarningsChanged() {
744  MaybeUpdateAfterNotification();
745}
746
747// This is called when the user clicks "Revoke File Access."
748void ExtensionSettingsHandler::InstallUIProceed() {
749  Profile* profile = Profile::FromWebUI(web_ui());
750  apps::SavedFilesService::Get(profile)->ClearQueue(
751      extension_service_->GetExtensionById(extension_id_prompting_, true));
752  if (apps::AppRestoreService::Get(profile)->
753          IsAppRestorable(extension_id_prompting_)) {
754    apps::AppLoadService::Get(profile)->RestartApplication(
755        extension_id_prompting_);
756  }
757  extension_id_prompting_.clear();
758}
759
760void ExtensionSettingsHandler::InstallUIAbort(bool user_initiated) {
761  extension_id_prompting_.clear();
762}
763
764void ExtensionSettingsHandler::ReloadUnpackedExtensions() {
765  const ExtensionSet* extensions = extension_service_->extensions();
766  std::vector<const Extension*> unpacked_extensions;
767  for (ExtensionSet::const_iterator extension = extensions->begin();
768       extension != extensions->end(); ++extension) {
769    if (Manifest::IsUnpackedLocation((*extension)->location()))
770      unpacked_extensions.push_back(extension->get());
771  }
772
773  for (std::vector<const Extension*>::iterator iter =
774       unpacked_extensions.begin(); iter != unpacked_extensions.end(); ++iter) {
775    extension_service_->ReloadExtension((*iter)->id());
776  }
777}
778
779void ExtensionSettingsHandler::HandleRequestExtensionsData(
780    const base::ListValue* args) {
781  base::DictionaryValue results;
782
783  Profile* profile = Profile::FromWebUI(web_ui());
784
785  // Add the extensions to the results structure.
786  base::ListValue* extensions_list = new base::ListValue();
787
788  ExtensionWarningService* warnings =
789      ExtensionSystem::Get(profile)->warning_service();
790
791  ExtensionRegistry* registry = ExtensionRegistry::Get(profile);
792  const ExtensionSet& enabled_set = registry->enabled_extensions();
793  for (ExtensionSet::const_iterator extension = enabled_set.begin();
794       extension != enabled_set.end(); ++extension) {
795    if (ui_util::ShouldDisplayInExtensionSettings(*extension, profile)) {
796      extensions_list->Append(CreateExtensionDetailValue(
797          extension->get(),
798          GetInspectablePagesForExtension(extension->get(), true),
799          warnings));
800    }
801  }
802  const ExtensionSet& disabled_set = registry->disabled_extensions();
803  for (ExtensionSet::const_iterator extension = disabled_set.begin();
804       extension != disabled_set.end(); ++extension) {
805    if (ui_util::ShouldDisplayInExtensionSettings(*extension, profile)) {
806      extensions_list->Append(CreateExtensionDetailValue(
807          extension->get(),
808          GetInspectablePagesForExtension(extension->get(), false),
809          warnings));
810    }
811  }
812  const ExtensionSet& terminated_set = registry->terminated_extensions();
813  std::vector<ExtensionPage> empty_pages;
814  for (ExtensionSet::const_iterator extension = terminated_set.begin();
815       extension != terminated_set.end(); ++extension) {
816    if (ui_util::ShouldDisplayInExtensionSettings(*extension, profile)) {
817      extensions_list->Append(CreateExtensionDetailValue(
818          extension->get(),
819          empty_pages,  // Terminated process has no active pages.
820          warnings));
821    }
822  }
823  results.Set("extensions", extensions_list);
824
825  bool is_supervised = profile->IsSupervised();
826  bool developer_mode =
827      !is_supervised &&
828      profile->GetPrefs()->GetBoolean(prefs::kExtensionsUIDeveloperMode);
829  results.SetBoolean("profileIsManaged", is_supervised);
830  results.SetBoolean("developerMode", developer_mode);
831
832  // Promote the Chrome Apps & Extensions Developer Tools if they are not
833  // installed and the user has not previously dismissed the warning.
834  bool promote_apps_dev_tools = false;
835  if (!ExtensionRegistry::Get(Profile::FromWebUI(web_ui()))->
836          GetExtensionById(kAppsDeveloperToolsExtensionId,
837                           ExtensionRegistry::EVERYTHING) &&
838      !profile->GetPrefs()->GetBoolean(prefs::kExtensionsUIDismissedADTPromo)) {
839    promote_apps_dev_tools = true;
840  }
841  results.SetBoolean("promoteAppsDevTools", promote_apps_dev_tools);
842
843  bool load_unpacked_disabled =
844      ExtensionPrefs::Get(profile)->ExtensionsBlacklistedByDefault();
845  results.SetBoolean("loadUnpackedDisabled", load_unpacked_disabled);
846
847  web_ui()->CallJavascriptFunction(
848      "extensions.ExtensionSettings.returnExtensionsData", results);
849
850  MaybeRegisterForNotifications();
851  UMA_HISTOGRAM_BOOLEAN("ExtensionSettings.ShouldDoVerificationCheck",
852                        should_do_verification_check_);
853  if (should_do_verification_check_) {
854    should_do_verification_check_ = false;
855    ExtensionSystem::Get(Profile::FromWebUI(web_ui()))
856        ->install_verifier()
857        ->VerifyAllExtensions();
858  }
859}
860
861void ExtensionSettingsHandler::HandleToggleDeveloperMode(
862    const base::ListValue* args) {
863  Profile* profile = Profile::FromWebUI(web_ui());
864  if (profile->IsSupervised())
865    return;
866
867  bool developer_mode =
868      !profile->GetPrefs()->GetBoolean(prefs::kExtensionsUIDeveloperMode);
869  profile->GetPrefs()->SetBoolean(prefs::kExtensionsUIDeveloperMode,
870                                  developer_mode);
871}
872
873void ExtensionSettingsHandler::HandleInspectMessage(
874    const base::ListValue* args) {
875  std::string extension_id;
876  std::string render_process_id_str;
877  std::string render_view_id_str;
878  int render_process_id;
879  int render_view_id;
880  bool incognito;
881  CHECK_EQ(4U, args->GetSize());
882  CHECK(args->GetString(0, &extension_id));
883  CHECK(args->GetString(1, &render_process_id_str));
884  CHECK(args->GetString(2, &render_view_id_str));
885  CHECK(args->GetBoolean(3, &incognito));
886  CHECK(base::StringToInt(render_process_id_str, &render_process_id));
887  CHECK(base::StringToInt(render_view_id_str, &render_view_id));
888
889  if (render_process_id == -1) {
890    // This message is for a lazy background page. Start the page if necessary.
891    const Extension* extension =
892        extension_service_->extensions()->GetByID(extension_id);
893    DCHECK(extension);
894    Profile* profile = Profile::FromWebUI(web_ui());
895    if (incognito)
896      profile = profile->GetOffTheRecordProfile();
897    devtools_util::InspectBackgroundPage(extension, profile);
898    return;
899  }
900
901  RenderViewHost* host = RenderViewHost::FromID(render_process_id,
902                                                render_view_id);
903  if (!host) {
904    // This can happen if the host has gone away since the page was displayed.
905    return;
906  }
907
908  DevToolsWindow::OpenDevToolsWindow(host);
909}
910
911void ExtensionSettingsHandler::HandleLaunchMessage(
912    const base::ListValue* args) {
913  CHECK_EQ(1U, args->GetSize());
914  std::string extension_id;
915  CHECK(args->GetString(0, &extension_id));
916  const Extension* extension =
917      extension_service_->GetExtensionById(extension_id, false);
918  OpenApplication(AppLaunchParams(extension_service_->profile(), extension,
919                                  extensions::LAUNCH_CONTAINER_WINDOW,
920                                  NEW_WINDOW));
921}
922
923void ExtensionSettingsHandler::HandleReloadMessage(
924    const base::ListValue* args) {
925  std::string extension_id = base::UTF16ToUTF8(ExtractStringValue(args));
926  CHECK(!extension_id.empty());
927  extension_service_->ReloadExtension(extension_id);
928}
929
930void ExtensionSettingsHandler::HandleEnableMessage(
931    const base::ListValue* args) {
932  CHECK_EQ(2U, args->GetSize());
933  std::string extension_id, enable_str;
934  CHECK(args->GetString(0, &extension_id));
935  CHECK(args->GetString(1, &enable_str));
936
937  const Extension* extension =
938      extension_service_->GetInstalledExtension(extension_id);
939  if (!extension)
940    return;
941
942  if (!management_policy_->UserMayModifySettings(extension, NULL)) {
943    LOG(ERROR) << "An attempt was made to enable an extension that is "
944               << "non-usermanagable. Extension id: " << extension->id();
945    return;
946  }
947
948  if (enable_str == "true") {
949    ExtensionPrefs* prefs = ExtensionPrefs::Get(extension_service_->profile());
950    if (prefs->DidExtensionEscalatePermissions(extension_id)) {
951      ShowExtensionDisabledDialog(
952          extension_service_, web_ui()->GetWebContents(), extension);
953    } else if ((prefs->GetDisableReasons(extension_id) &
954                   Extension::DISABLE_UNSUPPORTED_REQUIREMENT) &&
955               !requirements_checker_.get()) {
956      // Recheck the requirements.
957      scoped_refptr<const Extension> extension =
958          extension_service_->GetExtensionById(extension_id,
959                                               true /* include disabled */);
960      requirements_checker_.reset(new RequirementsChecker);
961      requirements_checker_->Check(
962          extension,
963          base::Bind(&ExtensionSettingsHandler::OnRequirementsChecked,
964                     AsWeakPtr(), extension_id));
965    } else {
966      extension_service_->EnableExtension(extension_id);
967    }
968  } else {
969    extension_service_->DisableExtension(
970        extension_id, Extension::DISABLE_USER_ACTION);
971  }
972}
973
974void ExtensionSettingsHandler::HandleEnableIncognitoMessage(
975    const base::ListValue* args) {
976  CHECK_EQ(2U, args->GetSize());
977  std::string extension_id, enable_str;
978  CHECK(args->GetString(0, &extension_id));
979  CHECK(args->GetString(1, &enable_str));
980  const Extension* extension =
981      extension_service_->GetInstalledExtension(extension_id);
982  if (!extension)
983    return;
984
985  // Flipping the incognito bit will generate unload/load notifications for the
986  // extension, but we don't want to reload the page, because a) we've already
987  // updated the UI to reflect the change, and b) we want the yellow warning
988  // text to stay until the user has left the page.
989  //
990  // TODO(aa): This creates crappiness in some cases. For example, in a main
991  // window, when toggling this, the browser action will flicker because it gets
992  // unloaded, then reloaded. It would be better to have a dedicated
993  // notification for this case.
994  //
995  // Bug: http://crbug.com/41384
996  base::AutoReset<bool> auto_reset_ignore_notifications(
997      &ignore_notifications_, true);
998  util::SetIsIncognitoEnabled(extension->id(),
999                              extension_service_->profile(),
1000                              enable_str == "true");
1001}
1002
1003void ExtensionSettingsHandler::HandleEnableErrorCollectionMessage(
1004    const base::ListValue* args) {
1005  CHECK_EQ(2u, args->GetSize());
1006  std::string extension_id;
1007  std::string enable_str;
1008  CHECK(args->GetString(0, &extension_id));
1009  CHECK(args->GetString(1, &enable_str));
1010  bool enabled = enable_str == "true";
1011  ErrorConsole::Get(Profile::FromWebUI(web_ui()))
1012      ->SetReportingAllForExtension(extension_id, enabled);
1013}
1014
1015void ExtensionSettingsHandler::HandleAllowFileAccessMessage(
1016    const base::ListValue* args) {
1017  CHECK_EQ(2U, args->GetSize());
1018  std::string extension_id, allow_str;
1019  CHECK(args->GetString(0, &extension_id));
1020  CHECK(args->GetString(1, &allow_str));
1021  const Extension* extension =
1022      extension_service_->GetInstalledExtension(extension_id);
1023  if (!extension)
1024    return;
1025
1026  if (!management_policy_->UserMayModifySettings(extension, NULL)) {
1027    LOG(ERROR) << "An attempt was made to change allow file access of an"
1028               << " extension that is non-usermanagable. Extension id : "
1029               << extension->id();
1030    return;
1031  }
1032
1033  util::SetAllowFileAccess(
1034      extension_id, extension_service_->profile(), allow_str == "true");
1035}
1036
1037void ExtensionSettingsHandler::HandleAllowOnAllUrlsMessage(
1038    const base::ListValue* args) {
1039  DCHECK(FeatureSwitch::scripts_require_action()->IsEnabled());
1040  CHECK_EQ(2u, args->GetSize());
1041  std::string extension_id;
1042  std::string allow_str;
1043  CHECK(args->GetString(0, &extension_id));
1044  CHECK(args->GetString(1, &allow_str));
1045  util::SetAllowedScriptingOnAllUrls(extension_id,
1046                                     extension_service_->GetBrowserContext(),
1047                                     allow_str == "true");
1048}
1049
1050void ExtensionSettingsHandler::HandleUninstallMessage(
1051    const base::ListValue* args) {
1052  CHECK_EQ(1U, args->GetSize());
1053  std::string extension_id;
1054  CHECK(args->GetString(0, &extension_id));
1055  const Extension* extension =
1056      extension_service_->GetInstalledExtension(extension_id);
1057  if (!extension)
1058    return;
1059
1060  if (!management_policy_->UserMayModifySettings(extension, NULL)) {
1061    LOG(ERROR) << "An attempt was made to uninstall an extension that is "
1062               << "non-usermanagable. Extension id : " << extension->id();
1063    return;
1064  }
1065
1066  if (!extension_id_prompting_.empty())
1067    return;  // Only one prompt at a time.
1068
1069  extension_id_prompting_ = extension_id;
1070
1071  GetExtensionUninstallDialog()->ConfirmUninstall(extension);
1072}
1073
1074void ExtensionSettingsHandler::HandleOptionsMessage(
1075    const base::ListValue* args) {
1076  const Extension* extension = GetActiveExtension(args);
1077  if (!extension || ManifestURL::GetOptionsPage(extension).is_empty())
1078    return;
1079  ExtensionTabUtil::OpenOptionsPage(extension,
1080      chrome::FindBrowserWithWebContents(web_ui()->GetWebContents()));
1081}
1082
1083void ExtensionSettingsHandler::HandlePermissionsMessage(
1084    const base::ListValue* args) {
1085  std::string extension_id(base::UTF16ToUTF8(ExtractStringValue(args)));
1086  CHECK(!extension_id.empty());
1087  const Extension* extension =
1088      ExtensionRegistry::Get(Profile::FromWebUI(web_ui()))
1089          ->GetExtensionById(extension_id, ExtensionRegistry::EVERYTHING);
1090  if (!extension)
1091    return;
1092
1093  if (!extension_id_prompting_.empty())
1094    return;  // Only one prompt at a time.
1095
1096  extension_id_prompting_ = extension->id();
1097  prompt_.reset(new ExtensionInstallPrompt(web_contents()));
1098  std::vector<base::FilePath> retained_file_paths;
1099  if (extension->permissions_data()->HasAPIPermission(
1100          APIPermission::kFileSystem)) {
1101    std::vector<apps::SavedFileEntry> retained_file_entries =
1102        apps::SavedFilesService::Get(Profile::FromWebUI(
1103            web_ui()))->GetAllFileEntries(extension_id_prompting_);
1104    for (size_t i = 0; i < retained_file_entries.size(); ++i) {
1105      retained_file_paths.push_back(retained_file_entries[i].path);
1106    }
1107  }
1108  // The BrokerDelegate manages its own lifetime.
1109  prompt_->ReviewPermissions(
1110      new BrokerDelegate(AsWeakPtr()), extension, retained_file_paths);
1111}
1112
1113void ExtensionSettingsHandler::HandleShowButtonMessage(
1114    const base::ListValue* args) {
1115  const Extension* extension = GetActiveExtension(args);
1116  if (!extension)
1117    return;
1118  ExtensionActionAPI::SetBrowserActionVisibility(
1119      ExtensionPrefs::Get(extension_service_->profile()),
1120      extension->id(),
1121      true);
1122}
1123
1124void ExtensionSettingsHandler::HandleAutoUpdateMessage(
1125    const base::ListValue* args) {
1126  ExtensionUpdater* updater = extension_service_->updater();
1127  if (updater) {
1128    ExtensionUpdater::CheckParams params;
1129    params.install_immediately = true;
1130    updater->CheckNow(params);
1131  }
1132}
1133
1134void ExtensionSettingsHandler::HandleDismissADTPromoMessage(
1135    const base::ListValue* args) {
1136  DCHECK(args->empty());
1137  Profile::FromWebUI(web_ui())->GetPrefs()->SetBoolean(
1138      prefs::kExtensionsUIDismissedADTPromo, true);
1139}
1140
1141void ExtensionSettingsHandler::ShowAlert(const std::string& message) {
1142  base::ListValue arguments;
1143  arguments.Append(base::Value::CreateStringValue(message));
1144  web_ui()->CallJavascriptFunction("alert", arguments);
1145}
1146
1147const Extension* ExtensionSettingsHandler::GetActiveExtension(
1148    const base::ListValue* args) {
1149  std::string extension_id = base::UTF16ToUTF8(ExtractStringValue(args));
1150  CHECK(!extension_id.empty());
1151  return extension_service_->GetExtensionById(extension_id, false);
1152}
1153
1154void ExtensionSettingsHandler::MaybeUpdateAfterNotification() {
1155  WebContents* contents = web_ui()->GetWebContents();
1156  if (!ignore_notifications_ && contents && contents->GetRenderViewHost())
1157    HandleRequestExtensionsData(NULL);
1158  deleting_rvh_ = NULL;
1159}
1160
1161void ExtensionSettingsHandler::MaybeRegisterForNotifications() {
1162  if (registered_for_notifications_)
1163    return;
1164
1165  registered_for_notifications_  = true;
1166  Profile* profile = Profile::FromWebUI(web_ui());
1167
1168  // Register for notifications that we need to reload the page.
1169  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UPDATE_DISABLED,
1170                 content::Source<Profile>(profile));
1171  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_CREATED,
1172                 content::NotificationService::AllBrowserContextsAndSources());
1173  registrar_.Add(this,
1174                 chrome::NOTIFICATION_BACKGROUND_CONTENTS_NAVIGATED,
1175                 content::NotificationService::AllBrowserContextsAndSources());
1176  registrar_.Add(this,
1177                 chrome::NOTIFICATION_BACKGROUND_CONTENTS_DELETED,
1178                 content::NotificationService::AllBrowserContextsAndSources());
1179  registrar_.Add(
1180      this,
1181      chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED,
1182      content::Source<ExtensionPrefs>(ExtensionPrefs::Get(profile)));
1183  registrar_.Add(this,
1184                 chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED,
1185                 content::NotificationService::AllBrowserContextsAndSources());
1186  registrar_.Add(this,
1187                 content::NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED,
1188                 content::NotificationService::AllBrowserContextsAndSources());
1189
1190  extension_registry_observer_.Add(ExtensionRegistry::Get(profile));
1191
1192  content::WebContentsObserver::Observe(web_ui()->GetWebContents());
1193
1194  warning_service_observer_.Add(
1195      ExtensionSystem::Get(profile)->warning_service());
1196
1197  error_console_observer_.Add(ErrorConsole::Get(profile));
1198
1199  base::Closure callback = base::Bind(
1200      &ExtensionSettingsHandler::MaybeUpdateAfterNotification,
1201      AsWeakPtr());
1202
1203  pref_registrar_.Init(profile->GetPrefs());
1204  pref_registrar_.Add(pref_names::kInstallDenyList, callback);
1205}
1206
1207std::vector<ExtensionPage>
1208ExtensionSettingsHandler::GetInspectablePagesForExtension(
1209    const Extension* extension, bool extension_is_enabled) {
1210  std::vector<ExtensionPage> result;
1211
1212  // Get the extension process's active views.
1213  extensions::ProcessManager* process_manager =
1214      ExtensionSystem::Get(extension_service_->profile())->process_manager();
1215  GetInspectablePagesForExtensionProcess(
1216      extension,
1217      process_manager->GetRenderViewHostsForExtension(extension->id()),
1218      &result);
1219
1220  // Get app window views
1221  GetAppWindowPagesForExtensionProfile(
1222      extension, extension_service_->profile(), &result);
1223
1224  // Include a link to start the lazy background page, if applicable.
1225  if (BackgroundInfo::HasLazyBackgroundPage(extension) &&
1226      extension_is_enabled &&
1227      !process_manager->GetBackgroundHostForExtension(extension->id())) {
1228    result.push_back(ExtensionPage(
1229        BackgroundInfo::GetBackgroundURL(extension),
1230        -1,
1231        -1,
1232        false,
1233        BackgroundInfo::HasGeneratedBackgroundPage(extension)));
1234  }
1235
1236  // Repeat for the incognito process, if applicable. Don't try to get
1237  // app windows for incognito processes.
1238  if (extension_service_->profile()->HasOffTheRecordProfile() &&
1239      IncognitoInfo::IsSplitMode(extension) &&
1240      util::IsIncognitoEnabled(extension->id(),
1241                               extension_service_->profile())) {
1242    extensions::ProcessManager* process_manager =
1243        ExtensionSystem::Get(extension_service_->profile()->
1244            GetOffTheRecordProfile())->process_manager();
1245    GetInspectablePagesForExtensionProcess(
1246        extension,
1247        process_manager->GetRenderViewHostsForExtension(extension->id()),
1248        &result);
1249
1250    if (BackgroundInfo::HasLazyBackgroundPage(extension) &&
1251        extension_is_enabled &&
1252        !process_manager->GetBackgroundHostForExtension(extension->id())) {
1253      result.push_back(ExtensionPage(
1254          BackgroundInfo::GetBackgroundURL(extension),
1255          -1,
1256          -1,
1257          true,
1258          BackgroundInfo::HasGeneratedBackgroundPage(extension)));
1259    }
1260  }
1261
1262  return result;
1263}
1264
1265void ExtensionSettingsHandler::GetInspectablePagesForExtensionProcess(
1266    const Extension* extension,
1267    const std::set<RenderViewHost*>& views,
1268    std::vector<ExtensionPage>* result) {
1269  bool has_generated_background_page =
1270      BackgroundInfo::HasGeneratedBackgroundPage(extension);
1271  for (std::set<RenderViewHost*>::const_iterator iter = views.begin();
1272       iter != views.end(); ++iter) {
1273    RenderViewHost* host = *iter;
1274    WebContents* web_contents = WebContents::FromRenderViewHost(host);
1275    ViewType host_type = GetViewType(web_contents);
1276    if (host == deleting_rvh_ ||
1277        VIEW_TYPE_EXTENSION_POPUP == host_type ||
1278        VIEW_TYPE_EXTENSION_DIALOG == host_type)
1279      continue;
1280
1281    GURL url = web_contents->GetURL();
1282    content::RenderProcessHost* process = host->GetProcess();
1283    bool is_background_page =
1284        (url == BackgroundInfo::GetBackgroundURL(extension));
1285    result->push_back(
1286        ExtensionPage(url,
1287                      process->GetID(),
1288                      host->GetRoutingID(),
1289                      process->GetBrowserContext()->IsOffTheRecord(),
1290                      is_background_page && has_generated_background_page));
1291  }
1292}
1293
1294void ExtensionSettingsHandler::GetAppWindowPagesForExtensionProfile(
1295    const Extension* extension,
1296    Profile* profile,
1297    std::vector<ExtensionPage>* result) {
1298  apps::AppWindowRegistry* registry = apps::AppWindowRegistry::Get(profile);
1299  if (!registry) return;
1300
1301  const apps::AppWindowRegistry::AppWindowList windows =
1302      registry->GetAppWindowsForApp(extension->id());
1303
1304  bool has_generated_background_page =
1305      BackgroundInfo::HasGeneratedBackgroundPage(extension);
1306  for (apps::AppWindowRegistry::const_iterator it = windows.begin();
1307       it != windows.end();
1308       ++it) {
1309    WebContents* web_contents = (*it)->web_contents();
1310    RenderViewHost* host = web_contents->GetRenderViewHost();
1311    content::RenderProcessHost* process = host->GetProcess();
1312
1313    bool is_background_page =
1314        (web_contents->GetURL() == BackgroundInfo::GetBackgroundURL(extension));
1315    result->push_back(
1316        ExtensionPage(web_contents->GetURL(),
1317                      process->GetID(),
1318                      host->GetRoutingID(),
1319                      process->GetBrowserContext()->IsOffTheRecord(),
1320                      is_background_page && has_generated_background_page));
1321  }
1322}
1323
1324ExtensionUninstallDialog*
1325ExtensionSettingsHandler::GetExtensionUninstallDialog() {
1326#if !defined(OS_ANDROID)
1327  if (!extension_uninstall_dialog_.get()) {
1328    Browser* browser = chrome::FindBrowserWithWebContents(
1329        web_ui()->GetWebContents());
1330    extension_uninstall_dialog_.reset(
1331        ExtensionUninstallDialog::Create(extension_service_->profile(),
1332                                         browser, this));
1333  }
1334  return extension_uninstall_dialog_.get();
1335#else
1336  return NULL;
1337#endif  // !defined(OS_ANDROID)
1338}
1339
1340void ExtensionSettingsHandler::OnRequirementsChecked(
1341    std::string extension_id,
1342    std::vector<std::string> requirement_errors) {
1343  if (requirement_errors.empty()) {
1344    extension_service_->EnableExtension(extension_id);
1345  } else {
1346    ExtensionErrorReporter::GetInstance()->ReportError(
1347        base::UTF8ToUTF16(JoinString(requirement_errors, ' ')),
1348        true);  // Be noisy.
1349  }
1350  requirements_checker_.reset();
1351}
1352
1353}  // namespace extensions
1354