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..f1c78c7b992d9c02ec620e42f799c418481de8fd 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 { |
|
Bernhard Bauer
2015/11/16 12:53:30
Move this into the anonymous namespace? I also wou
Reilly Grant (use Gerrit)
2015/11/16 22:18:27
It is necessary to define this type outside of the
Bernhard Bauer
2015/11/17 13:36:53
Oh, right! In that case, can you move it into the
Reilly Grant (use Gerrit)
2015/11/18 00:30:09
Done.
|
| + 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,114 @@ 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. |
| + ScopedVector<base::ListValue> all_provider_exceptions; |
|
Bernhard Bauer
2015/11/16 12:53:31
I think there is some code here that is shared wit
Reilly Grant (use Gerrit)
2015/11/16 22:18:27
I've rewritten the code in both places to avoid th
|
| + all_provider_exceptions.resize(HostContentSettingsMap::NUM_PROVIDER_TYPES); |
| + for (auto& one_provider_exceptions : all_provider_exceptions) |
| + one_provider_exceptions = new base::ListValue(); |
| + |
| + 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; |
| + |
| + base::ListValue* 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->Append(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->Append(GetChooserExceptionForPage( |
| + requesting_origin, requesting_origin, source, "", 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->Append(GetChooserExceptionForPage( |
| + requesting_origin, embedding_origin, source, |
| + sorted_objects_entry.first, sorted_objects_entry.second)); |
| + } |
| + } |
| + } |
| + } |
| + |
| + base::ListValue 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; |
|
Bernhard Bauer
2015/11/16 12:53:30
Also, this should use std::vector<scoped_ptr<>>. O
Reilly Grant (use Gerrit)
2015/11/16 22:18:27
Done.
|
| + 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); |
| + } |
| + } |
| + |
| + 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()) |
| @@ -1396,6 +1580,40 @@ 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; |
|
Bernhard Bauer
2015/11/16 12:53:30
Initialize this to null? Otherwise you might end u
Reilly Grant (use Gerrit)
2015/11/16 22:18:27
Done.
|
| + rv = args->GetDictionary(4, &object); |
| + DCHECK(rv); |
| + |
| + Profile* profile = Profile::FromWebUI(web_ui()); |
| + if (mode != "normal") { |
|
Bernhard Bauer
2015/11/16 12:53:30
Braces are unnecessary.
Reilly Grant (use Gerrit)
2015/11/16 22:18:27
Done.
|
| + 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 +1678,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); |