// Copyright 2016 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/settings/site_settings_handler.h" #include #include #include #include #include "base/barrier_closure.h" #include "base/bind.h" #include "base/feature_list.h" #include "base/i18n/number_formatting.h" #include "base/macros.h" #include "base/metrics/histogram_macros.h" #include "base/metrics/user_metrics.h" #include "base/stl_util.h" #include "base/strings/utf_string_conversions.h" #include "base/values.h" #include "chrome/browser/bluetooth/bluetooth_chooser_context.h" #include "chrome/browser/bluetooth/bluetooth_chooser_context_factory.h" #include "chrome/browser/content_settings/host_content_settings_map_factory.h" #include "chrome/browser/engagement/site_engagement_service.h" #include "chrome/browser/hid/hid_chooser_context.h" #include "chrome/browser/hid/hid_chooser_context_factory.h" #include "chrome/browser/infobars/infobar_service.h" #include "chrome/browser/media/unified_autoplay_config.h" #include "chrome/browser/permissions/permission_decision_auto_blocker_factory.h" #include "chrome/browser/serial/serial_chooser_context.h" #include "chrome/browser/serial/serial_chooser_context_factory.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_list.h" #include "chrome/browser/ui/page_info/page_info_infobar_delegate.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/ui/webui/recent_site_settings_helper.h" #include "chrome/browser/ui/webui/site_settings_helper.h" #include "chrome/browser/usb/usb_chooser_context.h" #include "chrome/browser/usb/usb_chooser_context_factory.h" #include "chrome/browser/web_applications/web_app_provider.h" #include "chrome/browser/web_applications/web_app_registrar.h" #include "chrome/common/extensions/manifest_handlers/app_launch_info.h" #include "chrome/common/pref_names.h" #include "chrome/grit/generated_resources.h" #include "components/content_settings/core/browser/cookie_settings.h" #include "components/content_settings/core/browser/uma_util.h" #include "components/content_settings/core/browser/website_settings_registry.h" #include "components/content_settings/core/common/content_settings_types.h" #include "components/content_settings/core/common/content_settings_utils.h" #include "components/content_settings/core/common/features.h" #include "components/content_settings/core/common/pref_names.h" #include "components/crx_file/id_util.h" #include "components/permissions/chooser_context_base.h" #include "components/permissions/permission_decision_auto_blocker.h" #include "components/permissions/permission_uma_util.h" #include "components/permissions/permission_util.h" #include "components/prefs/pref_change_registrar.h" #include "components/prefs/pref_service.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_ui.h" #include "content/public/common/content_features.h" #include "content/public/common/origin_util.h" #include "content/public/common/url_constants.h" #include "extensions/browser/extension_registry.h" #include "extensions/common/permissions/api_permission.h" #include "extensions/common/permissions/permissions_data.h" #include "third_party/blink/public/common/page/page_zoom.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/text/bytes_formatting.h" #if defined(OS_CHROMEOS) #include "components/user_manager/user_manager.h" #endif namespace settings { namespace { // Keys of the dictionary returned by HandleIsPatternValidForType. constexpr char kIsValidKey[] = "isValid"; constexpr char kReasonKey[] = "reason"; constexpr char kEffectiveTopLevelDomainPlus1Name[] = "etldPlus1"; constexpr char kOriginList[] = "origins"; constexpr char kNumCookies[] = "numCookies"; constexpr char kHasPermissionSettings[] = "hasPermissionSettings"; constexpr char kHasInstalledPWA[] = "hasInstalledPWA"; constexpr char kIsInstalled[] = "isInstalled"; constexpr char kZoom[] = "zoom"; // Placeholder value for ETLD+1 until a valid origin is added. If an ETLD+1 // only has placeholder, then create an ETLD+1 origin. constexpr char kPlaceholder[] = "placeholder"; // These values are persisted to logs. Entries should not be renumbered and // numeric values should never be reused. enum class AllSitesAction { kLoadPage = 0, kResetPermissions = 1, kClearData = 2, kEnterSiteDetails = 3, kMaxValue = kEnterSiteDetails, }; enum class AllSitesAction2 { kLoadPage = 0, kResetSiteGroupPermissions = 1, kResetOriginPermissions = 2, kClearAllData = 3, kClearSiteGroupData = 4, kClearOriginData = 5, kEnterSiteDetails = 6, kMaxValue = kEnterSiteDetails, }; // Return an appropriate API Permission ID for the given string name. extensions::APIPermission::APIPermission::ID APIPermissionFromGroupName( std::string type) { // Once there are more than two groups to consider, this should be changed to // something better than if's. if (site_settings::ContentSettingsTypeFromGroupName(type) == ContentSettingsType::GEOLOCATION) return extensions::APIPermission::APIPermission::kGeolocation; if (site_settings::ContentSettingsTypeFromGroupName(type) == ContentSettingsType::NOTIFICATIONS) return extensions::APIPermission::APIPermission::kNotifications; return extensions::APIPermission::APIPermission::kInvalid; } // Asks the |profile| for hosted apps which have the |permission| set, and // adds their web extent and launch URL to the |exceptions| list. void AddExceptionsGrantedByHostedApps( content::BrowserContext* context, extensions::APIPermission::APIPermission::ID permission, base::ListValue* exceptions) { const extensions::ExtensionSet& extensions = extensions::ExtensionRegistry::Get(context)->enabled_extensions(); for (extensions::ExtensionSet::const_iterator extension = extensions.begin(); extension != extensions.end(); ++extension) { if (!(*extension)->is_hosted_app() || !(*extension)->permissions_data()->HasAPIPermission(permission)) continue; const extensions::URLPatternSet& web_extent = (*extension)->web_extent(); // Add patterns from web extent. for (auto pattern = web_extent.begin(); pattern != web_extent.end(); ++pattern) { std::string url_pattern = pattern->GetAsString(); site_settings::AddExceptionForHostedApp(url_pattern, *extension->get(), exceptions); } // Retrieve the launch URL. GURL launch_url = extensions::AppLaunchInfo::GetLaunchWebURL(extension->get()); // Skip adding the launch URL if it is part of the web extent. if (web_extent.MatchesURL(launch_url)) continue; site_settings::AddExceptionForHostedApp(launch_url.spec(), *extension->get(), exceptions); } } base::flat_set GetInstalledAppOrigins( Profile* profile, const web_app::AppRegistrar& registrar) { base::flat_set origins; for (const web_app::AppId& app : registrar.GetAppIds()) origins.insert(registrar.GetAppScope(app).GetOrigin().spec()); return origins; } // Groups |url| into sets of eTLD+1s in |site_group_map|, assuming |url| is an // origin. // There are three cases: // 1. The ETLD+1 of |url| is not yet in |site_group_map|. We add the ETLD+1 // to |site_group_map|. If the |url| is an ETLD+1 cookie origin, put a // placeholder origin for the ETLD+1. // 2. The ETLD+1 of |url| is in |site_group_map|, and is equal to host of // |url|. This means case 1 has already happened and nothing more needs to // be done. // 3. The ETLD+1 of |url| is in |site_group_map| and is different to host of // |url|. For a cookies url, if a https origin with same host exists, // nothing more needs to be done. // In case 3, we try to add |url| to the set of origins for the ETLD+1. If an // existing origin is a placeholder, delete it, because the placeholder is no // longer needed. void CreateOrAppendSiteGroupEntry( std::map>* site_group_map, const GURL& url, bool url_is_origin_with_cookies = false) { std::string etld_plus1_string = net::registry_controlled_domains::GetDomainAndRegistry( url, net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES); auto entry = site_group_map->find(etld_plus1_string); bool etld_plus1_cookie_url = url_is_origin_with_cookies && url.host() == etld_plus1_string; if (entry == site_group_map->end()) { // Case 1: std::string origin = etld_plus1_cookie_url ? kPlaceholder : url.spec(); site_group_map->emplace(etld_plus1_string, std::set({origin})); return; } // Case 2: if (etld_plus1_cookie_url) return; // Case 3: if (url_is_origin_with_cookies) { // Cookies ignore schemes, so try and see if a https schemed version // already exists in the origin list, if not, then add the http schemed // version into the map. std::string https_url = std::string(url::kHttpsScheme) + url::kStandardSchemeSeparator + url.host() + "/"; if (entry->second.find(https_url) != entry->second.end()) return; } entry->second.insert(url.spec()); auto placeholder = entry->second.find(kPlaceholder); if (placeholder != entry->second.end()) entry->second.erase(placeholder); } // Update the storage data in |origin_size_map|. void UpdateDataForOrigin(const GURL& url, const int64_t size, std::map* origin_size_map) { if (size > 0) (*origin_size_map)[url.spec()] += size; } // Converts a given |site_group_map| to a list of base::DictionaryValues, adding // the site engagement score for each origin. void ConvertSiteGroupMapToListValue( const std::map>& site_group_map, const std::set& origin_permission_set, base::Value* list_value, Profile* profile, const web_app::AppRegistrar& registrar) { DCHECK_EQ(base::Value::Type::LIST, list_value->type()); DCHECK(profile); base::flat_set installed_origins = GetInstalledAppOrigins(profile, registrar); SiteEngagementService* engagement_service = SiteEngagementService::Get(profile); for (const auto& entry : site_group_map) { // eTLD+1 is the effective top level domain + 1. base::Value site_group(base::Value::Type::DICTIONARY); site_group.SetKey(kEffectiveTopLevelDomainPlus1Name, base::Value(entry.first)); bool has_installed_pwa = false; base::Value origin_list(base::Value::Type::LIST); for (const std::string& origin : entry.second) { base::Value origin_object(base::Value::Type::DICTIONARY); // If origin is placeholder, create a http ETLD+1 origin for it. if (origin == kPlaceholder) { origin_object.SetKey( "origin", base::Value(std::string(url::kHttpScheme) + url::kStandardSchemeSeparator + entry.first + "/")); } else { origin_object.SetKey("origin", base::Value(origin)); } origin_object.SetKey( "engagement", base::Value(engagement_service->GetScore(GURL(origin)))); origin_object.SetKey("usage", base::Value(0)); origin_object.SetKey(kNumCookies, base::Value(0)); bool is_installed = installed_origins.contains(origin); if (is_installed) has_installed_pwa = true; origin_object.SetKey(kIsInstalled, base::Value(is_installed)); origin_object.SetKey( kHasPermissionSettings, base::Value(base::Contains(origin_permission_set, origin))); origin_list.Append(std::move(origin_object)); } site_group.SetKey(kHasInstalledPWA, base::Value(has_installed_pwa)); site_group.SetKey(kNumCookies, base::Value(0)); site_group.SetKey(kOriginList, std::move(origin_list)); list_value->Append(std::move(site_group)); } } bool IsPatternValidForType(const std::string& pattern_string, const std::string& type, Profile* profile, std::string* out_error) { ContentSettingsType content_type = site_settings::ContentSettingsTypeFromGroupName(type); ContentSettingsPattern pattern = ContentSettingsPattern::FromString(pattern_string); HostContentSettingsMap* map = HostContentSettingsMapFactory::GetForProfile(profile); // Don't allow an input of '*', even though it's a valid pattern. This // changes the default setting. if (!pattern.IsValid() || pattern == ContentSettingsPattern::Wildcard()) { *out_error = l10n_util::GetStringUTF8(IDS_SETTINGS_NOT_VALID_WEB_ADDRESS); return false; } // Check if a setting can be set for this url and setting type, and if not, // return false with a string saying why. GURL url(pattern_string); if (url.is_valid() && map->IsRestrictedToSecureOrigins(content_type) && !content::IsOriginSecure(url)) { *out_error = l10n_util::GetStringUTF8( IDS_SETTINGS_NOT_VALID_WEB_ADDRESS_FOR_CONTENT_TYPE); return false; } // The pattern is valid. return true; } void UpdateDataFromCookiesTree( std::map>* all_sites_map, std::map* origin_size_map, const GURL& origin, int64_t size) { UpdateDataForOrigin(origin, size, origin_size_map); CreateOrAppendSiteGroupEntry(all_sites_map, origin); } void LogAllSitesAction(AllSitesAction2 action) { UMA_HISTOGRAM_ENUMERATION("WebsiteSettings.AllSitesAction2", action); } int GetNumCookieExceptionsOfTypes(HostContentSettingsMap* map, const std::set types) { ContentSettingsForOneType output; map->GetSettingsForOneType(ContentSettingsType::COOKIES, std::string(), &output); return std::count_if( output.begin(), output.end(), [types](const ContentSettingPatternSource setting) { return types.count( content_settings::ValueToContentSetting(&setting.setting_value)); }); } std::string GetCookieSettingDescription(Profile* profile) { HostContentSettingsMap* map = HostContentSettingsMapFactory::GetForProfile(profile); auto content_setting = map->GetDefaultContentSetting(ContentSettingsType::COOKIES, nullptr); bool block_third_party = profile->GetPrefs()->GetBoolean(prefs::kBlockThirdPartyCookies); auto control_mode = static_cast( profile->GetPrefs()->GetInteger(prefs::kCookieControlsMode)); // Determine what the effective cookie setting is. These conditions are not // mutually exclusive and rely on ordering. if (content_setting == ContentSetting::CONTENT_SETTING_BLOCK) { return l10n_util::GetPluralStringFUTF8( IDS_SETTINGS_SITE_SETTINGS_COOKIES_BLOCK, GetNumCookieExceptionsOfTypes( map, {ContentSetting::CONTENT_SETTING_ALLOW, ContentSetting::CONTENT_SETTING_SESSION_ONLY})); } else if (block_third_party) { return l10n_util::GetStringUTF8( IDS_SETTINGS_SITE_SETTINGS_COOKIES_BLOCK_THIRD_PARTY); } else if (base::FeatureList::IsEnabled( content_settings::kImprovedCookieControls) && control_mode == content_settings::CookieControlsMode::kIncognitoOnly) { return l10n_util::GetStringUTF8( IDS_SETTINGS_SITE_SETTINGS_COOKIES_BLOCK_THIRD_PARTY_INCOGNITO); } else { // We do not make a distinction between allow and clear on exit. return l10n_util::GetPluralStringFUTF8( IDS_SETTINGS_SITE_SETTINGS_COOKIES_ALLOW, GetNumCookieExceptionsOfTypes(map, {ContentSetting::CONTENT_SETTING_BLOCK})); } } } // namespace SiteSettingsHandler::SiteSettingsHandler(Profile* profile, web_app::AppRegistrar& app_registrar) : profile_(profile), app_registrar_(app_registrar) {} SiteSettingsHandler::~SiteSettingsHandler() { if (cookies_tree_model_) cookies_tree_model_->RemoveCookiesTreeObserver(this); } void SiteSettingsHandler::RegisterMessages() { web_ui()->RegisterMessageCallback( "fetchUsageTotal", base::BindRepeating(&SiteSettingsHandler::HandleFetchUsageTotal, base::Unretained(this))); web_ui()->RegisterMessageCallback( "clearUsage", base::BindRepeating(&SiteSettingsHandler::HandleClearUsage, base::Unretained(this))); web_ui()->RegisterMessageCallback( "setDefaultValueForContentType", base::BindRepeating( &SiteSettingsHandler::HandleSetDefaultValueForContentType, base::Unretained(this))); web_ui()->RegisterMessageCallback( "getDefaultValueForContentType", base::BindRepeating( &SiteSettingsHandler::HandleGetDefaultValueForContentType, base::Unretained(this))); web_ui()->RegisterMessageCallback( "getAllSites", base::BindRepeating(&SiteSettingsHandler::HandleGetAllSites, base::Unretained(this))); web_ui()->RegisterMessageCallback( "getCookieControlsManagedState", base::BindRepeating( &SiteSettingsHandler::HandleGetCookieControlsManagedState, base::Unretained(this))); web_ui()->RegisterMessageCallback( "getCookieSettingDescription", base::BindRepeating( &SiteSettingsHandler::HandleGetCookieSettingDescription, base::Unretained(this))); web_ui()->RegisterMessageCallback( "getRecentSitePermissions", base::BindRepeating(&SiteSettingsHandler::HandleGetRecentSitePermissions, base::Unretained(this))); web_ui()->RegisterMessageCallback( "getFormattedBytes", base::BindRepeating(&SiteSettingsHandler::HandleGetFormattedBytes, base::Unretained(this))); web_ui()->RegisterMessageCallback( "getExceptionList", base::BindRepeating(&SiteSettingsHandler::HandleGetExceptionList, base::Unretained(this))); web_ui()->RegisterMessageCallback( "getChooserExceptionList", base::BindRepeating(&SiteSettingsHandler::HandleGetChooserExceptionList, base::Unretained(this))); web_ui()->RegisterMessageCallback( "getOriginPermissions", base::BindRepeating(&SiteSettingsHandler::HandleGetOriginPermissions, base::Unretained(this))); web_ui()->RegisterMessageCallback( "setOriginPermissions", base::BindRepeating(&SiteSettingsHandler::HandleSetOriginPermissions, base::Unretained(this))); web_ui()->RegisterMessageCallback( "clearFlashPref", base::BindRepeating(&SiteSettingsHandler::HandleClearFlashPref, base::Unretained(this))); web_ui()->RegisterMessageCallback( "resetCategoryPermissionForPattern", base::BindRepeating( &SiteSettingsHandler::HandleResetCategoryPermissionForPattern, base::Unretained(this))); web_ui()->RegisterMessageCallback( "setCategoryPermissionForPattern", base::BindRepeating( &SiteSettingsHandler::HandleSetCategoryPermissionForPattern, base::Unretained(this))); web_ui()->RegisterMessageCallback( "resetChooserExceptionForSite", base::BindRepeating( &SiteSettingsHandler::HandleResetChooserExceptionForSite, base::Unretained(this))); web_ui()->RegisterMessageCallback( "isOriginValid", base::BindRepeating(&SiteSettingsHandler::HandleIsOriginValid, base::Unretained(this))); web_ui()->RegisterMessageCallback( "isPatternValidForType", base::BindRepeating(&SiteSettingsHandler::HandleIsPatternValidForType, base::Unretained(this))); web_ui()->RegisterMessageCallback( "updateIncognitoStatus", base::BindRepeating(&SiteSettingsHandler::HandleUpdateIncognitoStatus, base::Unretained(this))); web_ui()->RegisterMessageCallback( "fetchZoomLevels", base::BindRepeating(&SiteSettingsHandler::HandleFetchZoomLevels, base::Unretained(this))); web_ui()->RegisterMessageCallback( "removeZoomLevel", base::BindRepeating(&SiteSettingsHandler::HandleRemoveZoomLevel, base::Unretained(this))); web_ui()->RegisterMessageCallback( "setBlockAutoplayEnabled", base::BindRepeating(&SiteSettingsHandler::HandleSetBlockAutoplayEnabled, base::Unretained(this))); web_ui()->RegisterMessageCallback( "fetchBlockAutoplayStatus", base::BindRepeating(&SiteSettingsHandler::HandleFetchBlockAutoplayStatus, base::Unretained(this))); web_ui()->RegisterMessageCallback( "clearEtldPlus1DataAndCookies", base::BindRepeating( &SiteSettingsHandler::HandleClearEtldPlus1DataAndCookies, base::Unretained(this))); web_ui()->RegisterMessageCallback( "recordAction", base::BindRepeating(&SiteSettingsHandler::HandleRecordAction, base::Unretained(this))); } void SiteSettingsHandler::OnJavascriptAllowed() { ObserveSourcesForProfile(profile_); if (profile_->HasOffTheRecordProfile()) ObserveSourcesForProfile(profile_->GetOffTheRecordProfile()); // Here we only subscribe to the HostZoomMap for the default storage partition // since we don't allow the user to manage the zoom levels for apps. // We're only interested in zoom-levels that are persisted, since the user // is given the opportunity to view/delete these in the content-settings page. host_zoom_map_subscription_ = content::HostZoomMap::GetDefaultForBrowserContext(profile_) ->AddZoomLevelChangedCallback( base::BindRepeating(&SiteSettingsHandler::OnZoomLevelChanged, base::Unretained(this))); pref_change_registrar_ = std::make_unique(); pref_change_registrar_->Init(profile_->GetPrefs()); // If the block autoplay pref changes send the new status. pref_change_registrar_->Add( prefs::kBlockAutoplayEnabled, base::Bind(&SiteSettingsHandler::SendBlockAutoplayStatus, base::Unretained(this))); // Listen for prefs that impact the effective cookie setting pref_change_registrar_->Add( prefs::kBlockThirdPartyCookies, base::Bind(&SiteSettingsHandler::SendCookieSettingDescription, base::Unretained(this))); pref_change_registrar_->Add( prefs::kCookieControlsMode, base::Bind(&SiteSettingsHandler::SendCookieSettingDescription, base::Unretained(this))); #if defined(OS_CHROMEOS) pref_change_registrar_->Add( prefs::kEnableDRM, base::Bind(&SiteSettingsHandler::OnPrefEnableDrmChanged, base::Unretained(this))); #endif } void SiteSettingsHandler::OnJavascriptDisallowed() { observer_.RemoveAll(); chooser_observer_.RemoveAll(); host_zoom_map_subscription_.reset(); pref_change_registrar_->Remove(prefs::kBlockAutoplayEnabled); pref_change_registrar_->Remove(prefs::kBlockThirdPartyCookies); pref_change_registrar_->Remove(prefs::kCookieControlsMode); #if defined(OS_CHROMEOS) pref_change_registrar_->Remove(prefs::kEnableDRM); #endif observed_profiles_.RemoveAll(); } void SiteSettingsHandler::OnGetUsageInfo() { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); // Site Details Page does not display the number of cookies for the origin. const CookieTreeNode* root = cookies_tree_model_->GetRoot(); std::string usage_string = ""; std::string cookie_string = ""; for (const auto& site : root->children()) { std::string title = base::UTF16ToUTF8(site->GetTitle()); if (title != usage_host_) continue; int64_t size = site->InclusiveSize(); if (size != 0) usage_string = base::UTF16ToUTF8(ui::FormatBytes(size)); int num_cookies = site->NumberOfCookies(); if (num_cookies != 0) { cookie_string = base::UTF16ToUTF8(l10n_util::GetPluralStringFUTF16( IDS_SETTINGS_SITE_SETTINGS_NUM_COOKIES, num_cookies)); } break; } FireWebUIListener("usage-total-changed", base::Value(usage_host_), base::Value(usage_string), base::Value(cookie_string)); } #if defined(OS_CHROMEOS) void SiteSettingsHandler::OnPrefEnableDrmChanged() { FireWebUIListener("prefEnableDrmChanged"); } #endif void SiteSettingsHandler::OnContentSettingChanged( const ContentSettingsPattern& primary_pattern, const ContentSettingsPattern& secondary_pattern, ContentSettingsType content_type, const std::string& resource_identifier) { if (!site_settings::HasRegisteredGroupName(content_type)) return; if (primary_pattern.ToString().empty()) { FireWebUIListener("contentSettingCategoryChanged", base::Value(site_settings::ContentSettingsTypeToGroupName( content_type))); } else { FireWebUIListener( "contentSettingSitePermissionChanged", base::Value( site_settings::ContentSettingsTypeToGroupName(content_type)), base::Value(primary_pattern.ToString()), base::Value(secondary_pattern == ContentSettingsPattern::Wildcard() ? "" : secondary_pattern.ToString())); } // If the default sound content setting changed then we should send block // autoplay status. if (primary_pattern == ContentSettingsPattern() && secondary_pattern == ContentSettingsPattern() && content_type == ContentSettingsType::SOUND) { SendBlockAutoplayStatus(); } // If the default cookie setting changed we should update the effective // setting description. if (content_type == ContentSettingsType::COOKIES) { SendCookieSettingDescription(); } } void SiteSettingsHandler::OnOffTheRecordProfileCreated( Profile* off_the_record) { FireWebUIListener("onIncognitoStatusChanged", base::Value(true)); ObserveSourcesForProfile(off_the_record); } void SiteSettingsHandler::OnProfileWillBeDestroyed(Profile* profile) { if (profile->IsOffTheRecord()) FireWebUIListener("onIncognitoStatusChanged", base::Value(false)); StopObservingSourcesForProfile(profile); } void SiteSettingsHandler::OnChooserObjectPermissionChanged( ContentSettingsType guard_content_settings_type, ContentSettingsType data_content_settings_type) { if (!site_settings::HasRegisteredGroupName(guard_content_settings_type) || !site_settings::HasRegisteredGroupName(data_content_settings_type)) { return; } FireWebUIListener("contentSettingChooserPermissionChanged", base::Value(site_settings::ContentSettingsTypeToGroupName( guard_content_settings_type)), base::Value(site_settings::ContentSettingsTypeToGroupName( data_content_settings_type))); } void SiteSettingsHandler::OnZoomLevelChanged( const content::HostZoomMap::ZoomLevelChange& change) { SendZoomLevels(); } void SiteSettingsHandler::HandleFetchUsageTotal(const base::ListValue* args) { AllowJavascript(); CHECK_EQ(1U, args->GetSize()); std::string host; CHECK(args->GetString(0, &host)); usage_host_ = host; update_site_details_ = true; if (cookies_tree_model_ && !send_sites_list_) { cookies_tree_model_->RemoveCookiesTreeObserver(this); cookies_tree_model_.reset(); } EnsureCookiesTreeModelCreated(); } void SiteSettingsHandler::HandleClearUsage(const base::ListValue* args) { CHECK_EQ(1U, args->GetSize()); std::string origin; CHECK(args->GetString(0, &origin)); GURL url(origin); if (!url.is_valid()) return; AllowJavascript(); for (const auto& node : cookies_tree_model_->GetRoot()->children()) { if (origin == node->GetDetailedInfo().origin.GetURL().spec()) { cookies_tree_model_->DeleteCookieNode(node.get()); return; } } } void SiteSettingsHandler::HandleSetDefaultValueForContentType( const base::ListValue* args) { CHECK_EQ(2U, args->GetSize()); std::string content_type; CHECK(args->GetString(0, &content_type)); std::string setting; CHECK(args->GetString(1, &setting)); ContentSetting default_setting; CHECK(content_settings::ContentSettingFromString(setting, &default_setting)); ContentSettingsType type = site_settings::ContentSettingsTypeFromGroupName(content_type); Profile* profile = profile_; #if defined(OS_CHROMEOS) // ChromeOS special case: in Guest mode, settings are opened in Incognito // mode so we need the original profile to actually modify settings. if (user_manager::UserManager::Get()->IsLoggedInAsGuest()) profile = profile->GetOriginalProfile(); #endif HostContentSettingsMap* map = HostContentSettingsMapFactory::GetForProfile(profile); ContentSetting previous_setting = map->GetDefaultContentSetting(type, nullptr); map->SetDefaultContentSetting(type, default_setting); if (type == ContentSettingsType::SOUND && previous_setting != default_setting) { if (default_setting == CONTENT_SETTING_BLOCK) { base::RecordAction( base::UserMetricsAction("SoundContentSetting.MuteBy.DefaultSwitch")); } else { base::RecordAction(base::UserMetricsAction( "SoundContentSetting.UnmuteBy.DefaultSwitch")); } } } void SiteSettingsHandler::HandleGetDefaultValueForContentType( const base::ListValue* args) { AllowJavascript(); CHECK_EQ(2U, args->GetSize()); const base::Value* callback_id; CHECK(args->Get(0, &callback_id)); std::string type; CHECK(args->GetString(1, &type)); ContentSettingsType content_type = site_settings::ContentSettingsTypeFromGroupName(type); HostContentSettingsMap* map = HostContentSettingsMapFactory::GetForProfile(profile_); base::DictionaryValue category; site_settings::GetContentCategorySetting(map, content_type, &category); ResolveJavascriptCallback(*callback_id, category); } void SiteSettingsHandler::HandleGetAllSites(const base::ListValue* args) { AllowJavascript(); CHECK_EQ(2U, args->GetList().size()); std::string callback_id = args->GetList()[0].GetString(); auto types = args->GetList()[1].GetList(); all_sites_map_.clear(); origin_permission_set_.clear(); auto content_types = site_settings::ContentSettingsTypesFromGroupNames(types); // Incognito contains incognito content settings plus non-incognito content // settings. Thus if it exists, just get exceptions for the incognito profile. Profile* profile = profile_; if (profile_->HasOffTheRecordProfile() && profile_->GetOffTheRecordProfile() != profile_) { profile = profile_->GetOffTheRecordProfile(); } DCHECK(profile); HostContentSettingsMap* map = HostContentSettingsMapFactory::GetForProfile(profile); // Retrieve a list of embargoed settings to check separately. This ensures // that only settings included in |content_types| will be listed in all sites. auto* autoblocker = PermissionDecisionAutoBlockerFactory::GetForProfile(profile_); for (auto& url : autoblocker->GetEmbargoedOrigins(content_types)) { // Add |url| to the set if there are any embargo settings. CreateOrAppendSiteGroupEntry(&all_sites_map_, url); origin_permission_set_.insert(url.spec()); } // Get permission exceptions which apply to a single site for (auto content_type : content_types) { auto exceptions = site_settings::GetSiteExceptionsForContentType(map, content_type); for (const auto& e : exceptions) { GURL url = GURL(e.primary_pattern.ToString()); CreateOrAppendSiteGroupEntry(&all_sites_map_, url); origin_permission_set_.insert(url.spec()); } } // Recreate the cookies tree model to refresh the usage information. // This happens in the background and will call TreeModelEndBatch() when // finished. At that point we send usage data to the page. if (cookies_tree_model_) cookies_tree_model_->RemoveCookiesTreeObserver(this); cookies_tree_model_.reset(); EnsureCookiesTreeModelCreated(); base::Value result(base::Value::Type::LIST); // Respond with currently available data. ConvertSiteGroupMapToListValue(all_sites_map_, origin_permission_set_, &result, profile, app_registrar_); LogAllSitesAction(AllSitesAction2::kLoadPage); send_sites_list_ = true; ResolveJavascriptCallback(base::Value(callback_id), result); } void SiteSettingsHandler::HandleGetCookieControlsManagedState( const base::ListValue* args) { AllowJavascript(); CHECK_EQ(1U, args->GetList().size()); std::string callback_id = args->GetList()[0].GetString(); auto managed_states = site_settings::GetCookieControlsManagedState(profile_); base::Value result(base::Value::Type::DICTIONARY); result.SetKey( site_settings::kAllowAll, site_settings::GetValueForManagedState(managed_states.allow_all)); result.SetKey(site_settings::kBlockThirdPartyIncognito, site_settings::GetValueForManagedState( managed_states.block_third_party_incognito)); result.SetKey( site_settings::kBlockThirdParty, site_settings::GetValueForManagedState(managed_states.block_third_party)); result.SetKey( site_settings::kBlockAll, site_settings::GetValueForManagedState(managed_states.block_all)); result.SetKey( site_settings::kSessionOnly, site_settings::GetValueForManagedState(managed_states.session_only)); ResolveJavascriptCallback(base::Value(callback_id), result); } void SiteSettingsHandler::HandleGetCookieSettingDescription( const base::ListValue* args) { AllowJavascript(); CHECK_EQ(1U, args->GetList().size()); std::string callback_id = args->GetList()[0].GetString(); ResolveJavascriptCallback(base::Value(callback_id), base::Value(GetCookieSettingDescription(profile_))); } void SiteSettingsHandler::HandleGetRecentSitePermissions( const base::ListValue* args) { AllowJavascript(); CHECK_EQ(3U, args->GetList().size()); std::string callback_id = args->GetList()[0].GetString(); auto types = args->GetList()[1].GetList(); size_t max_sources = base::checked_cast(args->GetList()[2].GetInt()); auto content_types = site_settings::ContentSettingsTypesFromGroupNames(types); auto recent_site_permissions = site_settings::GetRecentSitePermissions( profile_, content_types, max_sources); // Convert groups of TimestampedPermissions for consumption by JS base::Value result(base::Value::Type::LIST); for (const auto& site_permissions : recent_site_permissions) { DCHECK(!site_permissions.settings.empty()); base::Value recent_site(base::Value::Type::DICTIONARY); recent_site.SetKey(site_settings::kOrigin, base::Value(site_permissions.origin.spec())); recent_site.SetKey(site_settings::kIncognito, base::Value(site_permissions.incognito)); base::Value permissions_list(base::Value::Type::LIST); for (const auto& p : site_permissions.settings) { base::Value recent_permission(base::Value::Type::DICTIONARY); recent_permission.SetKey( site_settings::kType, base::Value( site_settings::ContentSettingsTypeToGroupName(p.content_type))); recent_permission.SetKey( site_settings::kSetting, base::Value( content_settings::ContentSettingToString(p.content_setting))); recent_permission.SetKey( site_settings::kSource, base::Value( site_settings::SiteSettingSourceToString(p.setting_source))); permissions_list.Append(std::move(recent_permission)); } recent_site.SetKey(site_settings::kRecentPermissions, std::move(permissions_list)); result.Append(std::move(recent_site)); } ResolveJavascriptCallback(base::Value(callback_id), result); } base::Value SiteSettingsHandler::PopulateCookiesAndUsageData(Profile* profile) { std::map origin_size_map; std::map origin_cookie_map; base::Value list_value(base::Value::Type::LIST); GetOriginStorage(&all_sites_map_, &origin_size_map); GetOriginCookies(&all_sites_map_, &origin_cookie_map); ConvertSiteGroupMapToListValue(all_sites_map_, origin_permission_set_, &list_value, profile, app_registrar_); // Merge the origin usage and cookies number into |list_value|. for (base::Value& site_group : list_value.GetList()) { base::Value* origin_list = site_group.FindKey(kOriginList); int cookie_num = 0; const std::string& etld_plus1 = site_group.FindKey(kEffectiveTopLevelDomainPlus1Name)->GetString(); const auto& etld_plus1_cookie_num_it = origin_cookie_map.find(etld_plus1); // Add the number of eTLD+1 scoped cookies. if (etld_plus1_cookie_num_it != origin_cookie_map.end()) cookie_num = etld_plus1_cookie_num_it->second; // Iterate over the origins for the ETLD+1, and set their usage and cookie // numbers. for (base::Value& origin_info : origin_list->GetList()) { const std::string& origin = origin_info.FindKey("origin")->GetString(); const auto& size_info_it = origin_size_map.find(origin); if (size_info_it != origin_size_map.end()) origin_info.SetKey( "usage", base::Value(static_cast(size_info_it->second))); GURL origin_url(origin); const auto& origin_cookie_num_it = origin_cookie_map.find(origin_url.host()); if (origin_cookie_num_it != origin_cookie_map.end()) { origin_info.SetKey(kNumCookies, base::Value(origin_cookie_num_it->second)); // Add cookies numbers for origins that isn't an eTLD+1. if (origin_url.host() != etld_plus1) cookie_num += origin_cookie_num_it->second; } } site_group.SetKey(kNumCookies, base::Value(cookie_num)); } return list_value; } void SiteSettingsHandler::OnStorageFetched() { AllowJavascript(); FireWebUIListener("onStorageListFetched", PopulateCookiesAndUsageData(profile_)); } void SiteSettingsHandler::HandleGetFormattedBytes(const base::ListValue* args) { AllowJavascript(); CHECK_EQ(2U, args->GetSize()); const base::Value* callback_id; CHECK(args->Get(0, &callback_id)); double num_bytes; CHECK(args->GetDouble(1, &num_bytes)); const base::string16 string = ui::FormatBytes(int64_t(num_bytes)); ResolveJavascriptCallback(*callback_id, base::Value(string)); } void SiteSettingsHandler::HandleGetExceptionList(const base::ListValue* args) { AllowJavascript(); CHECK_EQ(2U, args->GetSize()); const base::Value* callback_id; CHECK(args->Get(0, &callback_id)); std::string type; CHECK(args->GetString(1, &type)); ContentSettingsType content_type = site_settings::ContentSettingsTypeFromGroupName(type); std::unique_ptr exceptions(new base::ListValue); HostContentSettingsMap* map = HostContentSettingsMapFactory::GetForProfile(profile_); const auto* extension_registry = extensions::ExtensionRegistry::Get(profile_); AddExceptionsGrantedByHostedApps(profile_, APIPermissionFromGroupName(type), exceptions.get()); site_settings::GetExceptionsFromHostContentSettingsMap( map, content_type, extension_registry, web_ui(), /*incognito=*/false, /*filter=*/nullptr, exceptions.get()); Profile* incognito = profile_->HasOffTheRecordProfile() ? profile_->GetOffTheRecordProfile() : nullptr; // On Chrome OS in Guest mode the incognito profile is the primary profile, // so do not fetch an extra copy of the same exceptions. if (incognito && incognito != profile_) { map = HostContentSettingsMapFactory::GetForProfile(incognito); extension_registry = extensions::ExtensionRegistry::Get(incognito); site_settings::GetExceptionsFromHostContentSettingsMap( map, content_type, extension_registry, web_ui(), /*incognito=*/true, /*filter=*/nullptr, exceptions.get()); } ResolveJavascriptCallback(*callback_id, *exceptions.get()); } void SiteSettingsHandler::HandleGetChooserExceptionList( const base::ListValue* args) { AllowJavascript(); CHECK_EQ(2U, args->GetSize()); const base::Value* callback_id; CHECK(args->Get(0, &callback_id)); std::string type; CHECK(args->GetString(1, &type)); const site_settings::ChooserTypeNameEntry* chooser_type = site_settings::ChooserTypeFromGroupName(type); CHECK(chooser_type); base::Value exceptions = site_settings::GetChooserExceptionListFromProfile( profile_, *chooser_type); ResolveJavascriptCallback(*callback_id, std::move(exceptions)); } void SiteSettingsHandler::HandleGetOriginPermissions( const base::ListValue* args) { AllowJavascript(); CHECK_EQ(3U, args->GetSize()); const base::Value* callback_id; CHECK(args->Get(0, &callback_id)); std::string origin; CHECK(args->GetString(1, &origin)); const base::ListValue* types; CHECK(args->GetList(2, &types)); // Note: Invalid URLs will just result in default settings being shown. const GURL origin_url(origin); auto exceptions = std::make_unique(); for (size_t i = 0; i < types->GetSize(); ++i) { std::string type; types->GetString(i, &type); ContentSettingsType content_type = site_settings::ContentSettingsTypeFromGroupName(type); HostContentSettingsMap* map = HostContentSettingsMapFactory::GetForProfile(profile_); const auto* extension_registry = extensions::ExtensionRegistry::Get(profile_); std::string source_string, display_name; ContentSetting content_setting = site_settings::GetContentSettingForOrigin( profile_, map, origin_url, content_type, &source_string, extension_registry, &display_name); std::string content_setting_string = content_settings::ContentSettingToString(content_setting); auto raw_site_exception = std::make_unique(); raw_site_exception->SetString(site_settings::kEmbeddingOrigin, origin); raw_site_exception->SetBoolean(site_settings::kIncognito, profile_->IsOffTheRecord()); raw_site_exception->SetString(site_settings::kOrigin, origin); raw_site_exception->SetString(site_settings::kDisplayName, display_name); raw_site_exception->SetString(site_settings::kSetting, content_setting_string); raw_site_exception->SetString(site_settings::kSource, source_string); exceptions->Append(std::move(raw_site_exception)); } ResolveJavascriptCallback(*callback_id, *exceptions); } void SiteSettingsHandler::HandleSetOriginPermissions( const base::ListValue* args) { CHECK_EQ(3U, args->GetSize()); std::string origin_string; CHECK(args->GetString(0, &origin_string)); const base::ListValue* types; CHECK(args->GetList(1, &types)); std::string value; CHECK(args->GetString(2, &value)); const GURL origin(origin_string); if (!origin.is_valid()) return; ContentSetting setting; CHECK(content_settings::ContentSettingFromString(value, &setting)); for (size_t i = 0; i < types->GetSize(); ++i) { std::string type; types->GetString(i, &type); ContentSettingsType content_type = site_settings::ContentSettingsTypeFromGroupName(type); HostContentSettingsMap* map = HostContentSettingsMapFactory::GetForProfile(profile_); permissions::PermissionUmaUtil::ScopedRevocationReporter scoped_revocation_reporter( profile_, origin, origin, content_type, permissions::PermissionSourceUI::SITE_SETTINGS); // Clear any existing embargo status if the new setting isn't block. if (setting != CONTENT_SETTING_BLOCK) { PermissionDecisionAutoBlockerFactory::GetForProfile(profile_) ->RemoveEmbargoByUrl(origin, content_type); } map->SetContentSettingDefaultScope(origin, origin, content_type, std::string(), setting); if (content_type == ContentSettingsType::SOUND) { ContentSetting default_setting = map->GetDefaultContentSetting(ContentSettingsType::SOUND, nullptr); bool mute = (setting == CONTENT_SETTING_BLOCK) || (setting == CONTENT_SETTING_DEFAULT && default_setting == CONTENT_SETTING_BLOCK); if (mute) { base::RecordAction( base::UserMetricsAction("SoundContentSetting.MuteBy.SiteSettings")); } else { base::RecordAction(base::UserMetricsAction( "SoundContentSetting.UnmuteBy.SiteSettings")); } } content_settings::LogWebSiteSettingsPermissionChange(content_type, setting); } // Show an infobar reminding the user to reload tabs where their site // permissions have been updated. for (auto* it : *BrowserList::GetInstance()) { TabStripModel* tab_strip = it->tab_strip_model(); for (int i = 0; i < tab_strip->count(); ++i) { content::WebContents* web_contents = tab_strip->GetWebContentsAt(i); GURL tab_url = web_contents->GetLastCommittedURL(); if (url::IsSameOriginWith(origin, tab_url)) { InfoBarService* infobar_service = InfoBarService::FromWebContents(web_contents); PageInfoInfoBarDelegate::Create(infobar_service); } } } } void SiteSettingsHandler::HandleClearFlashPref(const base::ListValue* args) { CHECK_EQ(1U, args->GetSize()); std::string origin_string; CHECK(args->GetString(0, &origin_string)); HostContentSettingsMap* map = HostContentSettingsMapFactory::GetForProfile(profile_); const GURL origin(origin_string); map->SetWebsiteSettingDefaultScope(origin, origin, ContentSettingsType::PLUGINS_DATA, std::string(), nullptr); } void SiteSettingsHandler::HandleResetCategoryPermissionForPattern( const base::ListValue* args) { CHECK_EQ(4U, args->GetSize()); std::string primary_pattern_string; CHECK(args->GetString(0, &primary_pattern_string)); std::string secondary_pattern_string; CHECK(args->GetString(1, &secondary_pattern_string)); std::string type; CHECK(args->GetString(2, &type)); bool incognito; CHECK(args->GetBoolean(3, &incognito)); ContentSettingsType content_type = site_settings::ContentSettingsTypeFromGroupName(type); Profile* profile = nullptr; if (incognito) { if (!profile_->HasOffTheRecordProfile()) return; profile = profile_->GetOffTheRecordProfile(); } else { profile = profile_; } HostContentSettingsMap* map = HostContentSettingsMapFactory::GetForProfile(profile); ContentSettingsPattern primary_pattern = ContentSettingsPattern::FromString(primary_pattern_string); ContentSettingsPattern secondary_pattern = secondary_pattern_string.empty() ? ContentSettingsPattern::Wildcard() : ContentSettingsPattern::FromString(secondary_pattern_string); permissions::PermissionUmaUtil::ScopedRevocationReporter scoped_revocation_reporter( profile, primary_pattern, secondary_pattern, content_type, permissions::PermissionSourceUI::SITE_SETTINGS); map->SetContentSettingCustomScope(primary_pattern, secondary_pattern, content_type, "", CONTENT_SETTING_DEFAULT); if (content_type == ContentSettingsType::SOUND) { ContentSetting default_setting = map->GetDefaultContentSetting(ContentSettingsType::SOUND, nullptr); if (default_setting == CONTENT_SETTING_BLOCK) { base::RecordAction(base::UserMetricsAction( "SoundContentSetting.MuteBy.PatternException")); } else { base::RecordAction(base::UserMetricsAction( "SoundContentSetting.UnmuteBy.PatternException")); } } content_settings::LogWebSiteSettingsPermissionChange( content_type, ContentSetting::CONTENT_SETTING_DEFAULT); } void SiteSettingsHandler::HandleSetCategoryPermissionForPattern( const base::ListValue* args) { CHECK_EQ(5U, args->GetSize()); std::string primary_pattern_string; CHECK(args->GetString(0, &primary_pattern_string)); std::string secondary_pattern_string; CHECK(args->GetString(1, &secondary_pattern_string)); std::string type; CHECK(args->GetString(2, &type)); std::string value; CHECK(args->GetString(3, &value)); bool incognito; CHECK(args->GetBoolean(4, &incognito)); ContentSettingsType content_type = site_settings::ContentSettingsTypeFromGroupName(type); ContentSetting setting; CHECK(content_settings::ContentSettingFromString(value, &setting)); Profile* profile = nullptr; if (incognito) { if (!profile_->HasOffTheRecordProfile()) return; profile = profile_->GetOffTheRecordProfile(); } else { profile = profile_; } HostContentSettingsMap* map = HostContentSettingsMapFactory::GetForProfile(profile); ContentSettingsPattern primary_pattern = ContentSettingsPattern::FromString(primary_pattern_string); ContentSettingsPattern secondary_pattern = secondary_pattern_string.empty() ? ContentSettingsPattern::Wildcard() : ContentSettingsPattern::FromString(secondary_pattern_string); permissions::PermissionUmaUtil::ScopedRevocationReporter scoped_revocation_reporter( profile, primary_pattern, secondary_pattern, content_type, permissions::PermissionSourceUI::SITE_SETTINGS); map->SetContentSettingCustomScope(primary_pattern, secondary_pattern, content_type, "", setting); if (content_type == ContentSettingsType::SOUND) { ContentSetting default_setting = map->GetDefaultContentSetting(ContentSettingsType::SOUND, nullptr); bool mute = (setting == CONTENT_SETTING_BLOCK) || (setting == CONTENT_SETTING_DEFAULT && default_setting == CONTENT_SETTING_BLOCK); if (mute) { base::RecordAction(base::UserMetricsAction( "SoundContentSetting.MuteBy.PatternException")); } else { base::RecordAction(base::UserMetricsAction( "SoundContentSetting.UnmuteBy.PatternException")); } } content_settings::LogWebSiteSettingsPermissionChange(content_type, setting); } void SiteSettingsHandler::HandleResetChooserExceptionForSite( const base::ListValue* args) { CHECK_EQ(4U, args->GetSize()); std::string chooser_type_str; CHECK(args->GetString(0, &chooser_type_str)); const site_settings::ChooserTypeNameEntry* chooser_type = site_settings::ChooserTypeFromGroupName(chooser_type_str); CHECK(chooser_type); std::string origin_str; CHECK(args->GetString(1, &origin_str)); GURL requesting_origin(origin_str); CHECK(requesting_origin.is_valid()); std::string embedding_origin_str; CHECK(args->GetString(2, &embedding_origin_str)); GURL embedding_origin(embedding_origin_str); CHECK(embedding_origin.is_valid()); permissions::ChooserContextBase* chooser_context = chooser_type->get_context(profile_); chooser_context->RevokeObjectPermission( url::Origin::Create(requesting_origin), url::Origin::Create(embedding_origin), args->GetList()[3]); } void SiteSettingsHandler::HandleIsOriginValid(const base::ListValue* args) { AllowJavascript(); CHECK_EQ(2U, args->GetSize()); const base::Value* callback_id; CHECK(args->Get(0, &callback_id)); std::string origin_string; CHECK(args->GetString(1, &origin_string)); ResolveJavascriptCallback(*callback_id, base::Value(GURL(origin_string).is_valid())); } void SiteSettingsHandler::HandleIsPatternValidForType( const base::ListValue* args) { AllowJavascript(); CHECK_EQ(3U, args->GetSize()); const base::Value* callback_id; CHECK(args->Get(0, &callback_id)); std::string pattern_string; CHECK(args->GetString(1, &pattern_string)); std::string type; CHECK(args->GetString(2, &type)); std::string reason = ""; bool is_valid = IsPatternValidForType(pattern_string, type, profile_, &reason); base::Value return_value(base::Value::Type::DICTIONARY); return_value.SetKey(kIsValidKey, base::Value(is_valid)); return_value.SetKey(kReasonKey, base::Value(std::move(reason))); ResolveJavascriptCallback(*callback_id, return_value); } void SiteSettingsHandler::HandleUpdateIncognitoStatus( const base::ListValue* args) { AllowJavascript(); FireWebUIListener("onIncognitoStatusChanged", base::Value(profile_->HasOffTheRecordProfile())); } void SiteSettingsHandler::HandleFetchZoomLevels(const base::ListValue* args) { AllowJavascript(); SendZoomLevels(); } void SiteSettingsHandler::SendZoomLevels() { if (!IsJavascriptAllowed()) return; base::ListValue zoom_levels_exceptions; content::HostZoomMap* host_zoom_map = content::HostZoomMap::GetDefaultForBrowserContext(profile_); content::HostZoomMap::ZoomLevelVector zoom_levels( host_zoom_map->GetAllZoomLevels()); const auto* extension_registry = extensions::ExtensionRegistry::Get(profile_); // Sort ZoomLevelChanges by host and scheme // (a.com < http://a.com < https://a.com < b.com). std::sort(zoom_levels.begin(), zoom_levels.end(), [](const content::HostZoomMap::ZoomLevelChange& a, const content::HostZoomMap::ZoomLevelChange& b) { return a.host == b.host ? a.scheme < b.scheme : a.host < b.host; }); for (const auto& zoom_level : zoom_levels) { std::unique_ptr exception(new base::DictionaryValue); switch (zoom_level.mode) { case content::HostZoomMap::ZOOM_CHANGED_FOR_HOST: { std::string host = zoom_level.host; if (host == content::kUnreachableWebDataURL) { host = l10n_util::GetStringUTF8(IDS_ZOOMLEVELS_CHROME_ERROR_PAGES_LABEL); } exception->SetString(site_settings::kOrigin, host); std::string display_name = host; std::string origin_for_favicon = host; // As an optimization, only check hosts that could be an extension. if (crx_file::id_util::IdIsValid(host)) { // Look up the host as an extension, if found then it is an extension. const extensions::Extension* extension = extension_registry->GetExtensionById( host, extensions::ExtensionRegistry::EVERYTHING); if (extension) { origin_for_favicon = extension->url().spec(); display_name = extension->name(); } } exception->SetString(site_settings::kDisplayName, display_name); exception->SetString(site_settings::kOriginForFavicon, origin_for_favicon); break; } case content::HostZoomMap::ZOOM_CHANGED_FOR_SCHEME_AND_HOST: // These are not stored in preferences and get cleared on next browser // start. Therefore, we don't care for them. continue; case content::HostZoomMap::PAGE_SCALE_IS_ONE_CHANGED: continue; case content::HostZoomMap::ZOOM_CHANGED_TEMPORARY_ZOOM: NOTREACHED(); } std::string setting_string = content_settings::ContentSettingToString(CONTENT_SETTING_DEFAULT); DCHECK(!setting_string.empty()); exception->SetString(site_settings::kSetting, setting_string); // Calculate the zoom percent from the factor. Round up to the nearest whole // number. int zoom_percent = static_cast( blink::PageZoomLevelToZoomFactor(zoom_level.zoom_level) * 100 + 0.5); exception->SetString(kZoom, base::FormatPercent(zoom_percent)); exception->SetString(site_settings::kSource, site_settings::SiteSettingSourceToString( site_settings::SiteSettingSource::kPreference)); // Append the new entry to the list and map. zoom_levels_exceptions.Append(std::move(exception)); } FireWebUIListener("onZoomLevelsChanged", zoom_levels_exceptions); } void SiteSettingsHandler::HandleRemoveZoomLevel(const base::ListValue* args) { CHECK_EQ(1U, args->GetSize()); std::string origin; CHECK(args->GetString(0, &origin)); if (origin == l10n_util::GetStringUTF8(IDS_ZOOMLEVELS_CHROME_ERROR_PAGES_LABEL)) { origin = content::kUnreachableWebDataURL; } content::HostZoomMap* host_zoom_map; host_zoom_map = content::HostZoomMap::GetDefaultForBrowserContext(profile_); double default_level = host_zoom_map->GetDefaultZoomLevel(); host_zoom_map->SetZoomLevelForHost(origin, default_level); } void SiteSettingsHandler::HandleFetchBlockAutoplayStatus( const base::ListValue* args) { AllowJavascript(); SendBlockAutoplayStatus(); } void SiteSettingsHandler::SendBlockAutoplayStatus() { if (!IsJavascriptAllowed()) return; base::DictionaryValue status; // Whether the block autoplay toggle should be checked. base::DictionaryValue pref; pref.SetKey( "value", base::Value( UnifiedAutoplayConfig::ShouldBlockAutoplay(profile_) && UnifiedAutoplayConfig::IsBlockAutoplayUserModifiable(profile_))); status.SetKey("pref", std::move(pref)); // Whether the block autoplay toggle should be enabled. status.SetKey( "enabled", base::Value( UnifiedAutoplayConfig::IsBlockAutoplayUserModifiable(profile_))); FireWebUIListener("onBlockAutoplayStatusChanged", status); } void SiteSettingsHandler::HandleSetBlockAutoplayEnabled( const base::ListValue* args) { AllowJavascript(); if (!UnifiedAutoplayConfig::IsBlockAutoplayUserModifiable(profile_)) return; CHECK_EQ(1U, args->GetSize()); bool value; CHECK(args->GetBoolean(0, &value)); profile_->GetPrefs()->SetBoolean(prefs::kBlockAutoplayEnabled, value); } void SiteSettingsHandler::EnsureCookiesTreeModelCreated() { if (cookies_tree_model_) return; cookies_tree_model_ = CookiesTreeModel::CreateForProfile(profile_); cookies_tree_model_->AddCookiesTreeObserver(this); } void SiteSettingsHandler::ObserveSourcesForProfile(Profile* profile) { auto* map = HostContentSettingsMapFactory::GetForProfile(profile); if (!observer_.IsObserving(map)) observer_.Add(map); auto* usb_context = UsbChooserContextFactory::GetForProfile(profile); if (!chooser_observer_.IsObserving(usb_context)) chooser_observer_.Add(usb_context); auto* serial_context = SerialChooserContextFactory::GetForProfile(profile); if (!chooser_observer_.IsObserving(serial_context)) chooser_observer_.Add(serial_context); auto* hid_context = HidChooserContextFactory::GetForProfile(profile); if (!chooser_observer_.IsObserving(hid_context)) chooser_observer_.Add(hid_context); if (base::FeatureList::IsEnabled( features::kWebBluetoothNewPermissionsBackend)) { auto* bluetooth_context = BluetoothChooserContextFactory::GetForProfile(profile); if (!chooser_observer_.IsObserving(bluetooth_context)) chooser_observer_.Add(bluetooth_context); } observed_profiles_.Add(profile); } void SiteSettingsHandler::StopObservingSourcesForProfile(Profile* profile) { auto* map = HostContentSettingsMapFactory::GetForProfile(profile); if (observer_.IsObserving(map)) observer_.Remove(map); auto* usb_context = UsbChooserContextFactory::GetForProfile(profile); if (chooser_observer_.IsObserving(usb_context)) chooser_observer_.Remove(usb_context); auto* serial_context = SerialChooserContextFactory::GetForProfile(profile); if (chooser_observer_.IsObserving(serial_context)) chooser_observer_.Remove(serial_context); auto* hid_context = HidChooserContextFactory::GetForProfile(profile); if (chooser_observer_.IsObserving(hid_context)) chooser_observer_.Remove(hid_context); if (base::FeatureList::IsEnabled( features::kWebBluetoothNewPermissionsBackend)) { auto* bluetooth_context = BluetoothChooserContextFactory::GetForProfile(profile); if (chooser_observer_.IsObserving(bluetooth_context)) chooser_observer_.Remove(bluetooth_context); } observed_profiles_.Remove(profile); } void SiteSettingsHandler::TreeNodesAdded(ui::TreeModel* model, ui::TreeModelNode* parent, size_t start, size_t count) {} void SiteSettingsHandler::TreeNodesRemoved(ui::TreeModel* model, ui::TreeModelNode* parent, size_t start, size_t count) {} void SiteSettingsHandler::TreeNodeChanged(ui::TreeModel* model, ui::TreeModelNode* node) {} void SiteSettingsHandler::TreeModelEndBatch(CookiesTreeModel* model) { // The WebUI may have shut down before we get the data. if (!IsJavascriptAllowed()) return; if (send_sites_list_) OnStorageFetched(); if (update_site_details_) OnGetUsageInfo(); send_sites_list_ = false; update_site_details_ = false; } void SiteSettingsHandler::GetOriginStorage( std::map>* all_sites_map, std::map* origin_size_map) { CHECK(cookies_tree_model_.get()); for (const auto& site : cookies_tree_model_->GetRoot()->children()) { int64_t size = site->InclusiveSize(); if (size == 0) continue; UpdateDataFromCookiesTree(all_sites_map, origin_size_map, site->GetDetailedInfo().origin.GetURL(), size); } } void SiteSettingsHandler::GetOriginCookies( std::map>* all_sites_map, std::map* origin_cookie_map) { CHECK(cookies_tree_model_.get()); // Get sites that don't have data but have cookies. for (const auto& site : cookies_tree_model_->GetRoot()->children()) { GURL url = site->GetDetailedInfo().origin.GetURL(); (*origin_cookie_map)[url.host()] = site->NumberOfCookies(); CreateOrAppendSiteGroupEntry(all_sites_map, url, /*url_is_origin_with_cookies = */ true); } } void SiteSettingsHandler::HandleClearEtldPlus1DataAndCookies( const base::ListValue* args) { CHECK_EQ(1U, args->GetSize()); std::string etld_plus1_string; CHECK(args->GetString(0, &etld_plus1_string)); AllowJavascript(); CookieTreeNode* parent = cookies_tree_model_->GetRoot(); // Find all the nodes that contain the given etld+1. std::vector nodes_to_delete; for (const auto& node : parent->children()) { std::string cookie_node_etld_plus1 = net::registry_controlled_domains::GetDomainAndRegistry( base::UTF16ToUTF8(node->GetTitle()), net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES); if (etld_plus1_string == cookie_node_etld_plus1) nodes_to_delete.push_back(node.get()); } for (auto* node : nodes_to_delete) cookies_tree_model_->DeleteCookieNode(node); } void SiteSettingsHandler::HandleRecordAction(const base::ListValue* args) { CHECK_EQ(1U, args->GetSize()); int action; CHECK(args->GetInteger(0, &action)); DCHECK_LE(action, static_cast(AllSitesAction2::kMaxValue)); DCHECK_GE(action, static_cast(AllSitesAction2::kLoadPage)); LogAllSitesAction(static_cast(action)); } void SiteSettingsHandler::SetCookiesTreeModelForTesting( std::unique_ptr cookies_tree_model) { cookies_tree_model_ = std::move(cookies_tree_model); } void SiteSettingsHandler::ClearAllSitesMapForTesting() { all_sites_map_.clear(); } void SiteSettingsHandler::SendCookieSettingDescription() { FireWebUIListener("cookieSettingDescriptionChanged", base::Value(GetCookieSettingDescription(profile_))); } } // namespace settings