1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/ui/webui/site_settings_helper.h"
6
7 #include <algorithm>
8 #include <functional>
9 #include <set>
10 #include <string>
11
12 #include "base/feature_list.h"
13 #include "base/stl_util.h"
14 #include "base/strings/string16.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/values.h"
17 #include "chrome/browser/bluetooth/bluetooth_chooser_context.h"
18 #include "chrome/browser/bluetooth/bluetooth_chooser_context_factory.h"
19 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
20 #include "chrome/browser/hid/hid_chooser_context.h"
21 #include "chrome/browser/hid/hid_chooser_context_factory.h"
22 #include "chrome/browser/permissions/permission_manager_factory.h"
23 #include "chrome/browser/profiles/profile.h"
24 #include "chrome/browser/serial/serial_chooser_context.h"
25 #include "chrome/browser/serial/serial_chooser_context_factory.h"
26 #include "chrome/browser/usb/usb_chooser_context.h"
27 #include "chrome/browser/usb/usb_chooser_context_factory.h"
28 #include "chrome/common/pref_names.h"
29 #include "components/content_settings/core/browser/cookie_settings.h"
30 #include "components/content_settings/core/browser/host_content_settings_map.h"
31 #include "components/content_settings/core/common/content_settings.h"
32 #include "components/content_settings/core/common/content_settings_pattern.h"
33 #include "components/content_settings/core/common/content_settings_utils.h"
34 #include "components/content_settings/core/common/pref_names.h"
35 #include "components/permissions/chooser_context_base.h"
36 #include "components/permissions/permission_manager.h"
37 #include "components/permissions/permission_result.h"
38 #include "components/prefs/pref_service.h"
39 #include "components/subresource_filter/core/browser/subresource_filter_features.h"
40 #include "components/url_formatter/url_formatter.h"
41 #include "content/public/common/content_features.h"
42 #include "extensions/browser/extension_registry.h"
43 #include "extensions/common/constants.h"
44 #include "url/origin.h"
45
46 namespace site_settings {
47
48 constexpr char kAppName[] = "appName";
49 constexpr char kAppId[] = "appId";
50
51 namespace {
52
53 // Maps from the UI string to the object it represents (for sorting purposes).
54 typedef std::multimap<std::string, const base::DictionaryValue*> SortedObjects;
55
56 // Maps from a secondary URL to the set of objects it has permission to access.
57 typedef std::map<GURL, SortedObjects> OneOriginObjects;
58
59 // Maps from a primary URL/source pair to a OneOriginObjects. All the mappings
60 // in OneOriginObjects share the given primary URL and source.
61 typedef std::map<std::pair<GURL, std::string>, OneOriginObjects>
62 AllOriginObjects;
63
64 // Chooser data group names.
65 const char kUsbChooserDataGroupType[] = "usb-devices-data";
66 const char kSerialChooserDataGroupType[] = "serial-ports-data";
67 const char kHidChooserDataGroupType[] = "hid-devices-data";
68 const char kBluetoothChooserDataGroupType[] = "bluetooth-devices-data";
69
70 const ContentSettingsTypeNameEntry kContentSettingsTypeGroupNames[] = {
71 // The following ContentSettingsTypes have UI in Content Settings
72 // and require a mapping from their Javascript string representation in
73 // chrome/browser/resources/settings/site_settings/constants.js to their C++
74 // ContentSettingsType provided here.
75 {ContentSettingsType::COOKIES, "cookies"},
76 {ContentSettingsType::IMAGES, "images"},
77 {ContentSettingsType::JAVASCRIPT, "javascript"},
78 {ContentSettingsType::PLUGINS, "plugins"},
79 {ContentSettingsType::POPUPS, "popups"},
80 {ContentSettingsType::GEOLOCATION, "location"},
81 {ContentSettingsType::NOTIFICATIONS, "notifications"},
82 {ContentSettingsType::MEDIASTREAM_MIC, "media-stream-mic"},
83 {ContentSettingsType::MEDIASTREAM_CAMERA, "media-stream-camera"},
84 {ContentSettingsType::PROTOCOL_HANDLERS, "register-protocol-handler"},
85 {ContentSettingsType::PPAPI_BROKER, "ppapi-broker"},
86 {ContentSettingsType::AUTOMATIC_DOWNLOADS, "multiple-automatic-downloads"},
87 {ContentSettingsType::MIDI_SYSEX, "midi-sysex"},
88 {ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER, "protected-content"},
89 {ContentSettingsType::BACKGROUND_SYNC, "background-sync"},
90 {ContentSettingsType::ADS, "ads"},
91 {ContentSettingsType::SOUND, "sound"},
92 {ContentSettingsType::CLIPBOARD_READ_WRITE, "clipboard"},
93 {ContentSettingsType::SENSORS, "sensors"},
94 {ContentSettingsType::PAYMENT_HANDLER, "payment-handler"},
95 {ContentSettingsType::USB_GUARD, "usb-devices"},
96 {ContentSettingsType::USB_CHOOSER_DATA, kUsbChooserDataGroupType},
97 {ContentSettingsType::IDLE_DETECTION, "idle-detection"},
98 {ContentSettingsType::SERIAL_GUARD, "serial-ports"},
99 {ContentSettingsType::SERIAL_CHOOSER_DATA, kSerialChooserDataGroupType},
100 {ContentSettingsType::BLUETOOTH_SCANNING, "bluetooth-scanning"},
101 {ContentSettingsType::HID_GUARD, "hid-devices"},
102 {ContentSettingsType::HID_CHOOSER_DATA, kHidChooserDataGroupType},
103 {ContentSettingsType::NATIVE_FILE_SYSTEM_WRITE_GUARD,
104 "native-file-system-write"},
105 {ContentSettingsType::MIXEDSCRIPT, "mixed-script"},
106 {ContentSettingsType::VR, "vr"},
107 {ContentSettingsType::AR, "ar"},
108 {ContentSettingsType::BLUETOOTH_GUARD, "bluetooth-devices"},
109 {ContentSettingsType::BLUETOOTH_CHOOSER_DATA,
110 kBluetoothChooserDataGroupType},
111
112 // Add new content settings here if a corresponding Javascript string
113 // representation for it is not required. Note some exceptions do have UI in
114 // Content Settings but do not require a separate string.
115 {ContentSettingsType::DEFAULT, nullptr},
116 {ContentSettingsType::AUTO_SELECT_CERTIFICATE, nullptr},
117 {ContentSettingsType::SSL_CERT_DECISIONS, nullptr},
118 {ContentSettingsType::APP_BANNER, nullptr},
119 {ContentSettingsType::SITE_ENGAGEMENT, nullptr},
120 {ContentSettingsType::DURABLE_STORAGE, nullptr},
121 {ContentSettingsType::AUTOPLAY, nullptr},
122 {ContentSettingsType::IMPORTANT_SITE_INFO, nullptr},
123 {ContentSettingsType::PERMISSION_AUTOBLOCKER_DATA, nullptr},
124 {ContentSettingsType::ADS_DATA, nullptr},
125 {ContentSettingsType::MIDI, nullptr},
126 {ContentSettingsType::PASSWORD_PROTECTION, nullptr},
127 {ContentSettingsType::MEDIA_ENGAGEMENT, nullptr},
128 {ContentSettingsType::CLIENT_HINTS, nullptr},
129 {ContentSettingsType::ACCESSIBILITY_EVENTS, nullptr},
130 {ContentSettingsType::CLIPBOARD_SANITIZED_WRITE, nullptr},
131 {ContentSettingsType::PLUGINS_DATA, nullptr},
132 {ContentSettingsType::BACKGROUND_FETCH, nullptr},
133 {ContentSettingsType::INTENT_PICKER_DISPLAY, nullptr},
134 {ContentSettingsType::PERIODIC_BACKGROUND_SYNC, nullptr},
135 {ContentSettingsType::WAKE_LOCK_SCREEN, nullptr},
136 {ContentSettingsType::WAKE_LOCK_SYSTEM, nullptr},
137 {ContentSettingsType::LEGACY_COOKIE_ACCESS, nullptr},
138 {ContentSettingsType::INSTALLED_WEB_APP_METADATA, nullptr},
139 {ContentSettingsType::NFC, nullptr},
140 {ContentSettingsType::SAFE_BROWSING_URL_CHECK_DATA, nullptr},
141 {ContentSettingsType::NATIVE_FILE_SYSTEM_READ_GUARD, nullptr},
142 {ContentSettingsType::STORAGE_ACCESS, nullptr},
143 };
144 static_assert(base::size(kContentSettingsTypeGroupNames) ==
145 // ContentSettingsType starts at -1, so add 1 here.
146 static_cast<int32_t>(ContentSettingsType::NUM_TYPES) + 1,
147 "kContentSettingsTypeGroupNames should have "
148 "CONTENT_SETTINGS_NUM_TYPES elements");
149
150 struct SiteSettingSourceStringMapping {
151 SiteSettingSource source;
152 const char* source_str;
153 };
154
155 const SiteSettingSourceStringMapping kSiteSettingSourceStringMapping[] = {
156 {SiteSettingSource::kAdsFilterBlacklist, "ads-filter-blacklist"},
157 {SiteSettingSource::kDefault, "default"},
158 {SiteSettingSource::kDrmDisabled, "drm-disabled"},
159 {SiteSettingSource::kEmbargo, "embargo"},
160 {SiteSettingSource::kExtension, "extension"},
161 {SiteSettingSource::kInsecureOrigin, "insecure-origin"},
162 {SiteSettingSource::kKillSwitch, "kill-switch"},
163 {SiteSettingSource::kPolicy, "policy"},
164 {SiteSettingSource::kPreference, "preference"},
165 };
166 static_assert(base::size(kSiteSettingSourceStringMapping) ==
167 static_cast<int>(SiteSettingSource::kNumSources),
168 "kSiteSettingSourceStringMapping should have "
169 "SiteSettingSource::kNumSources elements");
170
171 struct PolicyIndicatorTypeStringMapping {
172 PolicyIndicatorType source;
173 const char* indicator_str;
174 };
175
176 // Converts a policy indicator type to its JS usable string representation.
177 const PolicyIndicatorTypeStringMapping kPolicyIndicatorTypeStringMapping[] = {
178 {PolicyIndicatorType::kDevicePolicy, "devicePolicy"},
179 {PolicyIndicatorType::kExtension, "extension"},
180 {PolicyIndicatorType::kNone, "none"},
181 {PolicyIndicatorType::kOwner, "owner"},
182 {PolicyIndicatorType::kPrimaryUser, "primary_user"},
183 {PolicyIndicatorType::kRecommended, "recommended"},
184 {PolicyIndicatorType::kUserPolicy, "userPolicy"},
185 {PolicyIndicatorType::kParent, "parent"},
186 {PolicyIndicatorType::kChildRestriction, "childRestriction"},
187 };
188 static_assert(base::size(kPolicyIndicatorTypeStringMapping) ==
189 static_cast<int>(PolicyIndicatorType::kNumIndicators),
190 "kPolicyIndicatorStringMapping should have "
191 "PolicyIndicatorType::kNumIndicators elements");
192
193 // Retrieves the corresponding string, according to the following precedence
194 // order from highest to lowest priority:
195 // 1. Kill-switch.
196 // 2. Insecure origins (some permissions are denied to insecure origins).
197 // 3. Enterprise policy.
198 // 4. Extensions.
199 // 5. Activated for ads filtering (for Ads ContentSettingsType only).
200 // 6. DRM disabled (for CrOS's Protected Content ContentSettingsType only).
201 // 7. User-set per-origin setting.
202 // 8. Embargo.
203 // 9. User-set patterns.
204 // 10. User-set global default for a ContentSettingsType.
205 // 11. Chrome's built-in default.
CalculateSiteSettingSource(Profile * profile,const ContentSettingsType content_type,const GURL & origin,const content_settings::SettingInfo & info,const permissions::PermissionResult result)206 SiteSettingSource CalculateSiteSettingSource(
207 Profile* profile,
208 const ContentSettingsType content_type,
209 const GURL& origin,
210 const content_settings::SettingInfo& info,
211 const permissions::PermissionResult result) {
212 if (result.source == permissions::PermissionStatusSource::KILL_SWITCH)
213 return SiteSettingSource::kKillSwitch; // Source #1.
214
215 if (result.source == permissions::PermissionStatusSource::INSECURE_ORIGIN)
216 return SiteSettingSource::kInsecureOrigin; // Source #2.
217
218 if (info.source == content_settings::SETTING_SOURCE_POLICY ||
219 info.source == content_settings::SETTING_SOURCE_SUPERVISED) {
220 return SiteSettingSource::kPolicy; // Source #3.
221 }
222
223 if (info.source == content_settings::SETTING_SOURCE_EXTENSION)
224 return SiteSettingSource::kExtension; // Source #4.
225
226 if (content_type == ContentSettingsType::ADS &&
227 base::FeatureList::IsEnabled(
228 subresource_filter::kSafeBrowsingSubresourceFilter)) {
229 HostContentSettingsMap* map =
230 HostContentSettingsMapFactory::GetForProfile(profile);
231 if (map->GetWebsiteSetting(origin, GURL(), ContentSettingsType::ADS_DATA,
232 /*resource_identifier=*/std::string(),
233 /*setting_info=*/nullptr) != nullptr) {
234 return SiteSettingSource::kAdsFilterBlacklist; // Source #5.
235 }
236 }
237
238 // Protected Content will be blocked if the |kEnableDRM| pref is off.
239 if (content_type == ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER &&
240 !profile->GetPrefs()->GetBoolean(prefs::kEnableDRM)) {
241 return SiteSettingSource::kDrmDisabled; // Source #6.
242 }
243
244 DCHECK_NE(content_settings::SETTING_SOURCE_NONE, info.source);
245 if (info.source == content_settings::SETTING_SOURCE_USER) {
246 if (result.source ==
247 permissions::PermissionStatusSource::MULTIPLE_DISMISSALS ||
248 result.source ==
249 permissions::PermissionStatusSource::MULTIPLE_IGNORES) {
250 return SiteSettingSource::kEmbargo; // Source #8.
251 }
252 if (info.primary_pattern == ContentSettingsPattern::Wildcard() &&
253 info.secondary_pattern == ContentSettingsPattern::Wildcard()) {
254 return SiteSettingSource::kDefault; // Source #10, #11.
255 }
256
257 // Source #7, #9. When #7 is the source, |result.source| won't
258 // be set to any of the source #7 enum values, as PermissionManager is
259 // aware of the difference between these two sources internally. The
260 // subtlety here should go away when PermissionManager can handle all
261 // content settings and all possible sources.
262 return SiteSettingSource::kPreference;
263 }
264
265 NOTREACHED();
266 return SiteSettingSource::kPreference;
267 }
268
269 // Whether |pattern| applies to a single origin.
PatternAppliesToSingleOrigin(const ContentSettingPatternSource & pattern)270 bool PatternAppliesToSingleOrigin(const ContentSettingPatternSource& pattern) {
271 const GURL url(pattern.primary_pattern.ToString());
272 // Default settings and other patterns apply to multiple origins.
273 if (url::Origin::Create(url).opaque())
274 return false;
275 // Embedded content settings only when |url| is embedded in another origin, so
276 // ignore non-wildcard secondary patterns that are different to the primary.
277 if (pattern.primary_pattern != pattern.secondary_pattern &&
278 pattern.secondary_pattern != ContentSettingsPattern::Wildcard()) {
279 return false;
280 }
281 return true;
282 }
283
284 // Retrieves the source of a chooser exception as a string. This method uses the
285 // CalculateSiteSettingSource method above to calculate the correct string to
286 // use.
GetSourceStringForChooserException(Profile * profile,ContentSettingsType content_type,content_settings::SettingSource source)287 std::string GetSourceStringForChooserException(
288 Profile* profile,
289 ContentSettingsType content_type,
290 content_settings::SettingSource source) {
291 // Prepare the parameters needed by CalculateSiteSettingSource
292 content_settings::SettingInfo info;
293 info.source = source;
294
295 // Chooser exceptions do not use a PermissionContextBase for their
296 // permissions.
297 permissions::PermissionResult permission_result(
298 CONTENT_SETTING_DEFAULT,
299 permissions::PermissionStatusSource::UNSPECIFIED);
300
301 // The |origin| parameter is only used for |ContentSettingsType::ADS| with
302 // the |kSafeBrowsingSubresourceFilter| feature flag enabled, so an empty GURL
303 // is used.
304 SiteSettingSource calculated_source = CalculateSiteSettingSource(
305 profile, content_type, /*origin=*/GURL::EmptyGURL(), info,
306 permission_result);
307 DCHECK(calculated_source == SiteSettingSource::kPolicy ||
308 calculated_source == SiteSettingSource::kPreference);
309 return SiteSettingSourceToString(calculated_source);
310 }
311
GetUsbChooserContext(Profile * profile)312 permissions::ChooserContextBase* GetUsbChooserContext(Profile* profile) {
313 return UsbChooserContextFactory::GetForProfile(profile);
314 }
315
GetSerialChooserContext(Profile * profile)316 permissions::ChooserContextBase* GetSerialChooserContext(Profile* profile) {
317 return SerialChooserContextFactory::GetForProfile(profile);
318 }
319
GetHidChooserContext(Profile * profile)320 permissions::ChooserContextBase* GetHidChooserContext(Profile* profile) {
321 return HidChooserContextFactory::GetForProfile(profile);
322 }
323
324 // The BluetoothChooserContext is only available when the
325 // WebBluetoothNewPermissionsBackend flag is enabled.
326 // TODO(https://crbug.com/589228): Remove the feature check when it is enabled
327 // by default.
GetBluetoothChooserContext(Profile * profile)328 permissions::ChooserContextBase* GetBluetoothChooserContext(Profile* profile) {
329 if (base::FeatureList::IsEnabled(
330 features::kWebBluetoothNewPermissionsBackend)) {
331 return BluetoothChooserContextFactory::GetForProfile(profile);
332 }
333 return nullptr;
334 }
335
336 const ChooserTypeNameEntry kChooserTypeGroupNames[] = {
337 {&GetUsbChooserContext, kUsbChooserDataGroupType},
338 {&GetSerialChooserContext, kSerialChooserDataGroupType},
339 {&GetHidChooserContext, kHidChooserDataGroupType},
340 {&GetBluetoothChooserContext, kBluetoothChooserDataGroupType}};
341
GetPolicyIndicatorFromSettingSource(content_settings::SettingSource setting_source)342 PolicyIndicatorType GetPolicyIndicatorFromSettingSource(
343 content_settings::SettingSource setting_source) {
344 switch (setting_source) {
345 case content_settings::SETTING_SOURCE_POLICY:
346 return PolicyIndicatorType::kDevicePolicy;
347 case content_settings::SETTING_SOURCE_SUPERVISED:
348 return PolicyIndicatorType::kParent;
349 case content_settings::SETTING_SOURCE_EXTENSION:
350 return PolicyIndicatorType::kExtension;
351 case content_settings::SETTING_SOURCE_USER:
352 case content_settings::SETTING_SOURCE_WHITELIST:
353 case content_settings::SETTING_SOURCE_NONE:
354 return PolicyIndicatorType::kNone;
355 case content_settings::SETTING_SOURCE_INSTALLED_WEBAPP:
356 NOTREACHED();
357 return PolicyIndicatorType::kNone;
358 }
359 }
360
361 } // namespace
362
HasRegisteredGroupName(ContentSettingsType type)363 bool HasRegisteredGroupName(ContentSettingsType type) {
364 for (size_t i = 0; i < base::size(kContentSettingsTypeGroupNames); ++i) {
365 if (type == kContentSettingsTypeGroupNames[i].type &&
366 kContentSettingsTypeGroupNames[i].name != nullptr) {
367 return true;
368 }
369 }
370 return false;
371 }
372
ContentSettingsTypeFromGroupName(const std::string & name)373 ContentSettingsType ContentSettingsTypeFromGroupName(const std::string& name) {
374 for (size_t i = 0; i < base::size(kContentSettingsTypeGroupNames); ++i) {
375 if (name == kContentSettingsTypeGroupNames[i].name)
376 return kContentSettingsTypeGroupNames[i].type;
377 }
378
379 NOTREACHED() << name << " is not a recognized content settings type.";
380 return ContentSettingsType::DEFAULT;
381 }
382
ContentSettingsTypeToGroupName(ContentSettingsType type)383 std::string ContentSettingsTypeToGroupName(ContentSettingsType type) {
384 for (size_t i = 0; i < base::size(kContentSettingsTypeGroupNames); ++i) {
385 if (type == kContentSettingsTypeGroupNames[i].type) {
386 const char* name = kContentSettingsTypeGroupNames[i].name;
387 if (name != nullptr)
388 return name;
389 break;
390 }
391 }
392
393 NOTREACHED() << static_cast<int32_t>(type)
394 << " is not a recognized content settings type.";
395 return std::string();
396 }
397
ContentSettingsTypesFromGroupNames(const base::Value::ConstListView types)398 std::vector<ContentSettingsType> ContentSettingsTypesFromGroupNames(
399 const base::Value::ConstListView types) {
400 std::vector<ContentSettingsType> content_types;
401 content_types.reserve(types.size());
402 for (const auto& value : types) {
403 const auto& type = value.GetString();
404 content_types.push_back(
405 site_settings::ContentSettingsTypeFromGroupName(type));
406 }
407 return content_types;
408 }
409
SiteSettingSourceToString(const SiteSettingSource source)410 std::string SiteSettingSourceToString(const SiteSettingSource source) {
411 return kSiteSettingSourceStringMapping[static_cast<int>(source)].source_str;
412 }
413
GetValueForManagedState(const site_settings::ManagedState & state)414 base::Value GetValueForManagedState(const site_settings::ManagedState& state) {
415 base::Value value(base::Value::Type::DICTIONARY);
416 value.SetKey(site_settings::kDisabled, base::Value(state.disabled));
417 value.SetKey(
418 site_settings::kPolicyIndicator,
419 base::Value(site_settings::PolicyIndicatorTypeToString(state.indicator)));
420 return value;
421 }
422
423 // Add an "Allow"-entry to the list of |exceptions| for a |url_pattern| from
424 // the web extent of a hosted |app|.
AddExceptionForHostedApp(const std::string & url_pattern,const extensions::Extension & app,base::ListValue * exceptions)425 void AddExceptionForHostedApp(const std::string& url_pattern,
426 const extensions::Extension& app,
427 base::ListValue* exceptions) {
428 std::unique_ptr<base::DictionaryValue> exception(new base::DictionaryValue());
429
430 std::string setting_string =
431 content_settings::ContentSettingToString(CONTENT_SETTING_ALLOW);
432 DCHECK(!setting_string.empty());
433
434 exception->SetString(kSetting, setting_string);
435 exception->SetString(kOrigin, url_pattern);
436 exception->SetString(kDisplayName, url_pattern);
437 exception->SetString(kEmbeddingOrigin, url_pattern);
438 exception->SetString(kSource, "HostedApp");
439 exception->SetBoolean(kIncognito, false);
440 exception->SetString(kAppName, app.name());
441 exception->SetString(kAppId, app.id());
442 exceptions->Append(std::move(exception));
443 }
444
445 // Create a DictionaryValue* that will act as a data source for a single row
446 // in a HostContentSettingsMap-controlled exceptions table (e.g., cookies).
GetExceptionForPage(const ContentSettingsPattern & pattern,const ContentSettingsPattern & secondary_pattern,const std::string & display_name,const ContentSetting & setting,const std::string & provider_name,bool incognito)447 std::unique_ptr<base::DictionaryValue> GetExceptionForPage(
448 const ContentSettingsPattern& pattern,
449 const ContentSettingsPattern& secondary_pattern,
450 const std::string& display_name,
451 const ContentSetting& setting,
452 const std::string& provider_name,
453 bool incognito) {
454 auto exception = std::make_unique<base::DictionaryValue>();
455 exception->SetString(kOrigin, pattern.ToString());
456 exception->SetString(kDisplayName, display_name);
457 exception->SetString(kEmbeddingOrigin,
458 secondary_pattern == ContentSettingsPattern::Wildcard()
459 ? std::string()
460 : secondary_pattern.ToString());
461
462 std::string setting_string =
463 content_settings::ContentSettingToString(setting);
464 DCHECK(!setting_string.empty());
465
466 exception->SetString(kSetting, setting_string);
467 exception->SetString(kSource, provider_name);
468 exception->SetBoolean(kIncognito, incognito);
469 return exception;
470 }
471
GetDisplayNameForExtension(const GURL & url,const extensions::ExtensionRegistry * extension_registry)472 std::string GetDisplayNameForExtension(
473 const GURL& url,
474 const extensions::ExtensionRegistry* extension_registry) {
475 if (extension_registry && url.SchemeIs(extensions::kExtensionScheme)) {
476 // For the extension scheme, the pattern must be a valid URL.
477 DCHECK(url.is_valid());
478 const extensions::Extension* extension =
479 extension_registry->GetExtensionById(
480 url.host(), extensions::ExtensionRegistry::EVERYTHING);
481 if (extension)
482 return extension->name();
483 }
484 return std::string();
485 }
486
487 // Takes |url| and converts it into an individual origin string or retrieves
488 // name of the extension it belongs to.
GetDisplayNameForGURL(const GURL & url,const extensions::ExtensionRegistry * extension_registry)489 std::string GetDisplayNameForGURL(
490 const GURL& url,
491 const extensions::ExtensionRegistry* extension_registry) {
492 const url::Origin origin = url::Origin::Create(url);
493 if (origin.opaque())
494 return url.spec();
495
496 std::string display_name =
497 GetDisplayNameForExtension(url, extension_registry);
498 if (!display_name.empty())
499 return display_name;
500
501 auto url_16 = url_formatter::FormatUrl(
502 url,
503 url_formatter::kFormatUrlOmitDefaults |
504 url_formatter::kFormatUrlOmitHTTPS |
505 url_formatter::kFormatUrlOmitTrailingSlashOnBareHostname,
506 net::UnescapeRule::NONE, nullptr, nullptr, nullptr);
507 auto url_string = base::UTF16ToUTF8(url_16);
508 return url_string;
509 }
510
511 // If the given |pattern| represents an individual origin or extension, retrieve
512 // a string to display it as such. If not, return the pattern as a string.
GetDisplayNameForPattern(const ContentSettingsPattern & pattern,const extensions::ExtensionRegistry * extension_registry)513 std::string GetDisplayNameForPattern(
514 const ContentSettingsPattern& pattern,
515 const extensions::ExtensionRegistry* extension_registry) {
516 const GURL url(pattern.ToString());
517 const std::string extension_display_name =
518 GetDisplayNameForExtension(url, extension_registry);
519 if (!extension_display_name.empty())
520 return extension_display_name;
521 return pattern.ToString();
522 }
523
GetExceptionsFromHostContentSettingsMap(const HostContentSettingsMap * map,ContentSettingsType type,const extensions::ExtensionRegistry * extension_registry,content::WebUI * web_ui,bool incognito,const std::string * filter,base::ListValue * exceptions)524 void GetExceptionsFromHostContentSettingsMap(
525 const HostContentSettingsMap* map,
526 ContentSettingsType type,
527 const extensions::ExtensionRegistry* extension_registry,
528 content::WebUI* web_ui,
529 bool incognito,
530 const std::string* filter,
531 base::ListValue* exceptions) {
532 ContentSettingsForOneType entries;
533 map->GetSettingsForOneType(type, std::string(), &entries);
534 // Group settings by primary_pattern.
535 AllPatternsSettings all_patterns_settings;
536 for (auto i = entries.begin(); i != entries.end(); ++i) {
537 // Don't add default settings.
538 if (i->primary_pattern == ContentSettingsPattern::Wildcard() &&
539 i->secondary_pattern == ContentSettingsPattern::Wildcard() &&
540 i->source !=
541 SiteSettingSourceToString(SiteSettingSource::kPreference)) {
542 continue;
543 }
544
545 // Off-the-record HostContentSettingsMap contains incognito content settings
546 // as well as normal content settings. Here, we use the incongnito settings
547 // only.
548 if (map->IsOffTheRecord() && !i->incognito)
549 continue;
550
551 if (filter && i->primary_pattern.ToString() != *filter)
552 continue;
553
554 all_patterns_settings[std::make_pair(i->primary_pattern, i->source)]
555 [i->secondary_pattern] = i->GetContentSetting();
556 }
557
558 // Keep the exceptions sorted by provider so they will be displayed in
559 // precedence order.
560 std::vector<std::unique_ptr<base::DictionaryValue>>
561 all_provider_exceptions[HostContentSettingsMap::NUM_PROVIDER_TYPES];
562
563 // |all_patterns_settings| is sorted from the lowest precedence pattern to
564 // the highest (see operator< in ContentSettingsPattern), so traverse it in
565 // reverse to show the patterns with the highest precedence (the more specific
566 // ones) on the top.
567 for (auto i = all_patterns_settings.rbegin();
568 i != all_patterns_settings.rend(); ++i) {
569 const ContentSettingsPattern& primary_pattern = i->first.first;
570 const OnePatternSettings& one_settings = i->second;
571 const std::string display_name =
572 GetDisplayNameForPattern(primary_pattern, extension_registry);
573
574 // The "parent" entry either has an identical primary and secondary pattern,
575 // or has a wildcard secondary. The two cases are indistinguishable in the
576 // UI.
577 auto parent = one_settings.find(primary_pattern);
578 if (parent == one_settings.end())
579 parent = one_settings.find(ContentSettingsPattern::Wildcard());
580
581 const std::string& source = i->first.second;
582 auto& this_provider_exceptions = all_provider_exceptions
583 [HostContentSettingsMap::GetProviderTypeFromSource(source)];
584
585 // Add the "parent" entry for the non-embedded setting.
586 ContentSetting parent_setting =
587 parent == one_settings.end() ? CONTENT_SETTING_DEFAULT : parent->second;
588 const ContentSettingsPattern& secondary_pattern =
589 parent == one_settings.end() ? primary_pattern : parent->first;
590 this_provider_exceptions.push_back(
591 GetExceptionForPage(primary_pattern, secondary_pattern, display_name,
592 parent_setting, source, incognito));
593
594 // Add the "children" for any embedded settings.
595 for (auto j = one_settings.begin(); j != one_settings.end(); ++j) {
596 // Skip the non-embedded setting which we already added above.
597 if (j == parent)
598 continue;
599
600 ContentSetting content_setting = j->second;
601 this_provider_exceptions.push_back(
602 GetExceptionForPage(primary_pattern, j->first, display_name,
603 content_setting, source, incognito));
604 }
605 }
606
607 // For camera and microphone, we do not have policy exceptions, but we do have
608 // the policy-set allowed URLs, which should be displayed in the same manner.
609 if (type == ContentSettingsType::MEDIASTREAM_MIC ||
610 type == ContentSettingsType::MEDIASTREAM_CAMERA) {
611 auto& policy_exceptions = all_provider_exceptions
612 [HostContentSettingsMap::GetProviderTypeFromSource(
613 SiteSettingSourceToString(SiteSettingSource::kPolicy))];
614 DCHECK(policy_exceptions.empty());
615 GetPolicyAllowedUrls(type, &policy_exceptions, extension_registry, web_ui,
616 incognito);
617 }
618
619 for (auto& one_provider_exceptions : all_provider_exceptions) {
620 for (auto& exception : one_provider_exceptions)
621 exceptions->Append(std::move(exception));
622 }
623 }
624
GetContentCategorySetting(const HostContentSettingsMap * map,ContentSettingsType content_type,base::DictionaryValue * object)625 void GetContentCategorySetting(const HostContentSettingsMap* map,
626 ContentSettingsType content_type,
627 base::DictionaryValue* object) {
628 std::string provider;
629 std::string setting = content_settings::ContentSettingToString(
630 map->GetDefaultContentSetting(content_type, &provider));
631 DCHECK(!setting.empty());
632
633 object->SetString(kSetting, setting);
634 if (provider != SiteSettingSourceToString(SiteSettingSource::kDefault))
635 object->SetString(kSource, provider);
636 }
637
GetContentSettingForOrigin(Profile * profile,const HostContentSettingsMap * map,const GURL & origin,ContentSettingsType content_type,std::string * source_string,const extensions::ExtensionRegistry * extension_registry,std::string * display_name)638 ContentSetting GetContentSettingForOrigin(
639 Profile* profile,
640 const HostContentSettingsMap* map,
641 const GURL& origin,
642 ContentSettingsType content_type,
643 std::string* source_string,
644 const extensions::ExtensionRegistry* extension_registry,
645 std::string* display_name) {
646 // TODO(patricialor): In future, PermissionManager should know about all
647 // content settings, not just the permissions, plus all the possible sources,
648 // and the calls to HostContentSettingsMap should be removed.
649 content_settings::SettingInfo info;
650 std::unique_ptr<base::Value> value = map->GetWebsiteSetting(
651 origin, origin, content_type, std::string(), &info);
652
653 // Retrieve the content setting.
654 permissions::PermissionResult result(
655 CONTENT_SETTING_DEFAULT,
656 permissions::PermissionStatusSource::UNSPECIFIED);
657 if (permissions::PermissionUtil::IsPermission(content_type)) {
658 result =
659 PermissionManagerFactory::GetForProfile(profile)->GetPermissionStatus(
660 content_type, origin, origin);
661 } else {
662 DCHECK(value.get());
663 DCHECK_EQ(base::Value::Type::INTEGER, value->type());
664 result.content_setting =
665 content_settings::ValueToContentSetting(value.get());
666 }
667
668 // Retrieve the source of the content setting.
669 *source_string = SiteSettingSourceToString(
670 CalculateSiteSettingSource(profile, content_type, origin, info, result));
671 *display_name = GetDisplayNameForGURL(origin, extension_registry);
672
673 return result.content_setting;
674 }
675
GetSiteExceptionsForContentType(HostContentSettingsMap * map,ContentSettingsType content_type)676 std::vector<ContentSettingPatternSource> GetSiteExceptionsForContentType(
677 HostContentSettingsMap* map,
678 ContentSettingsType content_type) {
679 ContentSettingsForOneType entries;
680 map->GetSettingsForOneType(content_type, std::string(), &entries);
681 entries.erase(std::remove_if(entries.begin(), entries.end(),
682 [](const ContentSettingPatternSource& e) {
683 return !PatternAppliesToSingleOrigin(e);
684 }),
685 entries.end());
686 return entries;
687 }
688
GetPolicyAllowedUrls(ContentSettingsType type,std::vector<std::unique_ptr<base::DictionaryValue>> * exceptions,const extensions::ExtensionRegistry * extension_registry,content::WebUI * web_ui,bool incognito)689 void GetPolicyAllowedUrls(
690 ContentSettingsType type,
691 std::vector<std::unique_ptr<base::DictionaryValue>>* exceptions,
692 const extensions::ExtensionRegistry* extension_registry,
693 content::WebUI* web_ui,
694 bool incognito) {
695 DCHECK(type == ContentSettingsType::MEDIASTREAM_MIC ||
696 type == ContentSettingsType::MEDIASTREAM_CAMERA);
697
698 PrefService* prefs = Profile::FromWebUI(web_ui)->GetPrefs();
699 const base::ListValue* policy_urls =
700 prefs->GetList(type == ContentSettingsType::MEDIASTREAM_MIC
701 ? prefs::kAudioCaptureAllowedUrls
702 : prefs::kVideoCaptureAllowedUrls);
703
704 // Convert the URLs to |ContentSettingsPattern|s. Ignore any invalid ones.
705 std::vector<ContentSettingsPattern> patterns;
706 for (const auto& entry : *policy_urls) {
707 std::string url;
708 bool valid_string = entry.GetAsString(&url);
709 if (!valid_string)
710 continue;
711
712 ContentSettingsPattern pattern = ContentSettingsPattern::FromString(url);
713 if (!pattern.IsValid())
714 continue;
715
716 patterns.push_back(pattern);
717 }
718
719 // The patterns are shown in the UI in a reverse order defined by
720 // |ContentSettingsPattern::operator<|.
721 std::sort(patterns.begin(), patterns.end(),
722 std::greater<ContentSettingsPattern>());
723
724 for (const ContentSettingsPattern& pattern : patterns) {
725 std::string display_name =
726 GetDisplayNameForPattern(pattern, extension_registry);
727 exceptions->push_back(GetExceptionForPage(
728 pattern, ContentSettingsPattern(), display_name, CONTENT_SETTING_ALLOW,
729 SiteSettingSourceToString(SiteSettingSource::kPolicy), incognito));
730 }
731 }
732
ChooserTypeFromGroupName(const std::string & name)733 const ChooserTypeNameEntry* ChooserTypeFromGroupName(const std::string& name) {
734 for (const auto& chooser_type : kChooserTypeGroupNames) {
735 if (chooser_type.name == name)
736 return &chooser_type;
737 }
738 return nullptr;
739 }
740
741 // Create a DictionaryValue* that will act as a data source for a single row
742 // in a chooser permission exceptions table. The chooser permission will contain
743 // a list of site exceptions that correspond to the exception.
CreateChooserExceptionObject(const base::string16 & display_name,const base::Value & object,const std::string & chooser_type,const ChooserExceptionDetails & chooser_exception_details)744 base::Value CreateChooserExceptionObject(
745 const base::string16& display_name,
746 const base::Value& object,
747 const std::string& chooser_type,
748 const ChooserExceptionDetails& chooser_exception_details) {
749 base::Value exception(base::Value::Type::DICTIONARY);
750
751 std::string setting_string =
752 content_settings::ContentSettingToString(CONTENT_SETTING_DEFAULT);
753 DCHECK(!setting_string.empty());
754
755 exception.SetStringKey(kDisplayName, display_name);
756 exception.SetKey(kObject, object.Clone());
757 exception.SetStringKey(kChooserType, chooser_type);
758
759 // Order the sites by the provider precedence order.
760 std::vector<base::Value>
761 all_provider_sites[HostContentSettingsMap::NUM_PROVIDER_TYPES];
762 for (const auto& details : chooser_exception_details) {
763 const GURL& requesting_origin = details.first.first;
764 const std::string& source = details.first.second;
765
766 auto& this_provider_sites =
767 all_provider_sites[HostContentSettingsMap::GetProviderTypeFromSource(
768 source)];
769
770 for (const auto& embedding_origin_incognito_pair : details.second) {
771 const GURL& embedding_origin = embedding_origin_incognito_pair.first;
772 const bool incognito = embedding_origin_incognito_pair.second;
773 base::Value site(base::Value::Type::DICTIONARY);
774
775 site.SetStringKey(kOrigin, requesting_origin.spec());
776 site.SetStringKey(kDisplayName, requesting_origin.spec());
777 site.SetStringKey(kEmbeddingOrigin, embedding_origin.is_empty()
778 ? std::string()
779 : embedding_origin.spec());
780 site.SetStringKey(kSetting, setting_string);
781 site.SetStringKey(kSource, source);
782 site.SetBoolKey(kIncognito, incognito);
783 this_provider_sites.push_back(std::move(site));
784 }
785 }
786
787 base::Value sites(base::Value::Type::LIST);
788 for (auto& one_provider_sites : all_provider_sites) {
789 for (auto& site : one_provider_sites) {
790 sites.Append(std::move(site));
791 }
792 }
793
794 exception.SetKey(kSites, std::move(sites));
795 return exception;
796 }
797
GetChooserExceptionListFromProfile(Profile * profile,const ChooserTypeNameEntry & chooser_type)798 base::Value GetChooserExceptionListFromProfile(
799 Profile* profile,
800 const ChooserTypeNameEntry& chooser_type) {
801 base::Value exceptions(base::Value::Type::LIST);
802 ContentSettingsType content_type =
803 ContentSettingsTypeFromGroupName(std::string(chooser_type.name));
804
805 // The BluetoothChooserContext is only available when the
806 // WebBluetoothNewPermissionsBackend flag is enabled.
807 // TODO(https://crbug.com/589228): Remove the nullptr check when it is enabled
808 // by default.
809 permissions::ChooserContextBase* chooser_context =
810 chooser_type.get_context(profile);
811 if (!chooser_context)
812 return exceptions;
813
814 std::vector<std::unique_ptr<permissions::ChooserContextBase::Object>>
815 objects = chooser_context->GetAllGrantedObjects();
816
817 if (profile->HasOffTheRecordProfile()) {
818 Profile* incognito_profile = profile->GetOffTheRecordProfile();
819 permissions::ChooserContextBase* incognito_chooser_context =
820 chooser_type.get_context(incognito_profile);
821 std::vector<std::unique_ptr<permissions::ChooserContextBase::Object>>
822 incognito_objects = incognito_chooser_context->GetAllGrantedObjects();
823 objects.insert(objects.end(),
824 std::make_move_iterator(incognito_objects.begin()),
825 std::make_move_iterator(incognito_objects.end()));
826 }
827
828 // Maps from a chooser exception name/object pair to a
829 // ChooserExceptionDetails. This will group and sort the exceptions by the UI
830 // string and object for display.
831 std::map<std::pair<base::string16, base::Value>, ChooserExceptionDetails>
832 all_chooser_objects;
833 for (const auto& object : objects) {
834 base::string16 name = chooser_context->GetObjectDisplayName(object->value);
835 auto& chooser_exception_details =
836 all_chooser_objects[std::make_pair(name, object->value.Clone())];
837
838 std::string source = GetSourceStringForChooserException(
839 profile, content_type, object->source);
840
841 const auto requesting_origin_source_pair =
842 std::make_pair(object->requesting_origin, source);
843 auto& embedding_origin_incognito_pair_set =
844 chooser_exception_details[requesting_origin_source_pair];
845
846 const auto embedding_origin_incognito_pair =
847 std::make_pair(object->embedding_origin, object->incognito);
848 embedding_origin_incognito_pair_set.insert(embedding_origin_incognito_pair);
849 }
850
851 for (const auto& all_chooser_objects_entry : all_chooser_objects) {
852 const base::string16& name = all_chooser_objects_entry.first.first;
853 const base::Value& object = all_chooser_objects_entry.first.second;
854 const ChooserExceptionDetails& chooser_exception_details =
855 all_chooser_objects_entry.second;
856 exceptions.Append(CreateChooserExceptionObject(
857 name, object, chooser_type.name, chooser_exception_details));
858 }
859
860 return exceptions;
861 }
862
GetCookieControlsManagedState(Profile * profile)863 CookieControlsManagedState GetCookieControlsManagedState(Profile* profile) {
864 // Setup a default unmanaged state that is updated based on the actual
865 // managed state.
866 CookieControlsManagedState managed_state;
867
868 HostContentSettingsMap* map =
869 HostContentSettingsMapFactory::GetForProfile(profile);
870 std::string content_setting_provider;
871 auto content_setting = map->GetDefaultContentSetting(
872 ContentSettingsType::COOKIES, &content_setting_provider);
873 auto content_setting_source =
874 HostContentSettingsMap::GetSettingSourceFromProviderName(
875 content_setting_provider);
876 bool content_setting_enforced =
877 content_setting_source !=
878 content_settings::SettingSource::SETTING_SOURCE_USER;
879
880 // Both the content setting and the block_third_party preference can
881 // be controlled via policy.
882 const PrefService::Preference* block_third_party_pref =
883 profile->GetPrefs()->FindPreference(prefs::kBlockThirdPartyCookies);
884 bool block_third_party_on = block_third_party_pref->GetValue()->GetBool();
885 bool block_third_party_enforced = !block_third_party_pref->IsUserModifiable();
886 // IsRecommended() cannot be used as we care if a recommended value exists at
887 // all, even if a user has overwritten it.
888 bool block_third_party_recommended =
889 (block_third_party_pref && block_third_party_pref->GetRecommendedValue());
890 bool block_third_party_recommended_on =
891 block_third_party_recommended &&
892 block_third_party_pref->GetRecommendedValue()->GetBool();
893
894 // kCookieControlsMode == kOn should imply block_third_party is on.
895 auto control_mode = static_cast<content_settings::CookieControlsMode>(
896 profile->GetPrefs()->GetInteger(prefs::kCookieControlsMode));
897 DCHECK(control_mode != content_settings::CookieControlsMode::kOn ||
898 block_third_party_on)
899 << "kCookieControlsMode == kOn should imply "
900 << "kBlockThirdPartyCookies is true";
901
902 // Get indicators representing each settings source. These may or may not
903 // be used depending on the determined managed state.
904 auto content_setting_source_indicator =
905 GetPolicyIndicatorFromSettingSource(content_setting_source);
906 auto block_third_party_source_indicator =
907 GetPolicyIndicatorFromPref(block_third_party_pref);
908
909 if (!content_setting_enforced && !block_third_party_enforced &&
910 !block_third_party_recommended) {
911 // No cookie controls are managed or recommended.
912 return managed_state;
913 }
914
915 if (content_setting_enforced) {
916 // Block and session only managed state only depend on the content setting.
917 managed_state.block_all = {/*disabled*/ true,
918 content_setting_source_indicator};
919 managed_state.session_only = {/*disabled*/ true,
920 content_setting_source_indicator};
921 }
922
923 if (content_setting_enforced && content_setting == CONTENT_SETTING_BLOCK) {
924 // All remaining controls are managed by the content setting source.
925 managed_state.allow_all = {/*disabled*/ true,
926 content_setting_source_indicator};
927 managed_state.block_third_party_incognito = {
928 /*disabled*/ true, content_setting_source_indicator};
929 managed_state.block_third_party = {/*disabled*/ true,
930 content_setting_source_indicator};
931 return managed_state;
932 }
933 if (content_setting_enforced && block_third_party_enforced) {
934 // All remaining controls are managed by the block_third_party source.
935 managed_state.allow_all = {/*disabled*/ true,
936 block_third_party_source_indicator};
937 managed_state.block_third_party_incognito = {
938 /*disabled*/ true, block_third_party_source_indicator};
939 managed_state.block_third_party = {/*disabled*/ true,
940 block_third_party_source_indicator};
941 return managed_state;
942 }
943 DCHECK(!content_setting_enforced ||
944 content_setting == CONTENT_SETTING_ALLOW ||
945 content_setting == CONTENT_SETTING_SESSION_ONLY);
946 DCHECK(!content_setting_enforced || !block_third_party_enforced);
947 // At this stage the content setting is not enforcing a BLOCK state. Given
948 // this, allow and block_third_party are still valid choices that do not
949 // contradict the content setting. They can thus be controlled or recommended
950 // by the block_third_party preference.
951 if (block_third_party_enforced) {
952 DCHECK(!content_setting_enforced);
953 managed_state.block_third_party_incognito = {
954 true, block_third_party_source_indicator};
955 if (block_third_party_on) {
956 managed_state.allow_all = {/*disabled*/ true,
957 block_third_party_source_indicator};
958 } else {
959 managed_state.block_third_party = {/*disabled*/ true,
960 block_third_party_source_indicator};
961 }
962 return managed_state;
963 }
964 if (block_third_party_recommended) {
965 if (block_third_party_recommended_on) {
966 managed_state.block_third_party = {/*disabled*/ false,
967 block_third_party_source_indicator};
968 } else {
969 managed_state.allow_all = {/*disabled*/ false,
970 block_third_party_source_indicator};
971 }
972 return managed_state;
973 }
974 DCHECK(content_setting_enforced && !block_third_party_enforced &&
975 !block_third_party_recommended);
976 return managed_state;
977 }
978
PolicyIndicatorTypeToString(const PolicyIndicatorType type)979 std::string PolicyIndicatorTypeToString(const PolicyIndicatorType type) {
980 return kPolicyIndicatorTypeStringMapping[static_cast<int>(type)]
981 .indicator_str;
982 }
983
GetPolicyIndicatorFromPref(const PrefService::Preference * pref)984 PolicyIndicatorType GetPolicyIndicatorFromPref(
985 const PrefService::Preference* pref) {
986 if (!pref) {
987 return PolicyIndicatorType::kNone;
988 }
989 if (pref->IsExtensionControlled()) {
990 return PolicyIndicatorType::kExtension;
991 }
992 if (pref->IsManagedByCustodian()) {
993 return PolicyIndicatorType::kParent;
994 }
995 if (pref->IsManaged()) {
996 return PolicyIndicatorType::kDevicePolicy;
997 }
998 if (pref->GetRecommendedValue()) {
999 return PolicyIndicatorType::kRecommended;
1000 }
1001 return PolicyIndicatorType::kNone;
1002 }
1003
1004 } // namespace site_settings
1005