// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/ui/content_settings/content_setting_image_model.h" #include "base/macros.h" #include "build/build_config.h" #include "chrome/browser/content_settings/host_content_settings_map_factory.h" #include "chrome/browser/content_settings/tab_specific_content_settings.h" #include "chrome/browser/prerender/prerender_manager.h" #include "chrome/browser/profiles/profile.h" #include "chrome/grit/generated_resources.h" #include "components/content_settings/core/browser/host_content_settings_map.h" #include "content/public/browser/web_contents.h" #include "grit/theme_resources.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/material_design/material_design_controller.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/color_palette.h" #include "ui/gfx/color_utils.h" #include "ui/gfx/paint_vector_icon.h" #include "ui/gfx/vector_icons_public.h" using content::WebContents; namespace { bool UseVectorGraphics() { #if defined(OS_MACOSX) return false; #else return ui::MaterialDesignController::IsModeMaterial(); #endif } } // namespace // The image models hierarchy: // // ContentSettingImageModel - base class // ContentSettingSimpleImageModel - single content setting // ContentSettingBlockedImageModel - generic blocked setting // ContentSettingGeolocationImageModel - geolocation // ContentSettingRPHImageModel - protocol handlers // ContentSettingMIDISysExImageModel - midi sysex // ContentSettingMediaImageModel - media class ContentSettingBlockedImageModel : public ContentSettingSimpleImageModel { public: explicit ContentSettingBlockedImageModel(ContentSettingsType content_type); void UpdateFromWebContents(WebContents* web_contents) override; private: DISALLOW_COPY_AND_ASSIGN(ContentSettingBlockedImageModel); }; class ContentSettingGeolocationImageModel : public ContentSettingSimpleImageModel { public: ContentSettingGeolocationImageModel(); void UpdateFromWebContents(WebContents* web_contents) override; private: DISALLOW_COPY_AND_ASSIGN(ContentSettingGeolocationImageModel); }; // Image model for displaying media icons in the location bar. class ContentSettingMediaImageModel : public ContentSettingImageModel { public: ContentSettingMediaImageModel(); void UpdateFromWebContents(WebContents* web_contents) override; ContentSettingBubbleModel* CreateBubbleModel( ContentSettingBubbleModel::Delegate* delegate, WebContents* web_contents, Profile* profile) override; bool ShouldRunAnimation(WebContents* web_contents) override; void SetAnimationHasRun(WebContents* web_contents) override; private: DISALLOW_COPY_AND_ASSIGN(ContentSettingMediaImageModel); }; class ContentSettingRPHImageModel : public ContentSettingSimpleImageModel { public: ContentSettingRPHImageModel(); void UpdateFromWebContents(WebContents* web_contents) override; }; class ContentSettingMIDISysExImageModel : public ContentSettingSimpleImageModel { public: ContentSettingMIDISysExImageModel(); void UpdateFromWebContents(WebContents* web_contents) override; private: DISALLOW_COPY_AND_ASSIGN(ContentSettingMIDISysExImageModel); }; namespace { struct ContentSettingsImageDetails { ContentSettingsType type; int blocked_icon_id; gfx::VectorIconId vector_icon_id; int blocked_tooltip_id; int blocked_explanatory_text_id; int accessed_icon_id; int accessed_tooltip_id; }; static const ContentSettingsImageDetails kImageDetails[] = { {CONTENT_SETTINGS_TYPE_COOKIES, IDR_BLOCKED_COOKIES, gfx::VectorIconId::COOKIE, IDS_BLOCKED_COOKIES_TITLE, 0, IDR_ACCESSED_COOKIES, IDS_ACCESSED_COOKIES_TITLE}, {CONTENT_SETTINGS_TYPE_IMAGES, IDR_BLOCKED_IMAGES, gfx::VectorIconId::IMAGE, IDS_BLOCKED_IMAGES_TITLE, 0, 0, 0}, {CONTENT_SETTINGS_TYPE_JAVASCRIPT, IDR_BLOCKED_JAVASCRIPT, gfx::VectorIconId::CODE, IDS_BLOCKED_JAVASCRIPT_TITLE, 0, 0, 0}, {CONTENT_SETTINGS_TYPE_PLUGINS, IDR_BLOCKED_PLUGINS, gfx::VectorIconId::EXTENSION, IDS_BLOCKED_PLUGINS_MESSAGE, IDS_BLOCKED_PLUGIN_EXPLANATORY_TEXT, 0, 0}, {CONTENT_SETTINGS_TYPE_POPUPS, IDR_BLOCKED_POPUPS, gfx::VectorIconId::WEB, IDS_BLOCKED_POPUPS_TOOLTIP, IDS_BLOCKED_POPUPS_EXPLANATORY_TEXT, 0, 0}, {CONTENT_SETTINGS_TYPE_MIXEDSCRIPT, IDR_BLOCKED_MIXED_CONTENT, gfx::VectorIconId::MIXED_CONTENT, IDS_BLOCKED_DISPLAYING_INSECURE_CONTENT, 0, 0, 0}, {CONTENT_SETTINGS_TYPE_PPAPI_BROKER, IDR_BLOCKED_PPAPI_BROKER, gfx::VectorIconId::EXTENSION, IDS_BLOCKED_PPAPI_BROKER_TITLE, 0, IDR_BLOCKED_PPAPI_BROKER, IDS_ALLOWED_PPAPI_BROKER_TITLE}, {CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS, IDR_BLOCKED_DOWNLOADS, gfx::VectorIconId::FILE_DOWNLOAD, IDS_BLOCKED_DOWNLOAD_TITLE, IDS_BLOCKED_DOWNLOADS_EXPLANATION, IDR_ALLOWED_DOWNLOADS, IDS_ALLOWED_DOWNLOAD_TITLE}, }; const ContentSettingsImageDetails* GetImageDetails(ContentSettingsType type) { for (const ContentSettingsImageDetails& image_details : kImageDetails) { if (image_details.type == type) return &image_details; } return nullptr; } } // namespace // Single content setting ------------------------------------------------------ ContentSettingSimpleImageModel::ContentSettingSimpleImageModel( ContentSettingsType content_type) : ContentSettingImageModel(), content_type_(content_type) { } ContentSettingBubbleModel* ContentSettingSimpleImageModel::CreateBubbleModel( ContentSettingBubbleModel::Delegate* delegate, WebContents* web_contents, Profile* profile) { return ContentSettingBubbleModel::CreateContentSettingBubbleModel( delegate, web_contents, profile, content_type()); } bool ContentSettingSimpleImageModel::ShouldRunAnimation( WebContents* web_contents) { if (!web_contents) return false; TabSpecificContentSettings* content_settings = TabSpecificContentSettings::FromWebContents(web_contents); if (!content_settings) return false; return !content_settings->IsBlockageIndicated(content_type()); } void ContentSettingSimpleImageModel::SetAnimationHasRun( WebContents* web_contents) { if (!web_contents) return; TabSpecificContentSettings* content_settings = TabSpecificContentSettings::FromWebContents(web_contents); if (content_settings) content_settings->SetBlockageHasBeenIndicated(content_type()); } // static scoped_ptr ContentSettingSimpleImageModel::CreateForContentTypeForTesting( ContentSettingsType content_settings_type) { if (content_settings_type == CONTENT_SETTINGS_TYPE_GEOLOCATION) return make_scoped_ptr(new ContentSettingGeolocationImageModel()); if (content_settings_type == CONTENT_SETTINGS_TYPE_PROTOCOL_HANDLERS) return make_scoped_ptr(new ContentSettingRPHImageModel()); if (content_settings_type == CONTENT_SETTINGS_TYPE_MIDI_SYSEX) return make_scoped_ptr(new ContentSettingMIDISysExImageModel()); return make_scoped_ptr( new ContentSettingBlockedImageModel(content_settings_type)); } // Generic blocked content settings -------------------------------------------- ContentSettingBlockedImageModel::ContentSettingBlockedImageModel( ContentSettingsType content_type) : ContentSettingSimpleImageModel(content_type) { } void ContentSettingBlockedImageModel::UpdateFromWebContents( WebContents* web_contents) { set_visible(false); if (!web_contents) return; const ContentSettingsType type = content_type(); const ContentSettingsImageDetails* image_details = GetImageDetails(type); DCHECK(image_details) << "No entry for " << type << " in kImageDetails[]."; int icon_id = image_details->blocked_icon_id; int tooltip_id = image_details->blocked_tooltip_id; int explanation_id = image_details->blocked_explanatory_text_id; // For plugins, don't show the animated explanation unless the plugin was // blocked despite the user's content settings being set to allow it (e.g. // due to auto-blocking NPAPI plugins). Profile* profile = Profile::FromBrowserContext(web_contents->GetBrowserContext()); HostContentSettingsMap* map = HostContentSettingsMapFactory::GetForProfile(profile); if (type == CONTENT_SETTINGS_TYPE_PLUGINS) { GURL url = web_contents->GetURL(); if (map->GetContentSetting(url, url, type, std::string()) != CONTENT_SETTING_ALLOW) explanation_id = 0; } // If a content type is blocked by default and was accessed, display the // content blocked page action. TabSpecificContentSettings* content_settings = TabSpecificContentSettings::FromWebContents(web_contents); if (!content_settings) return; if (!content_settings->IsContentBlocked(type)) { if (!content_settings->IsContentAllowed(type)) return; // For cookies, only show the cookie blocked page action if cookies are // blocked by default. if (type == CONTENT_SETTINGS_TYPE_COOKIES && (map->GetDefaultContentSetting(type, NULL) != CONTENT_SETTING_BLOCK)) return; icon_id = image_details->accessed_icon_id; tooltip_id = image_details->accessed_tooltip_id; explanation_id = 0; } set_visible(true); if (!UseVectorGraphics()) { DCHECK(icon_id); SetIconByResourceId(icon_id); } else { gfx::VectorIconId badge_id = gfx::VectorIconId::VECTOR_ICON_NONE; if (type == CONTENT_SETTINGS_TYPE_PPAPI_BROKER) badge_id = gfx::VectorIconId::WARNING_BADGE; else if (content_settings->IsContentBlocked(type)) badge_id = gfx::VectorIconId::BLOCKED_BADGE; set_icon_by_vector_id(image_details->vector_icon_id, badge_id); } set_explanatory_string_id(explanation_id); DCHECK(tooltip_id); set_tooltip(l10n_util::GetStringUTF16(tooltip_id)); } // Geolocation ----------------------------------------------------------------- ContentSettingGeolocationImageModel::ContentSettingGeolocationImageModel() : ContentSettingSimpleImageModel(CONTENT_SETTINGS_TYPE_GEOLOCATION) { } void ContentSettingGeolocationImageModel::UpdateFromWebContents( WebContents* web_contents) { set_visible(false); if (!web_contents) return; TabSpecificContentSettings* content_settings = TabSpecificContentSettings::FromWebContents(web_contents); if (!content_settings) return; const ContentSettingsUsagesState& usages_state = content_settings-> geolocation_usages_state(); if (usages_state.state_map().empty()) return; set_visible(true); // If any embedded site has access the allowed icon takes priority over the // blocked icon. unsigned int state_flags = 0; usages_state.GetDetailedInfo(NULL, &state_flags); bool allowed = !!(state_flags & ContentSettingsUsagesState::TABSTATE_HAS_ANY_ALLOWED); if (!UseVectorGraphics()) { SetIconByResourceId(allowed ? IDR_ALLOWED_LOCATION : IDR_BLOCKED_LOCATION); } else { set_icon_by_vector_id(gfx::VectorIconId::MY_LOCATION, allowed ? gfx::VectorIconId::VECTOR_ICON_NONE : gfx::VectorIconId::BLOCKED_BADGE); } set_tooltip(l10n_util::GetStringUTF16(allowed ? IDS_GEOLOCATION_ALLOWED_TOOLTIP : IDS_GEOLOCATION_BLOCKED_TOOLTIP)); } // Media ----------------------------------------------------------------------- ContentSettingMediaImageModel::ContentSettingMediaImageModel() : ContentSettingImageModel() { } void ContentSettingMediaImageModel::UpdateFromWebContents( WebContents* web_contents) { set_visible(false); if (!web_contents) return; TabSpecificContentSettings* content_settings = TabSpecificContentSettings::FromWebContents(web_contents); if (!content_settings) return; TabSpecificContentSettings::MicrophoneCameraState state = content_settings->GetMicrophoneCameraState(); // If neither the microphone nor the camera stream was accessed then no icon // is displayed in the omnibox. if (state == TabSpecificContentSettings::MICROPHONE_CAMERA_NOT_ACCESSED) return; bool is_mic = (state & TabSpecificContentSettings::MICROPHONE_ACCESSED) != 0; bool is_cam = (state & TabSpecificContentSettings::CAMERA_ACCESSED) != 0; DCHECK(is_mic || is_cam); int id = IDS_CAMERA_BLOCKED; if (state & (TabSpecificContentSettings::MICROPHONE_BLOCKED | TabSpecificContentSettings::CAMERA_BLOCKED)) { if (!UseVectorGraphics()) { SetIconByResourceId(IDR_BLOCKED_CAMERA); } else { set_icon_by_vector_id(gfx::VectorIconId::VIDEOCAM, gfx::VectorIconId::BLOCKED_BADGE); } if (is_mic) id = is_cam ? IDS_MICROPHONE_CAMERA_BLOCKED : IDS_MICROPHONE_BLOCKED; } else { if (!UseVectorGraphics()) { SetIconByResourceId(IDR_ALLOWED_CAMERA); } else { set_icon_by_vector_id(gfx::VectorIconId::VIDEOCAM, gfx::VectorIconId::VECTOR_ICON_NONE); } id = IDS_CAMERA_ACCESSED; if (is_mic) id = is_cam ? IDS_MICROPHONE_CAMERA_ALLOWED : IDS_MICROPHONE_ACCESSED; } set_tooltip(l10n_util::GetStringUTF16(id)); set_visible(true); } ContentSettingBubbleModel* ContentSettingMediaImageModel::CreateBubbleModel( ContentSettingBubbleModel::Delegate* delegate, WebContents* web_contents, Profile* profile) { return new ContentSettingMediaStreamBubbleModel(delegate, web_contents, profile); } bool ContentSettingMediaImageModel::ShouldRunAnimation( WebContents* web_contents) { // TODO(msramek): For bubbles that are not tied to a single content setting, // TabSpecificContentSettings is not a good place to store this bit. // Perhaps we should instead use a map here in // ContentSettingMediaImageModel. if (!web_contents) return false; TabSpecificContentSettings* content_settings = TabSpecificContentSettings::FromWebContents(web_contents); if (!content_settings) return false; return (!content_settings->IsBlockageIndicated( CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC) && !content_settings->IsBlockageIndicated( CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA)); } void ContentSettingMediaImageModel::SetAnimationHasRun( WebContents* web_contents) { if (!web_contents) return; TabSpecificContentSettings* content_settings = TabSpecificContentSettings::FromWebContents(web_contents); if (content_settings) { content_settings->SetBlockageHasBeenIndicated( CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC); content_settings->SetBlockageHasBeenIndicated( CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA); } } // Protocol handlers ----------------------------------------------------------- ContentSettingRPHImageModel::ContentSettingRPHImageModel() : ContentSettingSimpleImageModel(CONTENT_SETTINGS_TYPE_PROTOCOL_HANDLERS) { if (!UseVectorGraphics()) { SetIconByResourceId(IDR_REGISTER_PROTOCOL_HANDLER); } else { set_icon_by_vector_id(gfx::VectorIconId::PROTOCOL_HANDLER, gfx::VectorIconId::VECTOR_ICON_NONE); } set_tooltip(l10n_util::GetStringUTF16(IDS_REGISTER_PROTOCOL_HANDLER_TOOLTIP)); } void ContentSettingRPHImageModel::UpdateFromWebContents( WebContents* web_contents) { set_visible(false); if (!web_contents) return; TabSpecificContentSettings* content_settings = TabSpecificContentSettings::FromWebContents(web_contents); if (!content_settings) return; if (content_settings->pending_protocol_handler().IsEmpty()) return; set_visible(true); } // MIDI SysEx ------------------------------------------------------------------ ContentSettingMIDISysExImageModel::ContentSettingMIDISysExImageModel() : ContentSettingSimpleImageModel(CONTENT_SETTINGS_TYPE_MIDI_SYSEX) { } void ContentSettingMIDISysExImageModel::UpdateFromWebContents( WebContents* web_contents) { set_visible(false); if (!web_contents) return; TabSpecificContentSettings* content_settings = TabSpecificContentSettings::FromWebContents(web_contents); if (!content_settings) return; const ContentSettingsUsagesState& usages_state = content_settings->midi_usages_state(); if (usages_state.state_map().empty()) return; set_visible(true); // If any embedded site has access the allowed icon takes priority over the // blocked icon. unsigned int state_flags = 0; usages_state.GetDetailedInfo(NULL, &state_flags); bool allowed = !!(state_flags & ContentSettingsUsagesState::TABSTATE_HAS_ANY_ALLOWED); if (!UseVectorGraphics()) { SetIconByResourceId(allowed ? IDR_ALLOWED_MIDI_SYSEX : IDR_BLOCKED_MIDI_SYSEX); } else { set_icon_by_vector_id(gfx::VectorIconId::MIDI, allowed ? gfx::VectorIconId::VECTOR_ICON_NONE : gfx::VectorIconId::BLOCKED_BADGE); } set_tooltip(l10n_util::GetStringUTF16(allowed ? IDS_MIDI_SYSEX_ALLOWED_TOOLTIP : IDS_MIDI_SYSEX_BLOCKED_TOOLTIP)); } // Base class ------------------------------------------------------------------ gfx::Image ContentSettingImageModel::GetIcon(SkColor nearby_text_color) const { return UseVectorGraphics() ? gfx::Image(gfx::CreateVectorIconWithBadge( vector_icon_id_, 16, color_utils::DeriveDefaultIconColor(nearby_text_color), vector_icon_badge_id_)) : raster_icon_; } ContentSettingImageModel::ContentSettingImageModel() : is_visible_(false), raster_icon_id_(0), vector_icon_id_(gfx::VectorIconId::VECTOR_ICON_NONE), vector_icon_badge_id_(gfx::VectorIconId::VECTOR_ICON_NONE), explanatory_string_id_(0) {} // static ScopedVector ContentSettingImageModel::GenerateContentSettingImageModels() { ScopedVector result; // The ordering of the models here influences the order in which icons are // shown in the omnibox. result.push_back( new ContentSettingBlockedImageModel(CONTENT_SETTINGS_TYPE_COOKIES)); result.push_back( new ContentSettingBlockedImageModel(CONTENT_SETTINGS_TYPE_IMAGES)); result.push_back( new ContentSettingBlockedImageModel(CONTENT_SETTINGS_TYPE_JAVASCRIPT)); result.push_back( new ContentSettingBlockedImageModel(CONTENT_SETTINGS_TYPE_PPAPI_BROKER)); result.push_back( new ContentSettingBlockedImageModel(CONTENT_SETTINGS_TYPE_PLUGINS)); result.push_back( new ContentSettingBlockedImageModel(CONTENT_SETTINGS_TYPE_POPUPS)); result.push_back(new ContentSettingGeolocationImageModel()); result.push_back( new ContentSettingBlockedImageModel(CONTENT_SETTINGS_TYPE_MIXEDSCRIPT)); result.push_back(new ContentSettingRPHImageModel()); result.push_back(new ContentSettingMediaImageModel()); result.push_back( new ContentSettingBlockedImageModel( CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS)); result.push_back(new ContentSettingMIDISysExImageModel()); return result; } void ContentSettingImageModel::SetIconByResourceId(int id) { raster_icon_id_ = id; raster_icon_ = ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed(id); }