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