| Index: chrome/browser/ui/webui/options/content_settings_handler.cc
|
| diff --git a/chrome/browser/ui/webui/options/content_settings_handler.cc b/chrome/browser/ui/webui/options/content_settings_handler.cc
|
| index 60990ff7184d7254d4d22774587166eec6f4bfb1..47c0e4f8d05936ca6d859f45dcd278927394ae8a 100644
|
| --- a/chrome/browser/ui/webui/options/content_settings_handler.cc
|
| +++ b/chrome/browser/ui/webui/options/content_settings_handler.cc
|
| @@ -25,8 +25,11 @@
|
| #include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h"
|
| #include "chrome/browser/extensions/extension_special_storage_policy.h"
|
| #include "chrome/browser/notifications/desktop_notification_profile_util.h"
|
| +#include "chrome/browser/permissions/chooser_context_base.h"
|
| #include "chrome/browser/profiles/profile.h"
|
| #include "chrome/browser/ui/browser_list.h"
|
| +#include "chrome/browser/usb/usb_chooser_context.h"
|
| +#include "chrome/browser/usb/usb_chooser_context_factory.h"
|
| #include "chrome/common/chrome_switches.h"
|
| #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
|
| #include "chrome/common/features.h"
|
| @@ -70,6 +73,16 @@ using content_settings::ContentSettingToString;
|
| using content_settings::ContentSettingFromString;
|
| using extensions::APIPermission;
|
|
|
| +namespace options {
|
| +
|
| +// This struct is declared early so that it can used by functions below.
|
| +struct ContentSettingsHandler::ChooserTypeNameEntry {
|
| + ContentSettingsType type;
|
| + ChooserContextBase* (*get_context)(Profile*);
|
| + const char* name;
|
| + const char* ui_name_key;
|
| +};
|
| +
|
| namespace {
|
|
|
| struct ContentSettingWithExceptions {
|
| @@ -93,6 +106,15 @@ typedef std::map<std::pair<ContentSettingsPattern, std::string>,
|
| OnePatternSettings>
|
| AllPatternsSettings;
|
|
|
| +// Maps from the UI string to the object it represents (for sorting purposes).
|
| +typedef std::multimap<std::string, const base::DictionaryValue*> SortedObjects;
|
| +// Maps from a secondary URL to the set of objects it has permission to access.
|
| +typedef std::map<GURL, SortedObjects> OneOriginObjects;
|
| +// Maps from a primary URL/source pair to a OneOriginObjects. All the mappings
|
| +// in OneOriginObjects share the given primary URL and source.
|
| +typedef std::map<std::pair<GURL, std::string>, OneOriginObjects>
|
| + AllOriginObjects;
|
| +
|
| // The AppFilter is used in AddExceptionsGrantedByHostedApps() to choose
|
| // extensions which should have their extent displayed.
|
| typedef bool (*AppFilter)(const extensions::Extension& app,
|
| @@ -110,6 +132,8 @@ const char kAppId[] = "appId";
|
| const char kEmbeddingOrigin[] = "embeddingOrigin";
|
| const char kPreferencesSource[] = "preference";
|
| const char kZoom[] = "zoom";
|
| +const char kObject[] = "object";
|
| +const char kObjectName[] = "objectName";
|
|
|
| const ContentSettingsTypeNameEntry kContentSettingsTypeGroupNames[] = {
|
| {CONTENT_SETTINGS_TYPE_COOKIES, "cookies"},
|
| @@ -135,6 +159,15 @@ const ContentSettingsTypeNameEntry kContentSettingsTypeGroupNames[] = {
|
| #endif
|
| };
|
|
|
| +ChooserContextBase* GetUsbChooserContext(Profile* profile) {
|
| + return UsbChooserContextFactory::GetForProfile(profile);
|
| +}
|
| +
|
| +const ContentSettingsHandler::ChooserTypeNameEntry kChooserTypeGroupNames[] = {
|
| + {CONTENT_SETTINGS_TYPE_USB_CHOOSER_DATA, &GetUsbChooserContext,
|
| + "usb-devices", "name"},
|
| +};
|
| +
|
| // A pseudo content type. We use it to display data like a content setting even
|
| // though it is not a real content setting.
|
| const char kZoomContentType[] = "zoomlevels";
|
| @@ -248,6 +281,15 @@ ContentSettingsType ContentSettingsTypeFromGroupName(const std::string& name) {
|
| return CONTENT_SETTINGS_TYPE_DEFAULT;
|
| }
|
|
|
| +const ContentSettingsHandler::ChooserTypeNameEntry* ChooserTypeFromGroupName(
|
| + const std::string& name) {
|
| + for (const auto& chooser_type : kChooserTypeGroupNames) {
|
| + if (chooser_type.name == name)
|
| + return &chooser_type;
|
| + }
|
| + return nullptr;
|
| +}
|
| +
|
| // Create a DictionaryValue* that will act as a data source for a single row
|
| // in a HostContentSettingsMap-controlled exceptions table (e.g., cookies).
|
| scoped_ptr<base::DictionaryValue> GetExceptionForPage(
|
| @@ -313,6 +355,31 @@ scoped_ptr<base::DictionaryValue> GetNotificationExceptionForPage(
|
| return make_scoped_ptr(exception);
|
| }
|
|
|
| +// Create a DictionaryValue* that will act as a data source for a single row
|
| +// in a chooser permission exceptions table.
|
| +scoped_ptr<base::DictionaryValue> GetChooserExceptionForPage(
|
| + const GURL& requesting_origin,
|
| + const GURL& embedding_origin,
|
| + const std::string& provider_name,
|
| + const std::string& name,
|
| + const base::DictionaryValue* object) {
|
| + scoped_ptr<base::DictionaryValue> exception(new base::DictionaryValue());
|
| +
|
| + std::string setting_string =
|
| + content_settings::ContentSettingToString(CONTENT_SETTING_DEFAULT);
|
| + DCHECK(!setting_string.empty());
|
| +
|
| + exception->SetString(kSetting, setting_string);
|
| + exception->SetString(kOrigin, requesting_origin.spec());
|
| + exception->SetString(kEmbeddingOrigin, embedding_origin.spec());
|
| + exception->SetString(kSource, provider_name);
|
| + if (object) {
|
| + exception->SetString(kObjectName, name);
|
| + exception->Set(kObject, object->CreateDeepCopy());
|
| + }
|
| + return exception.Pass();
|
| +}
|
| +
|
| // Returns true whenever the |extension| is hosted and has |permission|.
|
| // Must have the AppFilter signature.
|
| template <APIPermission::ID permission>
|
| @@ -372,8 +439,6 @@ void AddExceptionsGrantedByHostedApps(content::BrowserContext* context,
|
|
|
| } // namespace
|
|
|
| -namespace options {
|
| -
|
| ContentSettingsHandler::MediaSettingsInfo::MediaSettingsInfo() {
|
| }
|
|
|
| @@ -440,6 +505,7 @@ void ContentSettingsHandler::GetLocalizedValues(
|
| {"manageHandlers", IDS_HANDLERS_MANAGE},
|
| {"exceptionPatternHeader", IDS_EXCEPTIONS_PATTERN_HEADER},
|
| {"exceptionBehaviorHeader", IDS_EXCEPTIONS_ACTION_HEADER},
|
| + {"exceptionUsbDeviceHeader", IDS_EXCEPTIONS_USB_DEVICE_HEADER},
|
| {"exceptionZoomHeader", IDS_EXCEPTIONS_ZOOM_HEADER},
|
| {"embeddedOnHost", IDS_EXCEPTIONS_GEOLOCATION_EMBEDDED_ON_HOST},
|
| // Cookies filter.
|
| @@ -551,6 +617,8 @@ void ContentSettingsHandler::GetLocalizedValues(
|
| {"pushMessagingAllow", IDS_PUSH_MESSSAGING_ALLOW_RADIO},
|
| {"pushMessagingAsk", IDS_PUSH_MESSSAGING_ASK_RADIO},
|
| {"pushMessagingBlock", IDS_PUSH_MESSSAGING_BLOCK_RADIO},
|
| + {"usbDevicesHeader", IDS_USB_DEVICES_HEADER_AND_TAB_LABEL},
|
| + {"usbDevicesManage", IDS_USB_DEVICES_MANAGE_BUTTON},
|
| {"zoomlevelsHeader", IDS_ZOOMLEVELS_HEADER_AND_TAB_LABEL},
|
| {"zoomLevelsManage", IDS_ZOOMLEVELS_MANAGE_BUTTON},
|
| };
|
| @@ -605,6 +673,8 @@ void ContentSettingsHandler::GetLocalizedValues(
|
| IDS_AUTOMATIC_DOWNLOADS_TAB_LABEL);
|
| RegisterTitle(localized_strings, "midi-sysex",
|
| IDS_MIDI_SYSEX_TAB_LABEL);
|
| + RegisterTitle(localized_strings, "usb-devices",
|
| + IDS_USB_DEVICES_HEADER_AND_TAB_LABEL);
|
| RegisterTitle(localized_strings, "zoomlevels",
|
| IDS_ZOOMLEVELS_HEADER_AND_TAB_LABEL);
|
|
|
| @@ -700,6 +770,7 @@ void ContentSettingsHandler::InitializePage() {
|
|
|
| UpdateHandlersEnabledRadios();
|
| UpdateAllExceptionsViewsFromModel();
|
| + UpdateAllChooserExceptionsViewsFromModel();
|
| UpdateProtectedContentExceptionsButton();
|
| }
|
|
|
| @@ -713,9 +784,16 @@ void ContentSettingsHandler::OnContentSettingChanged(
|
| // TODO(estade): we pretend update_all() is always true.
|
| if (details.update_all_types()) {
|
| UpdateAllExceptionsViewsFromModel();
|
| + UpdateAllChooserExceptionsViewsFromModel();
|
| } else {
|
| if (ContainsKey(GetExceptionsInfoMap(), details.type()))
|
| UpdateExceptionsViewFromModel(details.type());
|
| + for (const ChooserTypeNameEntry& chooser_type : kChooserTypeGroupNames) {
|
| + if (chooser_type.type == details.type()) {
|
| + UpdateChooserExceptionsViewFromModel(chooser_type);
|
| + break;
|
| + }
|
| + }
|
| }
|
| }
|
|
|
| @@ -1033,6 +1111,98 @@ void ContentSettingsHandler::UpdateMIDISysExExceptionsView() {
|
| CONTENT_SETTINGS_TYPE_MIDI_SYSEX);
|
| }
|
|
|
| +void ContentSettingsHandler::UpdateAllChooserExceptionsViewsFromModel() {
|
| + for (const ChooserTypeNameEntry& chooser_type : kChooserTypeGroupNames)
|
| + UpdateChooserExceptionsViewFromModel(chooser_type);
|
| +}
|
| +
|
| +void ContentSettingsHandler::UpdateChooserExceptionsViewFromModel(
|
| + const ChooserTypeNameEntry& chooser_type) {
|
| + ChooserContextBase* chooser_context =
|
| + chooser_type.get_context(Profile::FromWebUI(web_ui()));
|
| + std::vector<scoped_ptr<ChooserContextBase::Object>> objects =
|
| + chooser_context->GetAllGrantedObjects();
|
| + AllOriginObjects all_origin_objects;
|
| + for (const auto& object : objects) {
|
| + std::string name;
|
| + bool found = object->object.GetString(chooser_type.ui_name_key, &name);
|
| + DCHECK(found);
|
| + // It is safe for this structure to hold references into |objects| because
|
| + // they are both destroyed at the end of this function.
|
| + all_origin_objects[make_pair(object->requesting_origin,
|
| + object->source)][object->embedding_origin]
|
| + .insert(make_pair(name, &object->object));
|
| + }
|
| +
|
| + // Keep the exceptions sorted by provider so they will be displayed in
|
| + // precedence order.
|
| + std::vector<scoped_ptr<base::DictionaryValue>>
|
| + all_provider_exceptions[HostContentSettingsMap::NUM_PROVIDER_TYPES];
|
| +
|
| + for (const auto& all_origin_objects_entry : all_origin_objects) {
|
| + const GURL& requesting_origin = all_origin_objects_entry.first.first;
|
| + const std::string& source = all_origin_objects_entry.first.second;
|
| + const OneOriginObjects& one_origin_objects =
|
| + all_origin_objects_entry.second;
|
| +
|
| + auto& this_provider_exceptions = all_provider_exceptions
|
| + [HostContentSettingsMap::GetProviderTypeFromSource(source)];
|
| +
|
| + // Add entries for any non-embedded origins.
|
| + bool has_embedded_entries = false;
|
| + for (const auto& one_origin_objects_entry : one_origin_objects) {
|
| + const GURL& embedding_origin = one_origin_objects_entry.first;
|
| + const SortedObjects& sorted_objects = one_origin_objects_entry.second;
|
| +
|
| + // Skip the embedded settings which will be added below.
|
| + if (requesting_origin != embedding_origin) {
|
| + has_embedded_entries = true;
|
| + continue;
|
| + }
|
| +
|
| + for (const auto& sorted_objects_entry : sorted_objects) {
|
| + this_provider_exceptions.push_back(GetChooserExceptionForPage(
|
| + requesting_origin, embedding_origin, source,
|
| + sorted_objects_entry.first, sorted_objects_entry.second));
|
| + }
|
| + }
|
| +
|
| + if (has_embedded_entries) {
|
| + // Add a "parent" entry that simply acts as a heading for all entries
|
| + // where |requesting_origin| has been embedded.
|
| + this_provider_exceptions.push_back(
|
| + GetChooserExceptionForPage(requesting_origin, requesting_origin,
|
| + source, std::string(), nullptr));
|
| +
|
| + // Add the "children" for any embedded settings.
|
| + for (const auto& one_origin_objects_entry : one_origin_objects) {
|
| + const GURL& embedding_origin = one_origin_objects_entry.first;
|
| + const SortedObjects& sorted_objects = one_origin_objects_entry.second;
|
| +
|
| + // Skip the non-embedded setting which we already added above.
|
| + if (requesting_origin == embedding_origin)
|
| + continue;
|
| +
|
| + for (const auto& sorted_objects_entry : sorted_objects) {
|
| + this_provider_exceptions.push_back(GetChooserExceptionForPage(
|
| + requesting_origin, embedding_origin, source,
|
| + sorted_objects_entry.first, sorted_objects_entry.second));
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + base::ListValue exceptions;
|
| + for (auto& one_provider_exceptions : all_provider_exceptions) {
|
| + for (auto& exception : one_provider_exceptions)
|
| + exceptions.Append(exception.Pass());
|
| + }
|
| +
|
| + base::StringValue type_string(chooser_type.name);
|
| + web_ui()->CallJavascriptFunction("ContentSettings.setExceptions", type_string,
|
| + exceptions);
|
| +}
|
| +
|
| void ContentSettingsHandler::AdjustZoomLevelsListForSigninPageIfNecessary(
|
| content::HostZoomMap::ZoomLevelVector* zoom_levels) {
|
| if (switches::IsEnableWebviewBasedSignin())
|
| @@ -1177,8 +1347,9 @@ void ContentSettingsHandler::UpdateExceptionsViewFromOTRHostContentSettingsMap(
|
| type_string, exceptions);
|
| }
|
|
|
| -scoped_ptr<base::ListValue> ContentSettingsHandler::GetPolicyAllowedUrls(
|
| - ContentSettingsType type) {
|
| +void ContentSettingsHandler::GetPolicyAllowedUrls(
|
| + ContentSettingsType type,
|
| + std::vector<scoped_ptr<base::DictionaryValue>>* exceptions) {
|
| DCHECK(type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC ||
|
| type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA);
|
|
|
| @@ -1208,16 +1379,11 @@ scoped_ptr<base::ListValue> ContentSettingsHandler::GetPolicyAllowedUrls(
|
| std::sort(
|
| patterns.begin(), patterns.end(), std::greater<ContentSettingsPattern>());
|
|
|
| - scoped_ptr<base::ListValue> exceptions(new base::ListValue());
|
| for (const ContentSettingsPattern& pattern : patterns) {
|
| - exceptions->Append(GetExceptionForPage(
|
| - pattern,
|
| - ContentSettingsPattern(),
|
| - CONTENT_SETTING_ALLOW,
|
| - kPolicyProviderId));
|
| + exceptions->push_back(GetExceptionForPage(pattern, ContentSettingsPattern(),
|
| + CONTENT_SETTING_ALLOW,
|
| + kPolicyProviderId));
|
| }
|
| -
|
| - return exceptions.Pass();
|
| }
|
|
|
| void ContentSettingsHandler::GetExceptionsFromHostContentSettingsMap(
|
| @@ -1249,10 +1415,8 @@ void ContentSettingsHandler::GetExceptionsFromHostContentSettingsMap(
|
|
|
| // Keep the exceptions sorted by provider so they will be displayed in
|
| // precedence order.
|
| - ScopedVector<base::ListValue> all_provider_exceptions;
|
| - all_provider_exceptions.resize(HostContentSettingsMap::NUM_PROVIDER_TYPES);
|
| - for (auto& one_provider_exceptions : all_provider_exceptions)
|
| - one_provider_exceptions = new base::ListValue();
|
| + std::vector<scoped_ptr<base::DictionaryValue>>
|
| + all_provider_exceptions[HostContentSettingsMap::NUM_PROVIDER_TYPES];
|
|
|
| // |all_patterns_settings| is sorted from the lowest precedence pattern to
|
| // the highest (see operator< in ContentSettingsPattern), so traverse it in
|
| @@ -1273,19 +1437,16 @@ void ContentSettingsHandler::GetExceptionsFromHostContentSettingsMap(
|
| parent = one_settings.find(ContentSettingsPattern::Wildcard());
|
|
|
| const std::string& source = i->first.second;
|
| - base::ListValue* this_provider_exceptions =
|
| - all_provider_exceptions[
|
| - HostContentSettingsMap::GetProviderTypeFromSource(source)];
|
| + auto& this_provider_exceptions = all_provider_exceptions
|
| + [HostContentSettingsMap::GetProviderTypeFromSource(source)];
|
|
|
| // Add the "parent" entry for the non-embedded setting.
|
| ContentSetting parent_setting =
|
| parent == one_settings.end() ? CONTENT_SETTING_DEFAULT : parent->second;
|
| const ContentSettingsPattern& secondary_pattern =
|
| parent == one_settings.end() ? primary_pattern : parent->first;
|
| - this_provider_exceptions->Append(GetExceptionForPage(primary_pattern,
|
| - secondary_pattern,
|
| - parent_setting,
|
| - source));
|
| + this_provider_exceptions.push_back(GetExceptionForPage(
|
| + primary_pattern, secondary_pattern, parent_setting, source));
|
|
|
| // Add the "children" for any embedded settings.
|
| for (OnePatternSettings::const_iterator j = one_settings.begin();
|
| @@ -1295,11 +1456,8 @@ void ContentSettingsHandler::GetExceptionsFromHostContentSettingsMap(
|
| continue;
|
|
|
| ContentSetting content_setting = j->second;
|
| - this_provider_exceptions->Append(GetExceptionForPage(
|
| - primary_pattern,
|
| - j->first,
|
| - content_setting,
|
| - source));
|
| + this_provider_exceptions.push_back(GetExceptionForPage(
|
| + primary_pattern, j->first, content_setting, source));
|
| }
|
| }
|
|
|
| @@ -1307,29 +1465,15 @@ void ContentSettingsHandler::GetExceptionsFromHostContentSettingsMap(
|
| // the policy-set allowed URLs, which should be displayed in the same manner.
|
| if (type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC ||
|
| type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA) {
|
| - base::ListValue* policy_exceptions = all_provider_exceptions[
|
| - HostContentSettingsMap::GetProviderTypeFromSource(kPolicyProviderId)];
|
| - DCHECK(policy_exceptions->empty());
|
| - policy_exceptions->Swap(GetPolicyAllowedUrls(type).get());
|
| + auto& policy_exceptions = all_provider_exceptions
|
| + [HostContentSettingsMap::GetProviderTypeFromSource(kPolicyProviderId)];
|
| + DCHECK(policy_exceptions.empty());
|
| + GetPolicyAllowedUrls(type, &policy_exceptions);
|
| }
|
|
|
| - for (const auto& one_provider_exceptions : all_provider_exceptions) {
|
| - // Append |one_provider_exceptions| at the end of |exceptions|. ListValue
|
| - // does not support concatenation, so we must append one item at a time.
|
| - // Furthermore, ListValue::Remove is O(size) if we remove an item from the
|
| - // beginning, so we need to remove them in the reverse order.
|
| - ScopedVector<base::Value> reverse_helper;
|
| - while (!one_provider_exceptions->empty()) {
|
| - scoped_ptr<base::Value> exception;
|
| - one_provider_exceptions->Remove(
|
| - one_provider_exceptions->GetSize() - 1, &exception);
|
| - reverse_helper.push_back(exception.Pass());
|
| - }
|
| - while (!reverse_helper.empty()) {
|
| - ScopedVector<base::Value>::iterator back = reverse_helper.end() - 1;
|
| - exceptions->Append(make_scoped_ptr(*back));
|
| - reverse_helper.weak_erase(back);
|
| - }
|
| + for (auto& one_provider_exceptions : all_provider_exceptions) {
|
| + for (auto& exception : one_provider_exceptions)
|
| + exceptions->Append(exception.Pass());
|
| }
|
| }
|
|
|
| @@ -1395,6 +1539,39 @@ void ContentSettingsHandler::RemoveZoomLevelException(
|
| host_zoom_map->SetZoomLevelForHost(pattern, default_level);
|
| }
|
|
|
| +void ContentSettingsHandler::RemoveChooserException(
|
| + const ChooserTypeNameEntry* chooser_type,
|
| + const base::ListValue* args) {
|
| + std::string mode;
|
| + bool rv = args->GetString(1, &mode);
|
| + DCHECK(rv);
|
| +
|
| + std::string requesting_origin_string;
|
| + rv = args->GetString(2, &requesting_origin_string);
|
| + DCHECK(rv);
|
| + GURL requesting_origin(requesting_origin_string);
|
| + DCHECK(requesting_origin.is_valid());
|
| +
|
| + std::string embedding_origin_string;
|
| + rv = args->GetString(3, &embedding_origin_string);
|
| + DCHECK(rv);
|
| + GURL embedding_origin(embedding_origin_string);
|
| + DCHECK(embedding_origin.is_valid());
|
| +
|
| + const base::DictionaryValue* object = nullptr;
|
| + rv = args->GetDictionary(4, &object);
|
| + DCHECK(rv);
|
| +
|
| + Profile* profile = Profile::FromWebUI(web_ui());
|
| + if (mode != "normal")
|
| + profile = profile->GetOffTheRecordProfile();
|
| +
|
| + ChooserContextBase* chooser_context = chooser_type->get_context(profile);
|
| + chooser_context->RevokeObjectPermission(requesting_origin, embedding_origin,
|
| + *object);
|
| + // TODO(reillyg): Create metrics for revocations. crbug.com/556845
|
| +}
|
| +
|
| void ContentSettingsHandler::RegisterMessages() {
|
| web_ui()->RegisterMessageCallback("setContentFilter",
|
| base::Bind(&ContentSettingsHandler::SetContentFilter,
|
| @@ -1459,6 +1636,13 @@ void ContentSettingsHandler::RemoveException(const base::ListValue* args) {
|
| return;
|
| }
|
|
|
| + const ChooserTypeNameEntry* chooser_type =
|
| + ChooserTypeFromGroupName(type_string);
|
| + if (chooser_type) {
|
| + RemoveChooserException(chooser_type, args);
|
| + return;
|
| + }
|
| +
|
| ContentSettingsType type = ContentSettingsTypeFromGroupName(type_string);
|
| RemoveExceptionFromHostContentSettingsMap(args, type);
|
|
|
|
|