blob: e20b7a062039cbfd5dc2f5a885f7b985f463ad82 [file] [log] [blame]
// 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/content_settings/tab_specific_content_settings.h"
#include <list>
#include <vector>
#include "base/command_line.h"
#include "base/lazy_instance.h"
#include "base/stl_util.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "chrome/browser/browsing_data/browsing_data_appcache_helper.h"
#include "chrome/browser/browsing_data/browsing_data_cookie_helper.h"
#include "chrome/browser/browsing_data/browsing_data_database_helper.h"
#include "chrome/browser/browsing_data/browsing_data_file_system_helper.h"
#include "chrome/browser/browsing_data/browsing_data_indexed_db_helper.h"
#include "chrome/browser/browsing_data/browsing_data_local_storage_helper.h"
#include "chrome/browser/browsing_data/cookies_tree_model.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/content_settings/chrome_content_settings_utils.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
#include "chrome/browser/media/webrtc/media_stream_capture_indicator.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/content_settings_renderer.mojom.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/render_messages.h"
#include "chrome/common/renderer_configuration.mojom.h"
#include "components/content_settings/core/browser/content_settings_details.h"
#include "components/content_settings/core/browser/content_settings_info.h"
#include "components/content_settings/core/browser/content_settings_registry.h"
#include "components/content_settings/core/browser/content_settings_utils.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_details.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/notification_registrar.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_delegate.h"
#include "content/public/common/content_constants.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "net/cookies/canonical_cookie.h"
#include "storage/common/fileapi/file_system_types.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
#include "url/origin.h"
using content::BrowserThread;
using content::NavigationController;
using content::NavigationEntry;
using content::WebContents;
namespace {
static TabSpecificContentSettings* GetForWCGetter(
const base::Callback<content::WebContents*(void)>& wc_getter) {
WebContents* web_contents = wc_getter.Run();
if (!web_contents)
return nullptr;
return TabSpecificContentSettings::FromWebContents(web_contents);
}
bool ShouldSendUpdatedContentSettingsRulesToRenderer(
ContentSettingsType content_type) {
// CONTENT_SETTINGS_TYPE_DEFAULT signals that multiple content settings may
// have been updated, e.g. by the PolicyProvider. This should always be sent
// to the renderer in case a relevant setting is updated.
if (content_type == CONTENT_SETTINGS_TYPE_DEFAULT)
return true;
return RendererContentSettingRules::IsRendererContentSetting((content_type));
}
} // namespace
TabSpecificContentSettings::SiteDataObserver::SiteDataObserver(
TabSpecificContentSettings* tab_specific_content_settings)
: tab_specific_content_settings_(tab_specific_content_settings) {
tab_specific_content_settings_->AddSiteDataObserver(this);
}
TabSpecificContentSettings::SiteDataObserver::~SiteDataObserver() {
if (tab_specific_content_settings_)
tab_specific_content_settings_->RemoveSiteDataObserver(this);
}
void TabSpecificContentSettings::SiteDataObserver::ContentSettingsDestroyed() {
tab_specific_content_settings_ = NULL;
}
TabSpecificContentSettings::TabSpecificContentSettings(WebContents* tab)
: content::WebContentsObserver(tab),
map_(HostContentSettingsMapFactory::GetForProfile(
Profile::FromBrowserContext(tab->GetBrowserContext()))),
allowed_local_shared_objects_(
Profile::FromBrowserContext(tab->GetBrowserContext())),
blocked_local_shared_objects_(
Profile::FromBrowserContext(tab->GetBrowserContext())),
geolocation_usages_state_(map_, CONTENT_SETTINGS_TYPE_GEOLOCATION),
midi_usages_state_(map_, CONTENT_SETTINGS_TYPE_MIDI_SYSEX),
pending_protocol_handler_(ProtocolHandler::EmptyProtocolHandler()),
previous_protocol_handler_(ProtocolHandler::EmptyProtocolHandler()),
pending_protocol_handler_setting_(CONTENT_SETTING_DEFAULT),
load_plugins_link_enabled_(true),
microphone_camera_state_(MICROPHONE_CAMERA_NOT_ACCESSED),
observer_(this) {
ClearContentSettingsExceptForNavigationRelatedSettings();
ClearNavigationRelatedContentSettings();
observer_.Add(map_);
}
TabSpecificContentSettings::~TabSpecificContentSettings() {
for (SiteDataObserver& observer : observer_list_)
observer.ContentSettingsDestroyed();
}
TabSpecificContentSettings* TabSpecificContentSettings::GetForFrame(
int render_process_id,
int render_frame_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
content::RenderFrameHost* frame = content::RenderFrameHost::FromID(
render_process_id, render_frame_id);
WebContents* web_contents = WebContents::FromRenderFrameHost(frame);
if (!web_contents)
return nullptr;
return TabSpecificContentSettings::FromWebContents(web_contents);
}
// static
void TabSpecificContentSettings::CookiesRead(
const base::Callback<content::WebContents*(void)>& wc_getter,
const GURL& url,
const GURL& frame_url,
const net::CookieList& cookie_list,
bool blocked_by_policy) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
TabSpecificContentSettings* settings = GetForWCGetter(wc_getter);
if (settings) {
settings->OnCookiesRead(url, frame_url, cookie_list, blocked_by_policy);
}
}
// static
void TabSpecificContentSettings::CookieChanged(
const base::Callback<WebContents*(void)>& wc_getter,
const GURL& url,
const GURL& frame_url,
const net::CanonicalCookie& cookie,
bool blocked_by_policy) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
TabSpecificContentSettings* settings = GetForWCGetter(wc_getter);
if (settings)
settings->OnCookieChange(url, frame_url, cookie, blocked_by_policy);
}
// static
void TabSpecificContentSettings::WebDatabaseAccessed(
int render_process_id,
int render_frame_id,
const GURL& url,
const base::string16& name,
const base::string16& display_name,
bool blocked_by_policy) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
TabSpecificContentSettings* settings = GetForFrame(
render_process_id, render_frame_id);
if (settings)
settings->OnWebDatabaseAccessed(url, name, display_name, blocked_by_policy);
}
// static
void TabSpecificContentSettings::DOMStorageAccessed(int render_process_id,
int render_frame_id,
const GURL& url,
bool local,
bool blocked_by_policy) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
TabSpecificContentSettings* settings = GetForFrame(
render_process_id, render_frame_id);
if (settings)
settings->OnLocalStorageAccessed(url, local, blocked_by_policy);
}
// static
void TabSpecificContentSettings::IndexedDBAccessed(
int render_process_id,
int render_frame_id,
const GURL& url,
bool blocked_by_policy) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
TabSpecificContentSettings* settings = GetForFrame(
render_process_id, render_frame_id);
if (settings)
settings->OnIndexedDBAccessed(url, blocked_by_policy);
}
// static
void TabSpecificContentSettings::FileSystemAccessed(int render_process_id,
int render_frame_id,
const GURL& url,
bool blocked_by_policy) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
TabSpecificContentSettings* settings = GetForFrame(
render_process_id, render_frame_id);
if (settings)
settings->OnFileSystemAccessed(url, blocked_by_policy);
}
// static
void TabSpecificContentSettings::ServiceWorkerAccessed(
const base::Callback<content::WebContents*(void)>& wc_getter,
const GURL& scope,
bool blocked_by_policy_javascript,
bool blocked_by_policy_cookie) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
TabSpecificContentSettings* settings = GetForWCGetter(wc_getter);
if (settings)
settings->OnServiceWorkerAccessed(scope, blocked_by_policy_javascript,
blocked_by_policy_cookie);
}
// static
void TabSpecificContentSettings::SharedWorkerAccessed(
int render_process_id,
int render_frame_id,
const GURL& worker_url,
const std::string& name,
const url::Origin& constructor_origin,
bool blocked_by_policy) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
TabSpecificContentSettings* settings =
GetForFrame(render_process_id, render_frame_id);
if (settings)
settings->OnSharedWorkerAccessed(worker_url, name, constructor_origin,
blocked_by_policy);
}
bool TabSpecificContentSettings::IsContentBlocked(
ContentSettingsType content_type) const {
DCHECK_NE(CONTENT_SETTINGS_TYPE_GEOLOCATION, content_type)
<< "Geolocation settings handled by ContentSettingGeolocationImageModel";
DCHECK_NE(CONTENT_SETTINGS_TYPE_NOTIFICATIONS, content_type)
<< "Notifications settings handled by "
<< "ContentSettingsNotificationsImageModel";
DCHECK_NE(CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS, content_type)
<< "Automatic downloads handled by DownloadRequestLimiter";
if (content_type == CONTENT_SETTINGS_TYPE_IMAGES ||
content_type == CONTENT_SETTINGS_TYPE_JAVASCRIPT ||
content_type == CONTENT_SETTINGS_TYPE_PLUGINS ||
content_type == CONTENT_SETTINGS_TYPE_COOKIES ||
content_type == CONTENT_SETTINGS_TYPE_POPUPS ||
content_type == CONTENT_SETTINGS_TYPE_MIXEDSCRIPT ||
content_type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC ||
content_type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA ||
content_type == CONTENT_SETTINGS_TYPE_PPAPI_BROKER ||
content_type == CONTENT_SETTINGS_TYPE_MIDI_SYSEX ||
content_type == CONTENT_SETTINGS_TYPE_ADS ||
content_type == CONTENT_SETTINGS_TYPE_SOUND ||
content_type == CONTENT_SETTINGS_TYPE_CLIPBOARD_READ ||
content_type == CONTENT_SETTINGS_TYPE_SENSORS) {
const auto& it = content_settings_status_.find(content_type);
if (it != content_settings_status_.end())
return it->second.blocked;
}
return false;
}
bool TabSpecificContentSettings::IsContentAllowed(
ContentSettingsType content_type) const {
DCHECK_NE(CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS, content_type)
<< "Automatic downloads handled by DownloadRequestLimiter";
// This method currently only returns meaningful values for the content type
// cookies, media, PPAPI broker, downloads, MIDI sysex, clipboard, and
// sensors.
if (content_type != CONTENT_SETTINGS_TYPE_COOKIES &&
content_type != CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC &&
content_type != CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA &&
content_type != CONTENT_SETTINGS_TYPE_PPAPI_BROKER &&
content_type != CONTENT_SETTINGS_TYPE_MIDI_SYSEX &&
content_type != CONTENT_SETTINGS_TYPE_CLIPBOARD_READ &&
content_type != CONTENT_SETTINGS_TYPE_SENSORS) {
return false;
}
const auto& it = content_settings_status_.find(content_type);
if (it != content_settings_status_.end())
return it->second.allowed;
return false;
}
void TabSpecificContentSettings::OnContentBlocked(ContentSettingsType type) {
OnContentBlockedWithDetail(type, base::string16());
}
void TabSpecificContentSettings::OnContentBlockedWithDetail(
ContentSettingsType type,
const base::string16& details) {
DCHECK(type != CONTENT_SETTINGS_TYPE_GEOLOCATION)
<< "Geolocation settings handled by OnGeolocationPermissionSet";
DCHECK(type != CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC &&
type != CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA)
<< "Media stream settings handled by OnMediaStreamPermissionSet";
if (!content_settings::ContentSettingsRegistry::GetInstance()->Get(type))
return;
ContentSettingsStatus& status = content_settings_status_[type];
if (!status.blocked) {
status.blocked = true;
// TODO: it would be nice to have a way of mocking this in tests.
content::NotificationService::current()->Notify(
chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED,
content::Source<WebContents>(web_contents()),
content::NotificationService::NoDetails());
if (type == CONTENT_SETTINGS_TYPE_MIXEDSCRIPT) {
content_settings::RecordMixedScriptAction(
content_settings::MIXED_SCRIPT_ACTION_DISPLAYED_SHIELD);
} else if (type == CONTENT_SETTINGS_TYPE_PLUGINS) {
content_settings::RecordPluginsAction(
content_settings::PLUGINS_ACTION_DISPLAYED_BLOCKED_ICON_IN_OMNIBOX);
} else if (type == CONTENT_SETTINGS_TYPE_POPUPS) {
content_settings::RecordPopupsAction(
content_settings::POPUPS_ACTION_DISPLAYED_BLOCKED_ICON_IN_OMNIBOX);
}
}
}
void TabSpecificContentSettings::OnContentAllowed(ContentSettingsType type) {
DCHECK(type != CONTENT_SETTINGS_TYPE_GEOLOCATION)
<< "Geolocation settings handled by OnGeolocationPermissionSet";
DCHECK(type != CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC &&
type != CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA)
<< "Media stream settings handled by OnMediaStreamPermissionSet";
bool access_changed = false;
ContentSettingsStatus& status = content_settings_status_[type];
#if defined(OS_ANDROID)
if (type == CONTENT_SETTINGS_TYPE_PROTECTED_MEDIA_IDENTIFIER &&
status.blocked) {
// content_settings_status_[type].allowed is always set to true in
// OnContentBlocked, so we have to use
// content_settings_status_[type].blocked to detect whether the protected
// media setting has changed.
status.blocked = false;
access_changed = true;
}
#endif
if (!status.allowed) {
status.allowed = true;
access_changed = true;
}
if (access_changed) {
content::NotificationService::current()->Notify(
chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED,
content::Source<WebContents>(web_contents()),
content::NotificationService::NoDetails());
}
}
void TabSpecificContentSettings::OnCookiesRead(
const GURL& url,
const GURL& frame_url,
const net::CookieList& cookie_list,
bool blocked_by_policy) {
if (cookie_list.empty())
return;
if (blocked_by_policy) {
blocked_local_shared_objects_.cookies()->AddReadCookies(
frame_url, url, cookie_list);
OnContentBlocked(CONTENT_SETTINGS_TYPE_COOKIES);
} else {
allowed_local_shared_objects_.cookies()->AddReadCookies(
frame_url, url, cookie_list);
OnContentAllowed(CONTENT_SETTINGS_TYPE_COOKIES);
}
NotifySiteDataObservers();
}
void TabSpecificContentSettings::OnCookieChange(
const GURL& url,
const GURL& frame_url,
const net::CanonicalCookie& cookie,
bool blocked_by_policy) {
if (blocked_by_policy) {
blocked_local_shared_objects_.cookies()->AddChangedCookie(frame_url, url,
cookie);
OnContentBlocked(CONTENT_SETTINGS_TYPE_COOKIES);
} else {
allowed_local_shared_objects_.cookies()->AddChangedCookie(frame_url, url,
cookie);
OnContentAllowed(CONTENT_SETTINGS_TYPE_COOKIES);
}
NotifySiteDataObservers();
}
void TabSpecificContentSettings::OnIndexedDBAccessed(const GURL& url,
bool blocked_by_policy) {
if (blocked_by_policy) {
blocked_local_shared_objects_.indexed_dbs()->AddIndexedDB(url);
OnContentBlocked(CONTENT_SETTINGS_TYPE_COOKIES);
} else {
allowed_local_shared_objects_.indexed_dbs()->AddIndexedDB(url);
OnContentAllowed(CONTENT_SETTINGS_TYPE_COOKIES);
}
NotifySiteDataObservers();
}
void TabSpecificContentSettings::OnLocalStorageAccessed(
const GURL& url,
bool local,
bool blocked_by_policy) {
LocalSharedObjectsContainer& container = blocked_by_policy ?
blocked_local_shared_objects_ : allowed_local_shared_objects_;
CannedBrowsingDataLocalStorageHelper* helper =
local ? container.local_storages() : container.session_storages();
helper->AddLocalStorage(url);
if (blocked_by_policy)
OnContentBlocked(CONTENT_SETTINGS_TYPE_COOKIES);
else
OnContentAllowed(CONTENT_SETTINGS_TYPE_COOKIES);
NotifySiteDataObservers();
}
void TabSpecificContentSettings::OnServiceWorkerAccessed(
const GURL& scope,
bool blocked_by_policy_javascript,
bool blocked_by_policy_cookie) {
DCHECK(scope.is_valid());
if (blocked_by_policy_javascript || blocked_by_policy_cookie) {
blocked_local_shared_objects_.service_workers()->AddServiceWorker(
scope.GetOrigin(), std::vector<GURL>(1, scope));
} else {
allowed_local_shared_objects_.service_workers()->AddServiceWorker(
scope.GetOrigin(), std::vector<GURL>(1, scope));
}
if (blocked_by_policy_javascript) {
OnContentBlocked(CONTENT_SETTINGS_TYPE_JAVASCRIPT);
} else {
OnContentAllowed(CONTENT_SETTINGS_TYPE_JAVASCRIPT);
}
if (blocked_by_policy_cookie) {
OnContentBlocked(CONTENT_SETTINGS_TYPE_COOKIES);
} else {
OnContentAllowed(CONTENT_SETTINGS_TYPE_COOKIES);
}
}
void TabSpecificContentSettings::OnSharedWorkerAccessed(
const GURL& worker_url,
const std::string& name,
const url::Origin& constructor_origin,
bool blocked_by_policy) {
DCHECK(worker_url.is_valid());
if (blocked_by_policy) {
blocked_local_shared_objects_.shared_workers()->AddSharedWorker(
worker_url, name, constructor_origin);
OnContentBlocked(CONTENT_SETTINGS_TYPE_COOKIES);
} else {
allowed_local_shared_objects_.shared_workers()->AddSharedWorker(
worker_url, name, constructor_origin);
OnContentAllowed(CONTENT_SETTINGS_TYPE_COOKIES);
}
}
void TabSpecificContentSettings::OnWebDatabaseAccessed(
const GURL& url,
const base::string16& name,
const base::string16& display_name,
bool blocked_by_policy) {
if (blocked_by_policy) {
blocked_local_shared_objects_.databases()->AddDatabase(
url, base::UTF16ToUTF8(name), base::UTF16ToUTF8(display_name));
OnContentBlocked(CONTENT_SETTINGS_TYPE_COOKIES);
} else {
allowed_local_shared_objects_.databases()->AddDatabase(
url, base::UTF16ToUTF8(name), base::UTF16ToUTF8(display_name));
OnContentAllowed(CONTENT_SETTINGS_TYPE_COOKIES);
}
NotifySiteDataObservers();
}
void TabSpecificContentSettings::OnFileSystemAccessed(
const GURL& url,
bool blocked_by_policy) {
if (blocked_by_policy) {
blocked_local_shared_objects_.file_systems()->AddFileSystem(
url::Origin::Create(url), storage::kFileSystemTypeTemporary, 0);
OnContentBlocked(CONTENT_SETTINGS_TYPE_COOKIES);
} else {
allowed_local_shared_objects_.file_systems()->AddFileSystem(
url::Origin::Create(url), storage::kFileSystemTypeTemporary, 0);
OnContentAllowed(CONTENT_SETTINGS_TYPE_COOKIES);
}
NotifySiteDataObservers();
}
void TabSpecificContentSettings::OnGeolocationPermissionSet(
const GURL& requesting_origin,
bool allowed) {
geolocation_usages_state_.OnPermissionSet(requesting_origin, allowed);
content::NotificationService::current()->Notify(
chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED,
content::Source<WebContents>(web_contents()),
content::NotificationService::NoDetails());
}
#if defined(OS_ANDROID) || defined(OS_CHROMEOS)
void TabSpecificContentSettings::OnProtectedMediaIdentifierPermissionSet(
const GURL& requesting_origin,
bool allowed) {
if (allowed) {
OnContentAllowed(CONTENT_SETTINGS_TYPE_PROTECTED_MEDIA_IDENTIFIER);
} else {
OnContentBlocked(CONTENT_SETTINGS_TYPE_PROTECTED_MEDIA_IDENTIFIER);
}
}
#endif
TabSpecificContentSettings::MicrophoneCameraState
TabSpecificContentSettings::GetMicrophoneCameraState() const {
MicrophoneCameraState state = microphone_camera_state_;
// Include capture devices in the state if there are still consumers of the
// approved media stream.
scoped_refptr<MediaStreamCaptureIndicator> media_indicator =
MediaCaptureDevicesDispatcher::GetInstance()->
GetMediaStreamCaptureIndicator();
if (media_indicator->IsCapturingAudio(web_contents()))
state |= MICROPHONE_ACCESSED;
if (media_indicator->IsCapturingVideo(web_contents()))
state |= CAMERA_ACCESSED;
return state;
}
bool TabSpecificContentSettings::IsMicrophoneCameraStateChanged() const {
if ((microphone_camera_state_ & MICROPHONE_ACCESSED) &&
((microphone_camera_state_& MICROPHONE_BLOCKED) ?
!IsContentBlocked(CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC) :
!IsContentAllowed(CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC)))
return true;
if ((microphone_camera_state_ & CAMERA_ACCESSED) &&
((microphone_camera_state_ & CAMERA_BLOCKED) ?
!IsContentBlocked(CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA) :
!IsContentAllowed(CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA)))
return true;
PrefService* prefs =
Profile::FromBrowserContext(web_contents()->GetBrowserContext())->
GetPrefs();
scoped_refptr<MediaStreamCaptureIndicator> media_indicator =
MediaCaptureDevicesDispatcher::GetInstance()->
GetMediaStreamCaptureIndicator();
if ((microphone_camera_state_ & MICROPHONE_ACCESSED) &&
prefs->GetString(prefs::kDefaultAudioCaptureDevice) !=
media_stream_selected_audio_device() &&
media_indicator->IsCapturingAudio(web_contents()))
return true;
if ((microphone_camera_state_ & CAMERA_ACCESSED) &&
prefs->GetString(prefs::kDefaultVideoCaptureDevice) !=
media_stream_selected_video_device() &&
media_indicator->IsCapturingVideo(web_contents()))
return true;
return false;
}
void TabSpecificContentSettings::OnMediaStreamPermissionSet(
const GURL& request_origin,
MicrophoneCameraState new_microphone_camera_state,
const std::string& media_stream_selected_audio_device,
const std::string& media_stream_selected_video_device,
const std::string& media_stream_requested_audio_device,
const std::string& media_stream_requested_video_device) {
media_stream_access_origin_ = request_origin;
if (new_microphone_camera_state & MICROPHONE_ACCESSED) {
media_stream_requested_audio_device_ = media_stream_requested_audio_device;
media_stream_selected_audio_device_ = media_stream_selected_audio_device;
bool mic_blocked = (new_microphone_camera_state & MICROPHONE_BLOCKED) != 0;
ContentSettingsStatus& status =
content_settings_status_[CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC];
status.allowed = !mic_blocked;
status.blocked = mic_blocked;
}
if (new_microphone_camera_state & CAMERA_ACCESSED) {
media_stream_requested_video_device_ = media_stream_requested_video_device;
media_stream_selected_video_device_ = media_stream_selected_video_device;
bool cam_blocked = (new_microphone_camera_state & CAMERA_BLOCKED) != 0;
ContentSettingsStatus& status =
content_settings_status_[CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA];
status.allowed = !cam_blocked;
status.blocked = cam_blocked;
}
if (microphone_camera_state_ != new_microphone_camera_state) {
microphone_camera_state_ = new_microphone_camera_state;
content::NotificationService::current()->Notify(
chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED,
content::Source<WebContents>(web_contents()),
content::NotificationService::NoDetails());
}
}
void TabSpecificContentSettings::OnMidiSysExAccessed(
const GURL& requesting_origin) {
midi_usages_state_.OnPermissionSet(requesting_origin, true);
OnContentAllowed(CONTENT_SETTINGS_TYPE_MIDI_SYSEX);
}
void TabSpecificContentSettings::OnMidiSysExAccessBlocked(
const GURL& requesting_origin) {
midi_usages_state_.OnPermissionSet(requesting_origin, false);
OnContentBlocked(CONTENT_SETTINGS_TYPE_MIDI_SYSEX);
}
void TabSpecificContentSettings::
ClearContentSettingsExceptForNavigationRelatedSettings() {
for (auto& status : content_settings_status_) {
if (status.first == CONTENT_SETTINGS_TYPE_COOKIES ||
status.first == CONTENT_SETTINGS_TYPE_JAVASCRIPT)
continue;
status.second.blocked = false;
status.second.allowed = false;
}
microphone_camera_state_ = MICROPHONE_CAMERA_NOT_ACCESSED;
load_plugins_link_enabled_ = true;
content::NotificationService::current()->Notify(
chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED,
content::Source<WebContents>(web_contents()),
content::NotificationService::NoDetails());
}
void TabSpecificContentSettings::ClearNavigationRelatedContentSettings() {
blocked_local_shared_objects_.Reset();
allowed_local_shared_objects_.Reset();
for (ContentSettingsType type :
{CONTENT_SETTINGS_TYPE_COOKIES, CONTENT_SETTINGS_TYPE_JAVASCRIPT}) {
ContentSettingsStatus& status =
content_settings_status_[type];
status.blocked = false;
status.allowed = false;
}
content::NotificationService::current()->Notify(
chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED,
content::Source<WebContents>(web_contents()),
content::NotificationService::NoDetails());
}
void TabSpecificContentSettings::FlashDownloadBlocked() {
OnContentBlockedWithDetail(CONTENT_SETTINGS_TYPE_PLUGINS,
base::UTF8ToUTF16(content::kFlashPluginName));
}
void TabSpecificContentSettings::ClearPopupsBlocked() {
ContentSettingsStatus& status =
content_settings_status_[CONTENT_SETTINGS_TYPE_POPUPS];
status.blocked = false;
content::NotificationService::current()->Notify(
chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED,
content::Source<WebContents>(web_contents()),
content::NotificationService::NoDetails());
}
void TabSpecificContentSettings::OnAudioBlocked() {
OnContentBlocked(CONTENT_SETTINGS_TYPE_SOUND);
}
void TabSpecificContentSettings::SetPepperBrokerAllowed(bool allowed) {
if (allowed) {
OnContentAllowed(CONTENT_SETTINGS_TYPE_PPAPI_BROKER);
} else {
OnContentBlocked(CONTENT_SETTINGS_TYPE_PPAPI_BROKER);
}
}
void TabSpecificContentSettings::OnContentSettingChanged(
const ContentSettingsPattern& primary_pattern,
const ContentSettingsPattern& secondary_pattern,
ContentSettingsType content_type,
const std::string& resource_identifier) {
const ContentSettingsDetails details(
primary_pattern, secondary_pattern, content_type, resource_identifier);
if (!details.update_all() &&
// The visible URL is the URL in the URL field of a tab.
// Currently this should be matched by the |primary_pattern|.
!details.primary_pattern().Matches(web_contents()->GetVisibleURL())) {
return;
}
if (content_type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC ||
content_type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA) {
const GURL media_origin = media_stream_access_origin();
ContentSetting setting = map_->GetContentSetting(
media_origin, media_origin, content_type, std::string());
ContentSettingsStatus& status = content_settings_status_[content_type];
status.allowed = setting == CONTENT_SETTING_ALLOW;
status.blocked = setting == CONTENT_SETTING_BLOCK;
}
if (!ShouldSendUpdatedContentSettingsRulesToRenderer(content_type))
return;
MaybeSendRendererContentSettingsRules(web_contents());
}
void TabSpecificContentSettings::MaybeSendRendererContentSettingsRules(
content::WebContents* web_contents) {
// Only send a message to the renderer if it is initialised and not dead.
// Otherwise, the IPC messages will be queued in the browser process,
// potentially causing large memory leaks. See https://crbug.com/875937.
content::RenderProcessHost* process =
web_contents->GetMainFrame()->GetProcess();
if (!process->IsInitializedAndNotDead())
return;
// |channel| may be null in tests.
IPC::ChannelProxy* channel = process->GetChannel();
if (!channel)
return;
RendererContentSettingRules rules;
GetRendererContentSettingRules(map_, &rules);
chrome::mojom::RendererConfigurationAssociatedPtr rc_interface;
channel->GetRemoteAssociatedInterface(&rc_interface);
rc_interface->SetContentSettingRules(rules);
}
void TabSpecificContentSettings::RenderFrameForInterstitialPageCreated(
content::RenderFrameHost* render_frame_host) {
// We want to tell the renderer-side code to ignore content settings for this
// page.
chrome::mojom::ContentSettingsRendererAssociatedPtr content_settings_renderer;
render_frame_host->GetRemoteAssociatedInterfaces()->GetInterface(
&content_settings_renderer);
content_settings_renderer->SetAsInterstitial();
}
bool TabSpecificContentSettings::OnMessageReceived(
const IPC::Message& message,
content::RenderFrameHost* render_frame_host) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(TabSpecificContentSettings, message)
IPC_MESSAGE_HANDLER(ChromeViewHostMsg_ContentBlocked,
OnContentBlockedWithDetail)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
void TabSpecificContentSettings::DidStartNavigation(
content::NavigationHandle* navigation_handle) {
if (!navigation_handle->IsInMainFrame() ||
navigation_handle->IsSameDocument()) {
return;
}
// If we're displaying a network error page do not reset the content
// settings delegate's cookies so the user has a chance to modify cookie
// settings.
if (!navigation_handle->IsErrorPage())
ClearNavigationRelatedContentSettings();
ClearGeolocationContentSettings();
ClearMidiContentSettings();
ClearPendingProtocolHandler();
ClearContentSettingsChangedViaPageInfo();
}
void TabSpecificContentSettings::ReadyToCommitNavigation(
content::NavigationHandle* navigation_handle) {
if (!navigation_handle->IsInMainFrame() ||
navigation_handle->IsSameDocument()) {
return;
}
// There may be content settings that were updated for the navigated URL.
// These would not have been sent before if we're navigating cross-origin.
// Ensure up to date rules are sent before navigation commits.
MaybeSendRendererContentSettingsRules(navigation_handle->GetWebContents());
}
void TabSpecificContentSettings::DidFinishNavigation(
content::NavigationHandle* navigation_handle) {
if (!navigation_handle->IsInMainFrame() ||
!navigation_handle->HasCommitted() ||
navigation_handle->IsSameDocument()) {
return;
}
// Clear "blocked" flags.
ClearContentSettingsExceptForNavigationRelatedSettings();
GeolocationDidNavigate(navigation_handle);
MidiDidNavigate(navigation_handle);
if (web_contents()->GetVisibleURL().SchemeIsHTTPOrHTTPS()) {
content_settings::RecordPluginsAction(
content_settings::PLUGINS_ACTION_TOTAL_NAVIGATIONS);
}
}
void TabSpecificContentSettings::AppCacheAccessed(const GURL& manifest_url,
bool blocked_by_policy) {
if (blocked_by_policy) {
blocked_local_shared_objects_.appcaches()->AddAppCache(manifest_url);
OnContentBlocked(CONTENT_SETTINGS_TYPE_COOKIES);
} else {
allowed_local_shared_objects_.appcaches()->AddAppCache(manifest_url);
OnContentAllowed(CONTENT_SETTINGS_TYPE_COOKIES);
}
}
void TabSpecificContentSettings::AddSiteDataObserver(
SiteDataObserver* observer) {
observer_list_.AddObserver(observer);
}
void TabSpecificContentSettings::RemoveSiteDataObserver(
SiteDataObserver* observer) {
observer_list_.RemoveObserver(observer);
}
void TabSpecificContentSettings::NotifySiteDataObservers() {
for (SiteDataObserver& observer : observer_list_)
observer.OnSiteDataAccessed();
}
void TabSpecificContentSettings::ClearGeolocationContentSettings() {
geolocation_usages_state_.ClearStateMap();
}
void TabSpecificContentSettings::ClearMidiContentSettings() {
midi_usages_state_.ClearStateMap();
}
void TabSpecificContentSettings::ClearContentSettingsChangedViaPageInfo() {
content_settings_changed_via_page_info_.clear();
}
void TabSpecificContentSettings::GeolocationDidNavigate(
content::NavigationHandle* navigation_handle) {
geolocation_usages_state_.DidNavigate(navigation_handle->GetURL(),
navigation_handle->GetPreviousURL());
}
void TabSpecificContentSettings::MidiDidNavigate(
content::NavigationHandle* navigation_handle) {
midi_usages_state_.DidNavigate(navigation_handle->GetURL(),
navigation_handle->GetPreviousURL());
}
void TabSpecificContentSettings::BlockAllContentForTesting() {
content_settings::ContentSettingsRegistry* registry =
content_settings::ContentSettingsRegistry::GetInstance();
for (const content_settings::ContentSettingsInfo* info : *registry) {
ContentSettingsType type = info->website_settings_info()->type();
if (type != CONTENT_SETTINGS_TYPE_GEOLOCATION &&
type != CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC &&
type != CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA) {
OnContentBlocked(type);
}
}
// Geolocation and media must be blocked separately, as the generic
// TabSpecificContentSettings::OnContentBlocked does not apply to them.
OnGeolocationPermissionSet(web_contents()->GetLastCommittedURL(), false);
MicrophoneCameraStateFlags media_blocked =
static_cast<MicrophoneCameraStateFlags>(
TabSpecificContentSettings::MICROPHONE_ACCESSED |
TabSpecificContentSettings::MICROPHONE_BLOCKED |
TabSpecificContentSettings::CAMERA_ACCESSED |
TabSpecificContentSettings::CAMERA_BLOCKED);
OnMediaStreamPermissionSet(
web_contents()->GetLastCommittedURL(),
media_blocked,
std::string(), std::string(), std::string(), std::string());
}
void TabSpecificContentSettings::ContentSettingChangedViaPageInfo(
ContentSettingsType type) {
content_settings_changed_via_page_info_.insert(type);
}
bool TabSpecificContentSettings::HasContentSettingChangedViaPageInfo(
ContentSettingsType type) const {
return content_settings_changed_via_page_info_.find(type) !=
content_settings_changed_via_page_info_.end();
}
WEB_CONTENTS_USER_DATA_KEY_IMPL(TabSpecificContentSettings)