Chromium Code Reviews| 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 10ce23ade6398fb732c9c6706db094e7d62b3d22..4a70eb44ec0f4d6c49432b41018f0214b05b98c0 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/pref_names.h" |
| @@ -69,6 +72,13 @@ using content_settings::ContentSettingToString; |
| using content_settings::ContentSettingFromString; |
| using extensions::APIPermission; |
| +struct ChooserTypeNameEntry { |
| + ContentSettingsType type; |
| + ChooserContextBase* (*get_context)(Profile*); |
| + const char* name; |
| + const char* ui_name_key; |
| +}; |
| + |
| namespace { |
| struct ContentSettingWithExceptions { |
| @@ -92,6 +102,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, |
| @@ -109,6 +128,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"}, |
| @@ -134,6 +155,15 @@ const ContentSettingsTypeNameEntry kContentSettingsTypeGroupNames[] = { |
| #endif |
| }; |
| +ChooserContextBase* GetUsbChooserContext(Profile* profile) { |
| + return UsbChooserContextFactory::GetForProfile(profile); |
| +} |
| + |
| +const 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"; |
| @@ -247,6 +277,14 @@ ContentSettingsType ContentSettingsTypeFromGroupName(const std::string& name) { |
| return CONTENT_SETTINGS_TYPE_DEFAULT; |
| } |
| +const ChooserTypeNameEntry* ChooserTypeFromGroupName(const std::string& name) { |
| + for (const ChooserTypeNameEntry& 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( |
| @@ -312,6 +350,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> |
| @@ -439,6 +502,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. |
| @@ -550,6 +614,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}, |
| }; |
| @@ -604,6 +670,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); |
| @@ -699,6 +767,7 @@ void ContentSettingsHandler::InitializePage() { |
| UpdateHandlersEnabledRadios(); |
| UpdateAllExceptionsViewsFromModel(); |
| + UpdateAllChooserExceptionsViewsFromModel(); |
| UpdateProtectedContentExceptionsButton(); |
| } |
| @@ -712,9 +781,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; |
| + } |
| + } |
| } |
| } |
| @@ -1032,6 +1108,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<ChosenObjectPattern>> objects = |
| + chooser_context->GetAllGrantedObjects(); |
| + AllOriginObjects all_origin_objects; |
| + for (const scoped_ptr<ChosenObjectPattern>& 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. |
|
Bernhard Bauer
2015/11/17 13:36:53
Nit: Move this to the previous line.
Reilly Grant (use Gerrit)
2015/11/18 00:30:09
Done.
|
| + this_provider_exceptions.push_back(GetChooserExceptionForPage( |
| + requesting_origin, requesting_origin, source, "", nullptr)); |
|
Bernhard Bauer
2015/11/17 13:36:53
Use the empty std::string() constructor to save so
Reilly Grant (use Gerrit)
2015/11/18 00:30:09
Done.
|
| + |
| + // 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()) |
| @@ -1176,8 +1344,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); |
| @@ -1207,16 +1376,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( |
| @@ -1248,10 +1412,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 |
| @@ -1272,19 +1434,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(); |
| @@ -1294,11 +1453,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)); |
| } |
| } |
| @@ -1306,29 +1462,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()); |
| } |
| } |
| @@ -1396,6 +1538,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): Record this in UMA or something. |
| +} |
| + |
| void ContentSettingsHandler::RegisterMessages() { |
| web_ui()->RegisterMessageCallback("setContentFilter", |
| base::Bind(&ContentSettingsHandler::SetContentFilter, |
| @@ -1460,6 +1635,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); |