blob: 8df72d7312e69b0d9e5c92af00f19c2a6923abbf [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/webui/extensions/extension_settings_handler.h"
#include "base/auto_reset.h"
#include "base/base64.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/command_line.h"
#include "base/file_util.h"
#include "base/string_util.h"
#include "base/string_number_conversions.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
#include "base/version.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/debugger/devtools_window.h"
#include "chrome/browser/extensions/crx_installer.h"
#include "chrome/browser/extensions/extension_disabled_ui.h"
#include "chrome/browser/extensions/extension_error_reporter.h"
#include "chrome/browser/extensions/extension_host.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_system.h"
#include "chrome/browser/extensions/extension_warning_set.h"
#include "chrome/browser/extensions/lazy_background_task_queue.h"
#include "chrome/browser/extensions/shell_window_registry.h"
#include "chrome/browser/extensions/unpacked_installer.h"
#include "chrome/browser/extensions/updater/extension_updater.h"
#include "chrome/browser/google/google_util.h"
#include "chrome/browser/managed_mode.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/tab_contents/background_contents.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/chrome_select_file_policy.h"
#include "chrome/browser/ui/extensions/shell_window.h"
#include "chrome/browser/ui/webui/extensions/extension_icon_source.h"
#include "chrome/browser/view_type_utils.h"
#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/extension_constants.h"
#include "chrome/common/extensions/extension_icon_set.h"
#include "chrome/common/extensions/extension_set.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_source.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/site_instance.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_view.h"
#include "content/public/browser/web_ui.h"
#include "grit/browser_resources.h"
#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
using content::RenderViewHost;
using content::WebContents;
using extensions::Extension;
using extensions::ExtensionUpdater;
using extensions::ManagementPolicy;
///////////////////////////////////////////////////////////////////////////////
//
// ExtensionSettingsHandler
//
///////////////////////////////////////////////////////////////////////////////
ExtensionSettingsHandler::ExtensionSettingsHandler()
: extension_service_(NULL),
management_policy_(NULL),
ignore_notifications_(false),
deleting_rvh_(NULL),
registered_for_notifications_(false) {
}
ExtensionSettingsHandler::~ExtensionSettingsHandler() {
// There may be pending file dialogs, we need to tell them that we've gone
// away so they don't try and call back to us.
if (load_extension_dialog_)
load_extension_dialog_->ListenerDestroyed();
registrar_.RemoveAll();
}
ExtensionSettingsHandler::ExtensionSettingsHandler(ExtensionService* service,
ManagementPolicy* policy)
: extension_service_(service),
management_policy_(policy),
ignore_notifications_(false),
deleting_rvh_(NULL),
registered_for_notifications_(false) {
}
// static
void ExtensionSettingsHandler::RegisterUserPrefs(PrefService* prefs) {
prefs->RegisterBooleanPref(prefs::kExtensionsUIDeveloperMode,
false,
PrefService::SYNCABLE_PREF);
}
DictionaryValue* ExtensionSettingsHandler::CreateExtensionDetailValue(
const Extension* extension,
const std::vector<ExtensionPage>& pages,
const ExtensionWarningSet* warnings_set) {
DictionaryValue* extension_data = new DictionaryValue();
bool enabled = extension_service_->IsExtensionEnabled(extension->id());
extension->GetBasicInfo(enabled, extension_data);
extension_data->SetBoolean("userModifiable",
management_policy_->UserMayModifySettings(extension, NULL));
GURL icon =
ExtensionIconSource::GetIconURL(extension,
extension_misc::EXTENSION_ICON_MEDIUM,
ExtensionIconSet::MATCH_BIGGER,
!enabled, NULL);
if (extension->location() == Extension::LOAD)
extension_data->SetString("path", extension->path().value());
extension_data->SetString("icon", icon.spec());
extension_data->SetBoolean("isUnpacked",
extension->location() == Extension::LOAD);
extension_data->SetBoolean("terminated",
extension_service_->terminated_extensions()->Contains(extension->id()));
extension_data->SetBoolean("enabledIncognito",
extension_service_->IsIncognitoEnabled(extension->id()));
extension_data->SetBoolean("wantsFileAccess", extension->wants_file_access());
extension_data->SetBoolean("allowFileAccess",
extension_service_->AllowFileAccess(extension));
extension_data->SetBoolean("allow_activity",
enabled && CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableExtensionActivityUI));
extension_data->SetBoolean("allow_reload",
extension->location() == Extension::LOAD);
extension_data->SetBoolean("is_hosted_app", extension->is_hosted_app());
extension_data->SetBoolean("homepageProvided",
extension->GetHomepageURL().is_valid());
// Determine the sort order: Extensions loaded through --load-extensions show
// up at the top. Disabled extensions show up at the bottom.
if (extension->location() == Extension::LOAD)
extension_data->SetInteger("order", 1);
else
extension_data->SetInteger("order", 2);
if (!extension_service_->extension_prefs()->
GetBrowserActionVisibility(extension)) {
extension_data->SetBoolean("enable_show_button", true);
}
// Add views
ListValue* views = new ListValue;
for (std::vector<ExtensionPage>::const_iterator iter = pages.begin();
iter != pages.end(); ++iter) {
DictionaryValue* view_value = new DictionaryValue;
if (iter->url.scheme() == chrome::kExtensionScheme) {
// No leading slash.
view_value->SetString("path", iter->url.path().substr(1));
} else {
// For live pages, use the full URL.
view_value->SetString("path", iter->url.spec());
}
view_value->SetInteger("renderViewId", iter->render_view_id);
view_value->SetInteger("renderProcessId", iter->render_process_id);
view_value->SetBoolean("incognito", iter->incognito);
views->Append(view_value);
}
extension_data->Set("views", views);
extension_data->SetBoolean("hasPopupAction",
extension->browser_action() || extension->page_action());
// Add warnings.
if (warnings_set) {
std::set<ExtensionWarningSet::WarningType> warnings;
warnings_set->GetWarningsAffectingExtension(extension->id(), &warnings);
if (!warnings.empty()) {
ListValue* warnings_list = new ListValue;
for (std::set<ExtensionWarningSet::WarningType>::const_iterator iter =
warnings.begin();
iter != warnings.end();
++iter) {
string16 warning_string(
ExtensionWarningSet::GetLocalizedWarning(*iter));
warnings_list->Append(Value::CreateStringValue(warning_string));
}
extension_data->Set("warnings", warnings_list);
}
}
// Add install warnings (these are not the same as warnings!).
if (extension->location() == Extension::LOAD) {
const Extension::InstallWarningVector& install_warnings =
extension->install_warnings();
if (!install_warnings.empty()) {
scoped_ptr<ListValue> list(new ListValue());
for (Extension::InstallWarningVector::const_iterator it =
install_warnings.begin(); it != install_warnings.end(); ++it) {
DictionaryValue* item = new DictionaryValue();
item->SetBoolean("isHTML",
it->format == Extension::InstallWarning::FORMAT_HTML);
item->SetString("message", it->message);
list->Append(item);
}
extension_data->Set("installWarnings", list.release());
}
}
return extension_data;
}
void ExtensionSettingsHandler::GetLocalizedValues(
DictionaryValue* localized_strings) {
localized_strings->SetString("extensionSettings",
l10n_util::GetStringUTF16(IDS_MANAGE_EXTENSIONS_SETTING_WINDOWS_TITLE));
localized_strings->SetString("extensionSettingsDeveloperMode",
l10n_util::GetStringUTF16(IDS_EXTENSIONS_DEVELOPER_MODE_LINK));
localized_strings->SetString("extensionSettingsNoExtensions",
l10n_util::GetStringUTF16(IDS_EXTENSIONS_NONE_INSTALLED));
localized_strings->SetString("extensionSettingsSuggestGallery",
l10n_util::GetStringFUTF16(IDS_EXTENSIONS_NONE_INSTALLED_SUGGEST_GALLERY,
ASCIIToUTF16(google_util::AppendGoogleLocaleParam(
GURL(extension_urls::GetWebstoreLaunchURL())).spec())));
localized_strings->SetString("extensionSettingsGetMoreExtensionsDeprecated",
l10n_util::GetStringFUTF16(IDS_GET_MORE_EXTENSIONS_DEPRECATED,
ASCIIToUTF16(google_util::AppendGoogleLocaleParam(
GURL(extension_urls::GetWebstoreLaunchURL())).spec())));
localized_strings->SetString("extensionSettingsGetMoreExtensions",
l10n_util::GetStringUTF16(IDS_GET_MORE_EXTENSIONS));
localized_strings->SetString("extensionSettingsGetMoreExtensionsUrl",
ASCIIToUTF16(google_util::AppendGoogleLocaleParam(
GURL(extension_urls::GetWebstoreLaunchURL())).spec()));
localized_strings->SetString("extensionSettingsExtensionId",
l10n_util::GetStringUTF16(IDS_EXTENSIONS_ID));
localized_strings->SetString("extensionSettingsExtensionPath",
l10n_util::GetStringUTF16(IDS_EXTENSIONS_PATH));
localized_strings->SetString("extensionSettingsInspectViews",
l10n_util::GetStringUTF16(IDS_EXTENSIONS_INSPECT_VIEWS));
localized_strings->SetString("extensionSettingsInstallWarnings",
l10n_util::GetStringUTF16(IDS_EXTENSIONS_INSTALL_WARNINGS));
localized_strings->SetString("viewIncognito",
l10n_util::GetStringUTF16(IDS_EXTENSIONS_VIEW_INCOGNITO));
localized_strings->SetString("viewInactive",
l10n_util::GetStringUTF16(IDS_EXTENSIONS_VIEW_INACTIVE));
localized_strings->SetString("extensionSettingsEnable",
l10n_util::GetStringUTF16(IDS_EXTENSIONS_ENABLE));
localized_strings->SetString("extensionSettingsEnabled",
l10n_util::GetStringUTF16(IDS_EXTENSIONS_ENABLED));
localized_strings->SetString("extensionSettingsRemove",
l10n_util::GetStringUTF16(IDS_EXTENSIONS_REMOVE));
localized_strings->SetString("extensionSettingsEnableIncognito",
l10n_util::GetStringUTF16(IDS_EXTENSIONS_ENABLE_INCOGNITO));
localized_strings->SetString("extensionSettingsAllowFileAccess",
l10n_util::GetStringUTF16(IDS_EXTENSIONS_ALLOW_FILE_ACCESS));
localized_strings->SetString("extensionSettingsIncognitoWarning",
l10n_util::GetStringUTF16(IDS_EXTENSIONS_INCOGNITO_WARNING));
localized_strings->SetString("extensionSettingsReloadTerminated",
l10n_util::GetStringUTF16(IDS_EXTENSIONS_RELOAD_TERMINATED));
localized_strings->SetString("extensionSettingsReloadUnpacked",
l10n_util::GetStringUTF16(IDS_EXTENSIONS_RELOAD_UNPACKED));
localized_strings->SetString("extensionSettingsOptions",
l10n_util::GetStringUTF16(IDS_EXTENSIONS_OPTIONS_LINK));
localized_strings->SetString("extensionSettingsActivity",
l10n_util::GetStringUTF16(IDS_EXTENSIONS_ACTIVITY_LINK));
localized_strings->SetString("extensionSettingsVisitWebsite",
l10n_util::GetStringUTF16(IDS_EXTENSIONS_VISIT_WEBSITE));
localized_strings->SetString("extensionSettingsVisitWebStore",
l10n_util::GetStringUTF16(IDS_EXTENSIONS_VISIT_WEBSTORE));
localized_strings->SetString("extensionSettingsPolicyControlled",
l10n_util::GetStringUTF16(IDS_EXTENSIONS_POLICY_CONTROLLED));
localized_strings->SetString("extensionSettingsManagedMode",
l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOCKED_MANAGED_MODE));
localized_strings->SetString("extensionSettingsShowButton",
l10n_util::GetStringUTF16(IDS_EXTENSIONS_SHOW_BUTTON));
localized_strings->SetString("extensionSettingsLoadUnpackedButton",
l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOAD_UNPACKED_BUTTON));
localized_strings->SetString("extensionSettingsPackButton",
l10n_util::GetStringUTF16(IDS_EXTENSIONS_PACK_BUTTON));
localized_strings->SetString("extensionSettingsCommandsLink",
l10n_util::GetStringUTF16(IDS_EXTENSIONS_COMMANDS_CONFIGURE));
localized_strings->SetString("extensionSettingsUpdateButton",
l10n_util::GetStringUTF16(IDS_EXTENSIONS_UPDATE_BUTTON));
localized_strings->SetString("extensionSettingsCrashMessage",
l10n_util::GetStringUTF16(IDS_EXTENSIONS_CRASHED_EXTENSION));
localized_strings->SetString("extensionSettingsInDevelopment",
l10n_util::GetStringUTF16(IDS_EXTENSIONS_IN_DEVELOPMENT));
localized_strings->SetString("extensionSettingsWarningsTitle",
l10n_util::GetStringUTF16(IDS_EXTENSION_WARNINGS_TITLE));
localized_strings->SetString("extensionSettingsShowDetails",
l10n_util::GetStringUTF16(IDS_EXTENSIONS_SHOW_DETAILS));
localized_strings->SetString("extensionSettingsHideDetails",
l10n_util::GetStringUTF16(IDS_EXTENSIONS_HIDE_DETAILS));
// TODO(estade): comb through the above strings to find ones no longer used in
// uber extensions.
localized_strings->SetString("extensionUninstall",
l10n_util::GetStringUTF16(IDS_EXTENSIONS_UNINSTALL));
}
void ExtensionSettingsHandler::NavigateToPendingEntry(const GURL& url,
content::NavigationController::ReloadType reload_type) {
if (reload_type != content::NavigationController::NO_RELOAD)
ReloadUnpackedExtensions();
}
void ExtensionSettingsHandler::RegisterMessages() {
// Don't override an |extension_service_| or |management_policy_| injected
// for testing.
if (!extension_service_) {
extension_service_ = Profile::FromWebUI(web_ui())->GetOriginalProfile()->
GetExtensionService();
}
if (!management_policy_) {
management_policy_ = extensions::ExtensionSystem::Get(
extension_service_->profile())->management_policy();
}
web_ui()->RegisterMessageCallback("extensionSettingsRequestExtensionsData",
base::Bind(&ExtensionSettingsHandler::HandleRequestExtensionsData,
base::Unretained(this)));
web_ui()->RegisterMessageCallback("extensionSettingsToggleDeveloperMode",
base::Bind(&ExtensionSettingsHandler::HandleToggleDeveloperMode,
base::Unretained(this)));
web_ui()->RegisterMessageCallback("extensionSettingsInspect",
base::Bind(&ExtensionSettingsHandler::HandleInspectMessage,
base::Unretained(this)));
web_ui()->RegisterMessageCallback("extensionSettingsReload",
base::Bind(&ExtensionSettingsHandler::HandleReloadMessage,
base::Unretained(this)));
web_ui()->RegisterMessageCallback("extensionSettingsEnable",
base::Bind(&ExtensionSettingsHandler::HandleEnableMessage,
base::Unretained(this)));
web_ui()->RegisterMessageCallback("extensionSettingsEnableIncognito",
base::Bind(&ExtensionSettingsHandler::HandleEnableIncognitoMessage,
base::Unretained(this)));
web_ui()->RegisterMessageCallback("extensionSettingsAllowFileAccess",
base::Bind(&ExtensionSettingsHandler::HandleAllowFileAccessMessage,
base::Unretained(this)));
web_ui()->RegisterMessageCallback("extensionSettingsUninstall",
base::Bind(&ExtensionSettingsHandler::HandleUninstallMessage,
base::Unretained(this)));
web_ui()->RegisterMessageCallback("extensionSettingsOptions",
base::Bind(&ExtensionSettingsHandler::HandleOptionsMessage,
base::Unretained(this)));
web_ui()->RegisterMessageCallback("extensionSettingsShowButton",
base::Bind(&ExtensionSettingsHandler::HandleShowButtonMessage,
base::Unretained(this)));
web_ui()->RegisterMessageCallback("extensionSettingsAutoupdate",
base::Bind(&ExtensionSettingsHandler::HandleAutoUpdateMessage,
base::Unretained(this)));
web_ui()->RegisterMessageCallback("extensionSettingsLoadUnpackedExtension",
base::Bind(&ExtensionSettingsHandler::HandleLoadUnpackedExtensionMessage,
base::Unretained(this)));
}
void ExtensionSettingsHandler::FileSelected(const FilePath& path, int index,
void* params) {
last_unpacked_directory_ = FilePath(path);
extensions::UnpackedInstaller::Create(extension_service_)->Load(path);
}
void ExtensionSettingsHandler::MultiFilesSelected(
const std::vector<FilePath>& files, void* params) {
NOTREACHED();
}
void ExtensionSettingsHandler::Observe(
int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
Profile* profile = Profile::FromWebUI(web_ui());
Profile* source_profile = NULL;
switch (type) {
// We listen for notifications that will result in the page being
// repopulated with data twice for the same event in certain cases.
// For instance, EXTENSION_LOADED & EXTENSION_HOST_CREATED because
// we don't know about the views for an extension at EXTENSION_LOADED, but
// if we only listen to EXTENSION_HOST_CREATED, we'll miss extensions
// that don't have a process at startup.
//
// Doing it this way gets everything but causes the page to be rendered
// more than we need. It doesn't seem to result in any noticeable flicker.
case content::NOTIFICATION_RENDER_VIEW_HOST_DELETED:
deleting_rvh_ = content::Source<RenderViewHost>(source).ptr();
// Fall through.
case content::NOTIFICATION_RENDER_VIEW_HOST_CREATED:
source_profile = Profile::FromBrowserContext(
content::Source<RenderViewHost>(source)->GetSiteInstance()->
GetBrowserContext());
if (!profile->IsSameProfile(source_profile))
return;
MaybeUpdateAfterNotification();
break;
case chrome::NOTIFICATION_BACKGROUND_CONTENTS_DELETED:
deleting_rvh_ = content::Details<BackgroundContents>(details)->
web_contents()->GetRenderViewHost();
// Fall through.
case chrome::NOTIFICATION_BACKGROUND_CONTENTS_NAVIGATED:
case chrome::NOTIFICATION_EXTENSION_HOST_CREATED:
source_profile = content::Source<Profile>(source).ptr();
if (!profile->IsSameProfile(source_profile))
return;
MaybeUpdateAfterNotification();
break;
case chrome::NOTIFICATION_EXTENSION_LOADED:
case chrome::NOTIFICATION_EXTENSION_UNLOADED:
case chrome::NOTIFICATION_EXTENSION_UPDATE_DISABLED:
case chrome::NOTIFICATION_EXTENSION_WARNING_CHANGED:
case chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED:
case chrome::NOTIFICATION_PREF_CHANGED:
MaybeUpdateAfterNotification();
break;
default:
NOTREACHED();
}
}
void ExtensionSettingsHandler::ExtensionUninstallAccepted() {
DCHECK(!extension_id_prompting_.empty());
bool was_terminated = false;
// The extension can be uninstalled in another window while the UI was
// showing. Do nothing in that case.
const Extension* extension =
extension_service_->GetExtensionById(extension_id_prompting_, true);
if (!extension) {
extension = extension_service_->GetTerminatedExtension(
extension_id_prompting_);
was_terminated = true;
}
if (!extension)
return;
extension_service_->UninstallExtension(extension_id_prompting_,
false, // External uninstall.
NULL); // Error.
extension_id_prompting_ = "";
// There will be no EXTENSION_UNLOADED notification for terminated
// extensions as they were already unloaded.
if (was_terminated)
HandleRequestExtensionsData(NULL);
}
void ExtensionSettingsHandler::ExtensionUninstallCanceled() {
extension_id_prompting_ = "";
}
void ExtensionSettingsHandler::ReloadUnpackedExtensions() {
const ExtensionSet* extensions = extension_service_->extensions();
std::vector<const Extension*> unpacked_extensions;
for (ExtensionSet::const_iterator extension = extensions->begin();
extension != extensions->end(); ++extension) {
if ((*extension)->location() == Extension::LOAD)
unpacked_extensions.push_back(*extension);
}
for (std::vector<const Extension*>::iterator iter =
unpacked_extensions.begin(); iter != unpacked_extensions.end(); ++iter) {
extension_service_->ReloadExtension((*iter)->id());
}
}
void ExtensionSettingsHandler::HandleRequestExtensionsData(
const ListValue* args) {
DictionaryValue results;
// Add the extensions to the results structure.
ListValue *extensions_list = new ListValue();
ExtensionWarningSet* warnings = extension_service_->extension_warnings();
const ExtensionSet* extensions = extension_service_->extensions();
for (ExtensionSet::const_iterator extension = extensions->begin();
extension != extensions->end(); ++extension) {
if ((*extension)->ShouldDisplayInExtensionSettings()) {
extensions_list->Append(CreateExtensionDetailValue(
*extension,
GetInspectablePagesForExtension(*extension, true),
warnings));
}
}
extensions = extension_service_->disabled_extensions();
for (ExtensionSet::const_iterator extension = extensions->begin();
extension != extensions->end(); ++extension) {
if ((*extension)->ShouldDisplayInExtensionSettings()) {
extensions_list->Append(CreateExtensionDetailValue(
*extension,
GetInspectablePagesForExtension(*extension, false),
warnings));
}
}
extensions = extension_service_->terminated_extensions();
std::vector<ExtensionPage> empty_pages;
for (ExtensionSet::const_iterator extension = extensions->begin();
extension != extensions->end(); ++extension) {
if ((*extension)->ShouldDisplayInExtensionSettings()) {
extensions_list->Append(CreateExtensionDetailValue(
*extension,
empty_pages, // Terminated process has no active pages.
warnings));
}
}
results.Set("extensions", extensions_list);
if (ManagedMode::IsInManagedMode()) {
results.SetBoolean("managedMode", true);
results.SetBoolean("developerMode", false);
} else {
results.SetBoolean("managedMode", false);
Profile* profile = Profile::FromWebUI(web_ui());
bool developer_mode =
profile->GetPrefs()->GetBoolean(prefs::kExtensionsUIDeveloperMode);
results.SetBoolean("developerMode", developer_mode);
}
bool load_unpacked_disabled =
extension_service_->extension_prefs()->ExtensionsBlacklistedByDefault();
results.SetBoolean("loadUnpackedDisabled", load_unpacked_disabled);
web_ui()->CallJavascriptFunction("ExtensionSettings.returnExtensionsData",
results);
content::WebContentsObserver::Observe(web_ui()->GetWebContents());
MaybeRegisterForNotifications();
}
void ExtensionSettingsHandler::HandleToggleDeveloperMode(
const ListValue* args) {
if (ManagedMode::IsInManagedMode())
return;
Profile* profile = Profile::FromWebUI(web_ui());
bool developer_mode =
profile->GetPrefs()->GetBoolean(prefs::kExtensionsUIDeveloperMode);
profile->GetPrefs()->SetBoolean(
prefs::kExtensionsUIDeveloperMode, !developer_mode);
}
void ExtensionSettingsHandler::HandleInspectMessage(const ListValue* args) {
std::string extension_id;
std::string render_process_id_str;
std::string render_view_id_str;
int render_process_id;
int render_view_id;
bool incognito;
CHECK_EQ(4U, args->GetSize());
CHECK(args->GetString(0, &extension_id));
CHECK(args->GetString(1, &render_process_id_str));
CHECK(args->GetString(2, &render_view_id_str));
CHECK(args->GetBoolean(3, &incognito));
CHECK(base::StringToInt(render_process_id_str, &render_process_id));
CHECK(base::StringToInt(render_view_id_str, &render_view_id));
if (render_process_id == -1) {
// This message is for a lazy background page. Start the page if necessary.
const Extension* extension =
extension_service_->extensions()->GetByID(extension_id);
DCHECK(extension);
ExtensionService* service = extension_service_;
if (incognito)
service = extensions::ExtensionSystem::Get(extension_service_->
profile()->GetOffTheRecordProfile())->extension_service();
service->InspectBackgroundPage(extension);
return;
}
RenderViewHost* host = RenderViewHost::FromID(render_process_id,
render_view_id);
if (!host) {
// This can happen if the host has gone away since the page was displayed.
return;
}
DevToolsWindow::OpenDevToolsWindow(host);
}
void ExtensionSettingsHandler::HandleReloadMessage(const ListValue* args) {
std::string extension_id = UTF16ToUTF8(ExtractStringValue(args));
CHECK(!extension_id.empty());
extension_service_->ReloadExtension(extension_id);
}
void ExtensionSettingsHandler::HandleEnableMessage(const ListValue* args) {
CHECK_EQ(2U, args->GetSize());
std::string extension_id, enable_str;
CHECK(args->GetString(0, &extension_id));
CHECK(args->GetString(1, &enable_str));
const Extension* extension =
extension_service_->GetInstalledExtension(extension_id);
if (!extension ||
!management_policy_->UserMayModifySettings(extension, NULL)) {
LOG(ERROR) << "Attempt to enable an extension that is non-usermanagable was"
<< "made. Extension id: " << extension->id();
return;
}
if (enable_str == "true") {
extensions::ExtensionPrefs* prefs = extension_service_->extension_prefs();
if (prefs->DidExtensionEscalatePermissions(extension_id)) {
Browser* browser = browser::FindBrowserWithWebContents(
web_ui()->GetWebContents());
extensions::ShowExtensionDisabledDialog(
extension_service_, browser, extension);
} else if ((prefs->GetDisableReasons(extension_id) &
Extension::DISABLE_UNSUPPORTED_REQUIREMENT) &&
!requirements_checker_.get()) {
// Recheck the requirements.
scoped_refptr<const Extension> extension =
extension_service_->GetExtensionById(extension_id,
true /* include disabled */);
requirements_checker_.reset(new extensions::RequirementsChecker());
requirements_checker_->Check(
extension,
base::Bind(&ExtensionSettingsHandler::OnRequirementsChecked,
AsWeakPtr(), extension_id));
} else {
extension_service_->EnableExtension(extension_id);
}
} else {
extension_service_->DisableExtension(
extension_id, Extension::DISABLE_USER_ACTION);
}
}
void ExtensionSettingsHandler::HandleEnableIncognitoMessage(
const ListValue* args) {
CHECK_EQ(2U, args->GetSize());
std::string extension_id, enable_str;
CHECK(args->GetString(0, &extension_id));
CHECK(args->GetString(1, &enable_str));
const Extension* extension =
extension_service_->GetInstalledExtension(extension_id);
if (!extension)
return;
// Flipping the incognito bit will generate unload/load notifications for the
// extension, but we don't want to reload the page, because a) we've already
// updated the UI to reflect the change, and b) we want the yellow warning
// text to stay until the user has left the page.
//
// TODO(aa): This creates crappiness in some cases. For example, in a main
// window, when toggling this, the browser action will flicker because it gets
// unloaded, then reloaded. It would be better to have a dedicated
// notification for this case.
//
// Bug: http://crbug.com/41384
AutoReset<bool> auto_reset_ignore_notifications(&ignore_notifications_, true);
extension_service_->SetIsIncognitoEnabled(extension->id(),
enable_str == "true");
}
void ExtensionSettingsHandler::HandleAllowFileAccessMessage(
const ListValue* args) {
CHECK_EQ(2U, args->GetSize());
std::string extension_id, allow_str;
CHECK(args->GetString(0, &extension_id));
CHECK(args->GetString(1, &allow_str));
const Extension* extension =
extension_service_->GetInstalledExtension(extension_id);
if (!extension)
return;
if (!management_policy_->UserMayModifySettings(extension, NULL)) {
LOG(ERROR) << "Attempt to change allow file access of an extension that is "
<< "non-usermanagable was made. Extension id : "
<< extension->id();
return;
}
extension_service_->SetAllowFileAccess(extension, allow_str == "true");
}
void ExtensionSettingsHandler::HandleUninstallMessage(const ListValue* args) {
CHECK_EQ(1U, args->GetSize());
std::string extension_id;
CHECK(args->GetString(0, &extension_id));
const Extension* extension =
extension_service_->GetInstalledExtension(extension_id);
if (!extension)
return;
if (!management_policy_->UserMayModifySettings(extension, NULL)) {
LOG(ERROR) << "Attempt to uninstall an extension that is non-usermanagable "
<< "was made. Extension id : " << extension->id();
return;
}
if (!extension_id_prompting_.empty())
return; // Only one prompt at a time.
extension_id_prompting_ = extension_id;
GetExtensionUninstallDialog()->ConfirmUninstall(extension);
}
void ExtensionSettingsHandler::HandleOptionsMessage(const ListValue* args) {
const Extension* extension = GetActiveExtension(args);
if (!extension || extension->options_url().is_empty())
return;
Profile::FromWebUI(web_ui())->GetExtensionProcessManager()->OpenOptionsPage(
extension,
browser::FindBrowserWithWebContents(web_ui()->GetWebContents()));
}
void ExtensionSettingsHandler::HandleShowButtonMessage(const ListValue* args) {
const Extension* extension = GetActiveExtension(args);
if (!extension)
return;
extension_service_->extension_prefs()->
SetBrowserActionVisibility(extension, true);
}
void ExtensionSettingsHandler::HandleAutoUpdateMessage(const ListValue* args) {
ExtensionUpdater* updater = extension_service_->updater();
if (updater)
updater->CheckNow();
}
void ExtensionSettingsHandler::HandleLoadUnpackedExtensionMessage(
const ListValue* args) {
DCHECK(args->empty());
string16 select_title =
l10n_util::GetStringUTF16(IDS_EXTENSION_LOAD_FROM_DIRECTORY);
const int kFileTypeIndex = 0; // No file type information to index.
const ui::SelectFileDialog::Type kSelectType =
ui::SelectFileDialog::SELECT_FOLDER;
load_extension_dialog_ = ui::SelectFileDialog::Create(
this, new ChromeSelectFilePolicy(web_ui()->GetWebContents()));
load_extension_dialog_->SelectFile(
kSelectType, select_title, last_unpacked_directory_, NULL,
kFileTypeIndex, FILE_PATH_LITERAL(""),
web_ui()->GetWebContents()->GetView()->GetTopLevelNativeWindow(), NULL);
}
void ExtensionSettingsHandler::ShowAlert(const std::string& message) {
ListValue arguments;
arguments.Append(Value::CreateStringValue(message));
web_ui()->CallJavascriptFunction("alert", arguments);
}
const Extension* ExtensionSettingsHandler::GetActiveExtension(
const ListValue* args) {
std::string extension_id = UTF16ToUTF8(ExtractStringValue(args));
CHECK(!extension_id.empty());
return extension_service_->GetExtensionById(extension_id, false);
}
void ExtensionSettingsHandler::MaybeUpdateAfterNotification() {
WebContents* contents = web_ui()->GetWebContents();
if (!ignore_notifications_ && contents && contents->GetRenderViewHost())
HandleRequestExtensionsData(NULL);
deleting_rvh_ = NULL;
}
void ExtensionSettingsHandler::MaybeRegisterForNotifications() {
if (registered_for_notifications_)
return;
registered_for_notifications_ = true;
Profile* profile = Profile::FromWebUI(web_ui());
// Register for notifications that we need to reload the page.
registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
content::Source<Profile>(profile));
registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
content::Source<Profile>(profile));
registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UPDATE_DISABLED,
content::Source<Profile>(profile));
registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_WARNING_CHANGED,
content::Source<Profile>(profile));
registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_CREATED,
content::NotificationService::AllBrowserContextsAndSources());
registrar_.Add(this,
content::NOTIFICATION_RENDER_VIEW_HOST_CREATED,
content::NotificationService::AllBrowserContextsAndSources());
registrar_.Add(this,
content::NOTIFICATION_RENDER_VIEW_HOST_DELETED,
content::NotificationService::AllBrowserContextsAndSources());
registrar_.Add(this,
chrome::NOTIFICATION_BACKGROUND_CONTENTS_NAVIGATED,
content::NotificationService::AllBrowserContextsAndSources());
registrar_.Add(this,
chrome::NOTIFICATION_BACKGROUND_CONTENTS_DELETED,
content::NotificationService::AllBrowserContextsAndSources());
registrar_.Add(
this,
chrome::NOTIFICATION_EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED,
content::Source<extensions::ExtensionPrefs>(
profile->GetExtensionService()->extension_prefs()));
pref_registrar_.Init(profile->GetPrefs());
pref_registrar_.Add(prefs::kExtensionInstallDenyList, this);
local_state_pref_registrar_.Init(g_browser_process->local_state());
local_state_pref_registrar_.Add(prefs::kInManagedMode, this);
}
std::vector<ExtensionPage>
ExtensionSettingsHandler::GetInspectablePagesForExtension(
const Extension* extension, bool extension_is_enabled) {
std::vector<ExtensionPage> result;
// Get the extension process's active views.
ExtensionProcessManager* process_manager =
extension_service_->profile()->GetExtensionProcessManager();
GetInspectablePagesForExtensionProcess(
process_manager->GetRenderViewHostsForExtension(extension->id()),
&result);
// Get shell window views
GetShellWindowPagesForExtensionProfile(extension,
extension_service_->profile(), &result);
// Include a link to start the lazy background page, if applicable.
if (extension->has_lazy_background_page() && extension_is_enabled &&
!process_manager->GetBackgroundHostForExtension(extension->id())) {
result.push_back(
ExtensionPage(extension->GetBackgroundURL(), -1, -1, false));
}
// Repeat for the incognito process, if applicable. Don't try to get
// shell windows for incognito processes.
if (extension_service_->profile()->HasOffTheRecordProfile() &&
extension->incognito_split_mode()) {
ExtensionProcessManager* process_manager =
extension_service_->profile()->GetOffTheRecordProfile()->
GetExtensionProcessManager();
GetInspectablePagesForExtensionProcess(
process_manager->GetRenderViewHostsForExtension(extension->id()),
&result);
if (extension->has_lazy_background_page() && extension_is_enabled &&
!process_manager->GetBackgroundHostForExtension(extension->id())) {
result.push_back(
ExtensionPage(extension->GetBackgroundURL(), -1, -1, true));
}
}
return result;
}
void ExtensionSettingsHandler::GetInspectablePagesForExtensionProcess(
const std::set<RenderViewHost*>& views,
std::vector<ExtensionPage>* result) {
for (std::set<RenderViewHost*>::const_iterator iter = views.begin();
iter != views.end(); ++iter) {
RenderViewHost* host = *iter;
WebContents* web_contents = WebContents::FromRenderViewHost(host);
chrome::ViewType host_type = chrome::GetViewType(web_contents);
if (host == deleting_rvh_ ||
chrome::VIEW_TYPE_EXTENSION_POPUP == host_type ||
chrome::VIEW_TYPE_EXTENSION_DIALOG == host_type)
continue;
GURL url = web_contents->GetURL();
content::RenderProcessHost* process = host->GetProcess();
result->push_back(
ExtensionPage(url, process->GetID(), host->GetRoutingID(),
process->GetBrowserContext()->IsOffTheRecord()));
}
}
void ExtensionSettingsHandler::GetShellWindowPagesForExtensionProfile(
const Extension* extension,
Profile* profile,
std::vector<ExtensionPage>* result) {
extensions::ShellWindowRegistry* registry =
extensions::ShellWindowRegistry::Get(profile);
if (!registry) return;
const extensions::ShellWindowRegistry::ShellWindowSet windows =
registry->GetShellWindowsForApp(extension->id());
for (extensions::ShellWindowRegistry::const_iterator it = windows.begin();
it != windows.end(); ++it) {
WebContents* web_contents = (*it)->web_contents();
RenderViewHost* host = web_contents->GetRenderViewHost();
content::RenderProcessHost* process = host->GetProcess();
result->push_back(
ExtensionPage(web_contents->GetURL(), process->GetID(),
host->GetRoutingID(),
process->GetBrowserContext()->IsOffTheRecord()));
}
}
ExtensionUninstallDialog*
ExtensionSettingsHandler::GetExtensionUninstallDialog() {
#if !defined(OS_ANDROID)
if (!extension_uninstall_dialog_.get()) {
Browser* browser = browser::FindBrowserWithWebContents(
web_ui()->GetWebContents());
extension_uninstall_dialog_.reset(
ExtensionUninstallDialog::Create(browser, this));
}
return extension_uninstall_dialog_.get();
#else
return NULL;
#endif // !defined(OS_ANDROID)
}
void ExtensionSettingsHandler::OnRequirementsChecked(
std::string extension_id,
std::vector<std::string> requirement_errors) {
if (requirement_errors.empty()) {
extension_service_->EnableExtension(extension_id);
} else {
ExtensionErrorReporter::GetInstance()->ReportError(
UTF8ToUTF16(JoinString(requirement_errors, ' ')),
true /* be noisy */);
}
requirements_checker_.reset();
}