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