diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-10-12 14:27:29 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-10-13 09:35:20 +0000 |
commit | c30a6232df03e1efbd9f3b226777b07e087a1122 (patch) | |
tree | e992f45784689f373bcc38d1b79a239ebe17ee23 /chromium/components/content_settings/browser | |
parent | 7b5b123ac58f58ffde0f4f6e488bcd09aa4decd3 (diff) | |
download | qtwebengine-chromium-85-based.tar.gz |
BASELINE: Update Chromium to 85.0.4183.14085-based
Change-Id: Iaa42f4680837c57725b1344f108c0196741f6057
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/components/content_settings/browser')
12 files changed, 1097 insertions, 515 deletions
diff --git a/chromium/components/content_settings/browser/BUILD.gn b/chromium/components/content_settings/browser/BUILD.gn index 8377ccc4031..a4168a97483 100644 --- a/chromium/components/content_settings/browser/BUILD.gn +++ b/chromium/components/content_settings/browser/BUILD.gn @@ -26,11 +26,24 @@ source_set("browser") { ] } +source_set("test_support") { + testonly = true + sources = [ + "test_tab_specific_content_settings_delegate.cc", + "test_tab_specific_content_settings_delegate.h", + ] + deps = [ + ":browser", + "//content/public/browser", + ] +} + source_set("unit_tests") { testonly = true sources = [ "tab_specific_content_settings_unittest.cc" ] deps = [ ":browser", + ":test_support", "//base", "//components/content_settings/core/browser", "//components/security_state/core", diff --git a/chromium/components/content_settings/browser/content_settings_usages_state.cc b/chromium/components/content_settings/browser/content_settings_usages_state.cc index b65036db080..63e75f1af13 100644 --- a/chromium/components/content_settings/browser/content_settings_usages_state.cc +++ b/chromium/components/content_settings/browser/content_settings_usages_state.cc @@ -11,9 +11,10 @@ #include "components/url_formatter/url_formatter.h" ContentSettingsUsagesState::ContentSettingsUsagesState( - HostContentSettingsMap* host_content_settings_map, - ContentSettingsType type) - : host_content_settings_map_(host_content_settings_map), type_(type) {} + content_settings::TabSpecificContentSettings::Delegate* delegate_, + ContentSettingsType type, + const GURL& embedder_url) + : delegate_(delegate_), type_(type), embedder_url_(embedder_url) {} ContentSettingsUsagesState::~ContentSettingsUsagesState() {} @@ -23,64 +24,67 @@ void ContentSettingsUsagesState::OnPermissionSet(const GURL& requesting_origin, allowed ? CONTENT_SETTING_ALLOW : CONTENT_SETTING_BLOCK; } -void ContentSettingsUsagesState::DidNavigate(const GURL& url, - const GURL& previous_url) { - embedder_url_ = url; - if (state_map_.empty()) - return; - if (previous_url.GetOrigin() != url.GetOrigin()) { - state_map_.clear(); - return; - } - // We're in the same origin, check if there's any icon to be displayed. - unsigned int tab_state_flags = 0; - GetDetailedInfo(nullptr, &tab_state_flags); - if (!(tab_state_flags & TABSTATE_HAS_ANY_ICON)) - state_map_.clear(); -} - -void ContentSettingsUsagesState::ClearStateMap() { - state_map_.clear(); -} - void ContentSettingsUsagesState::GetDetailedInfo( FormattedHostsPerState* formatted_hosts_per_state, unsigned int* tab_state_flags) const { DCHECK(tab_state_flags); DCHECK(embedder_url_.is_valid()); + // This logic is used only for |ContentSettingsType::GEOLOCATION| and + // |ContentSettingsType::MIDI_SYSEX|. + DCHECK(type_ == ContentSettingsType::GEOLOCATION || + type_ == ContentSettingsType::MIDI_SYSEX); ContentSetting default_setting = - host_content_settings_map_->GetDefaultContentSetting(type_, nullptr); + delegate_->GetSettingsMap()->GetDefaultContentSetting(type_, nullptr); std::set<std::string> formatted_hosts; std::set<std::string> repeated_formatted_hosts; - // Build a set of repeated formatted hosts - for (auto i(state_map_.begin()); i != state_map_.end(); ++i) { - std::string formatted_host = GURLToFormattedHost(i->first); + // Build a set of repeated formatted hosts. + for (const auto& i : state_map_) { + std::string formatted_host = GURLToFormattedHost(i.first); if (!formatted_hosts.insert(formatted_host).second) { repeated_formatted_hosts.insert(formatted_host); } } - for (auto i(state_map_.begin()); i != state_map_.end(); ++i) { - if (i->second == CONTENT_SETTING_ALLOW) + for (const auto& i : state_map_) { + const GURL& origin = i.first; + // The setting that was applied when the corresponding capability was last + // requested. + const ContentSetting& effective_setting = i.second; + + if (effective_setting == CONTENT_SETTING_ALLOW) *tab_state_flags |= TABSTATE_HAS_ANY_ALLOWED; if (formatted_hosts_per_state) { - std::string formatted_host = GURLToFormattedHost(i->first); + std::string formatted_host = GURLToFormattedHost(origin); std::string final_formatted_host = repeated_formatted_hosts.find(formatted_host) == repeated_formatted_hosts.end() ? formatted_host - : i->first.spec(); - (*formatted_hosts_per_state)[i->second].insert(final_formatted_host); + : origin.spec(); + (*formatted_hosts_per_state)[effective_setting].insert( + final_formatted_host); } - const ContentSetting saved_setting = - host_content_settings_map_->GetContentSetting(i->first, embedder_url_, - type_, std::string()); + ContentSetting saved_setting = + delegate_->GetSettingsMap()->GetContentSetting(origin, embedder_url_, + type_, std::string()); + ContentSetting embargo_setting = + delegate_->GetEmbargoSetting(origin, type_); + + // |embargo_setting| can be only CONTENT_SETTING_ASK or + // CONTENT_SETTING_BLOCK. If |saved_setting| is CONTENT_SETTING_ASK then + // the |embargo_setting| takes effect. + if (saved_setting == CONTENT_SETTING_ASK) + saved_setting = embargo_setting; + + // |effective_setting| can be only CONTENT_SETTING_ALLOW or + // CONTENT_SETTING_BLOCK. + if (saved_setting != effective_setting) + *tab_state_flags |= TABSTATE_HAS_CHANGED; + if (saved_setting != default_setting) *tab_state_flags |= TABSTATE_HAS_EXCEPTION; - if (saved_setting != i->second) - *tab_state_flags |= TABSTATE_HAS_CHANGED; + if (saved_setting != CONTENT_SETTING_ASK) *tab_state_flags |= TABSTATE_HAS_ANY_ICON; } diff --git a/chromium/components/content_settings/browser/content_settings_usages_state.h b/chromium/components/content_settings/browser/content_settings_usages_state.h index a34ed720de4..2e68ca9875c 100644 --- a/chromium/components/content_settings/browser/content_settings_usages_state.h +++ b/chromium/components/content_settings/browser/content_settings_usages_state.h @@ -9,19 +9,23 @@ #include <set> #include "base/macros.h" +#include "components/content_settings/browser/tab_specific_content_settings.h" #include "components/content_settings/core/common/content_settings.h" #include "components/content_settings/core/common/content_settings_types.h" #include "url/gurl.h" -class HostContentSettingsMap; - -// This class manages a content setting state per tab for a given +// This class manages a content setting state per page for a given // |ContentSettingsType|, and provides information and presentation data about // the content setting usage. +// TODO(crbug.com/1086363): Move this class into the `content_settings` +// namespace. class ContentSettingsUsagesState { public: - ContentSettingsUsagesState(HostContentSettingsMap* host_content_settings_map, - ContentSettingsType type); + ContentSettingsUsagesState( + content_settings::TabSpecificContentSettings::Delegate* delegate_, + ContentSettingsType type, + const GURL& embedder_url); + ~ContentSettingsUsagesState(); typedef std::map<GURL, ContentSetting> StateMap; @@ -30,12 +34,6 @@ class ContentSettingsUsagesState { // Sets the state for |requesting_origin|. void OnPermissionSet(const GURL& requesting_origin, bool allowed); - // Delegated by WebContents to indicate a navigation has happened and we - // may need to clear our settings. - void DidNavigate(const GURL& url, const GURL& previous_url); - - void ClearStateMap(); - enum TabState { TABSTATE_NONE = 0, // There's at least one entry with non-default setting. @@ -57,8 +55,7 @@ class ContentSettingsUsagesState { private: std::string GURLToFormattedHost(const GURL& url) const; - - HostContentSettingsMap* const host_content_settings_map_; + content_settings::TabSpecificContentSettings::Delegate* delegate_; ContentSettingsType type_; StateMap state_map_; GURL embedder_url_; diff --git a/chromium/components/content_settings/browser/tab_specific_content_settings.cc b/chromium/components/content_settings/browser/tab_specific_content_settings.cc index b783a2c6fde..95ab944de51 100644 --- a/chromium/components/content_settings/browser/tab_specific_content_settings.cc +++ b/chromium/components/content_settings/browser/tab_specific_content_settings.cc @@ -9,6 +9,8 @@ #include "base/command_line.h" #include "base/lazy_instance.h" +#include "base/memory/ptr_util.h" +#include "base/optional.h" #include "base/stl_util.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" @@ -22,17 +24,14 @@ #include "components/browsing_data/content/local_storage_helper.h" #include "components/browsing_data/content/service_worker_helper.h" #include "components/browsing_data/content/shared_worker_helper.h" +#include "components/content_settings/browser/content_settings_usages_state.h" #include "components/content_settings/common/content_settings_agent.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/prefs/pref_service.h" -#include "components/security_state/core/security_state_pref_names.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/render_frame_host.h" #include "content/public/browser/render_process_host.h" @@ -44,9 +43,6 @@ #include "url/origin.h" using content::BrowserThread; -using content::NavigationController; -using content::NavigationEntry; -using content::WebContents; namespace content_settings { namespace { @@ -62,60 +58,325 @@ bool ShouldSendUpdatedContentSettingsRulesToRenderer( return RendererContentSettingRules::IsRendererContentSetting((content_type)); } +void MaybeSendRendererContentSettingsRules( + content::RenderFrameHost* rfh, + const HostContentSettingsMap* map, + TabSpecificContentSettings::Delegate* delegate) { + DCHECK_EQ(rfh, rfh->GetMainFrame()); + // 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 = rfh->GetProcess(); + if (!process->IsInitializedAndNotDead()) + return; + + RendererContentSettingRules rules; + GetRendererContentSettingRules(map, &rules); + delegate->SetContentSettingRules(process, rules); +} + +bool WillNavigationCreateNewTabSpecificContentSettingsOnCommit( + content::NavigationHandle* navigation_handle) { + return navigation_handle->IsInMainFrame() && + !navigation_handle->IsSameDocument() && + !navigation_handle->IsServedFromBackForwardCache(); +} + } // namespace TabSpecificContentSettings::SiteDataObserver::SiteDataObserver( - TabSpecificContentSettings* tab_specific_content_settings) - : tab_specific_content_settings_(tab_specific_content_settings) { - tab_specific_content_settings_->AddSiteDataObserver(this); + content::WebContents* web_contents) + : web_contents_(web_contents) { + // Make sure the handler was attached to the WebContents as some UT might skip + // this. + auto* handler = + TabSpecificContentSettings::WebContentsHandler::FromWebContents( + web_contents_); + if (handler) + handler->AddSiteDataObserver(this); } TabSpecificContentSettings::SiteDataObserver::~SiteDataObserver() { - if (tab_specific_content_settings_) - tab_specific_content_settings_->RemoveSiteDataObserver(this); + if (!web_contents_) + return; + auto* handler = + TabSpecificContentSettings::WebContentsHandler::FromWebContents( + web_contents_); + if (handler) + handler->RemoveSiteDataObserver(this); } -void TabSpecificContentSettings::SiteDataObserver::ContentSettingsDestroyed() { - tab_specific_content_settings_ = nullptr; +void TabSpecificContentSettings::SiteDataObserver::WebContentsDestroyed() { + web_contents_ = nullptr; } -TabSpecificContentSettings::TabSpecificContentSettings( - WebContents* tab, +// static +void TabSpecificContentSettings::WebContentsHandler::CreateForWebContents( + content::WebContents* web_contents, + std::unique_ptr<Delegate> delegate) { + DCHECK(web_contents); + if (TabSpecificContentSettings::WebContentsHandler::FromWebContents( + web_contents)) { + return; + } + + web_contents->SetUserData( + TabSpecificContentSettings::WebContentsHandler::UserDataKey(), + base::WrapUnique(new TabSpecificContentSettings::WebContentsHandler( + web_contents, std::move(delegate)))); +} + +TabSpecificContentSettings::WebContentsHandler::WebContentsHandler( + content::WebContents* web_contents, std::unique_ptr<Delegate> delegate) - : content::WebContentsObserver(tab), + : WebContentsObserver(web_contents), delegate_(std::move(delegate)), + map_(delegate_->GetSettingsMap()) { + DCHECK(!TabSpecificContentSettings::GetForCurrentDocument( + web_contents->GetMainFrame())); + content::SetRenderDocumentHostUserData( + web_contents->GetMainFrame(), TabSpecificContentSettings::UserDataKey(), + base::WrapUnique(new TabSpecificContentSettings(*this, delegate_.get()))); +} + +TabSpecificContentSettings::WebContentsHandler::~WebContentsHandler() { + for (SiteDataObserver& observer : observer_list_) + observer.WebContentsDestroyed(); +} + +void TabSpecificContentSettings::WebContentsHandler:: + TransferNavigationContentSettingsToCommittedDocument( + const InflightNavigationContentSettings& navigation_settings, + content::RenderFrameHost* rfh) { + for (const auto& cookie_access : navigation_settings.cookie_accesses) { + OnCookiesAccessed(rfh, cookie_access); + } + for (const auto& service_worker_access : + navigation_settings.service_worker_accesses) { + OnServiceWorkerAccessed(rfh, service_worker_access.first, + service_worker_access.second); + } +} + +void TabSpecificContentSettings::WebContentsHandler::OnCookiesAccessed( + content::NavigationHandle* navigation, + const content::CookieAccessDetails& details) { + auto it = inflight_navigation_settings_.find(navigation); + if (it != inflight_navigation_settings_.end()) { + it->second.cookie_accesses.push_back(details); + return; + } + // TODO(carlscab): We should be able to + // DHECK(!WillNavigationCreateNewTabSpecificContentSettingsOnCommit) here, but + // there is still code that starts a navigation before attaching the tab + // helpers in DevConsole related code. So we miss the DidStartNavigation event + // for those navigations. (https://crbug.com/1095576) + OnCookiesAccessed(web_contents()->GetMainFrame(), details); +} + +void TabSpecificContentSettings::WebContentsHandler::OnCookiesAccessed( + content::RenderFrameHost* rfh, + const content::CookieAccessDetails& details) { + auto* tscs = + TabSpecificContentSettings::GetForCurrentDocument(rfh->GetMainFrame()); + if (tscs) + tscs->OnCookiesAccessed(details); +} + +void TabSpecificContentSettings::WebContentsHandler::OnServiceWorkerAccessed( + content::NavigationHandle* navigation, + const GURL& scope, + content::AllowServiceWorkerResult allowed) { + DCHECK(scope.is_valid()); + + auto it = inflight_navigation_settings_.find(navigation); + if (it != inflight_navigation_settings_.end()) { + it->second.service_worker_accesses.emplace_back( + std::make_pair(scope, allowed)); + return; + } + // TODO(carlscab): We should be able to + // DHECK(!WillNavigationCreateNewTabSpecificContentSettingsOnCommit) here, but + // there is still code that starts a navigation before attaching the tab + // helpers in DevConsole related code. So we miss the DidStartNavigation event + // for those navigations. + OnServiceWorkerAccessed(web_contents()->GetMainFrame(), scope, allowed); +} + +void TabSpecificContentSettings::WebContentsHandler::OnServiceWorkerAccessed( + content::RenderFrameHost* frame, + const GURL& scope, + content::AllowServiceWorkerResult allowed) { + auto* tscs = + TabSpecificContentSettings::GetForCurrentDocument(frame->GetMainFrame()); + if (tscs) + tscs->OnServiceWorkerAccessed(scope, allowed); +} + +void TabSpecificContentSettings::WebContentsHandler:: + RenderFrameForInterstitialPageCreated( + content::RenderFrameHost* render_frame_host) { + // We want to tell the renderer-side code to ignore content settings for this + // page. + mojo::AssociatedRemote<content_settings::mojom::ContentSettingsAgent> + content_settings_agent; + render_frame_host->GetRemoteAssociatedInterfaces()->GetInterface( + &content_settings_agent); + content_settings_agent->SetAsInterstitial(); +} + +void TabSpecificContentSettings::WebContentsHandler::DidStartNavigation( + content::NavigationHandle* navigation_handle) { + if (!WillNavigationCreateNewTabSpecificContentSettingsOnCommit( + navigation_handle)) { + return; + } + + inflight_navigation_settings_.insert( + std::make_pair(navigation_handle, InflightNavigationContentSettings())); +} + +void TabSpecificContentSettings::WebContentsHandler::ReadyToCommitNavigation( + content::NavigationHandle* navigation_handle) { + if (!WillNavigationCreateNewTabSpecificContentSettingsOnCommit( + navigation_handle)) { + 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()->GetMainFrame(), map_, + delegate_.get()); +} + +void TabSpecificContentSettings::WebContentsHandler::DidFinishNavigation( + content::NavigationHandle* navigation_handle) { + if (!WillNavigationCreateNewTabSpecificContentSettingsOnCommit( + navigation_handle)) { + return; + } + + if (!navigation_handle->HasCommitted()) { + inflight_navigation_settings_.erase(navigation_handle); + return; + } + + auto tscs = + base::WrapUnique(new TabSpecificContentSettings(*this, delegate_.get())); + + // TODO(carlscab): This sort of internal. Maybe add a + // RenderDocumentHostUserData::Create(RenderFrameHost* rfh, Params...) + content::SetRenderDocumentHostUserData( + navigation_handle->GetRenderFrameHost(), + TabSpecificContentSettings::UserDataKey(), std::move(tscs)); + + auto it = inflight_navigation_settings_.find(navigation_handle); + if (it != inflight_navigation_settings_.end()) { + TransferNavigationContentSettingsToCommittedDocument( + it->second, navigation_handle->GetRenderFrameHost()); + inflight_navigation_settings_.erase(it); + } + + delegate_->UpdateLocationBar(); +} + +void TabSpecificContentSettings::WebContentsHandler::AppCacheAccessed( + const GURL& manifest_url, + bool blocked_by_policy) { + auto* tscs = TabSpecificContentSettings::GetForCurrentDocument( + web_contents()->GetMainFrame()); + if (tscs) + tscs->AppCacheAccessed(manifest_url, blocked_by_policy); +} + +void TabSpecificContentSettings::WebContentsHandler::AddSiteDataObserver( + SiteDataObserver* observer) { + observer_list_.AddObserver(observer); +} + +void TabSpecificContentSettings::WebContentsHandler::RemoveSiteDataObserver( + SiteDataObserver* observer) { + observer_list_.RemoveObserver(observer); +} + +void TabSpecificContentSettings::WebContentsHandler::NotifySiteDataObservers() { + for (SiteDataObserver& observer : observer_list_) + observer.OnSiteDataAccessed(); +} + +TabSpecificContentSettings::WebContentsHandler:: + InflightNavigationContentSettings::InflightNavigationContentSettings() = + default; +TabSpecificContentSettings::WebContentsHandler:: + InflightNavigationContentSettings::InflightNavigationContentSettings( + const InflightNavigationContentSettings&) = default; +TabSpecificContentSettings::WebContentsHandler:: + InflightNavigationContentSettings::InflightNavigationContentSettings( + InflightNavigationContentSettings&&) = default; + +TabSpecificContentSettings::WebContentsHandler:: + InflightNavigationContentSettings::~InflightNavigationContentSettings() = + default; + +TabSpecificContentSettings::WebContentsHandler:: + InflightNavigationContentSettings& + TabSpecificContentSettings::WebContentsHandler:: + InflightNavigationContentSettings::operator=( + InflightNavigationContentSettings&&) = default; + +WEB_CONTENTS_USER_DATA_KEY_IMPL(TabSpecificContentSettings::WebContentsHandler) + +TabSpecificContentSettings::TabSpecificContentSettings( + TabSpecificContentSettings::WebContentsHandler& handler, + Delegate* delegate) + : handler_(handler), + main_frame_(handler_.web_contents()->GetMainFrame()), + delegate_(delegate), + visible_url_(handler_.web_contents()->GetVisibleURL()), map_(delegate_->GetSettingsMap()), - allowed_local_shared_objects_(tab->GetBrowserContext(), - delegate_->GetAdditionalFileSystemTypes(), - delegate_->GetIsDeletionDisabledCallback()), - blocked_local_shared_objects_(tab->GetBrowserContext(), - delegate_->GetAdditionalFileSystemTypes(), - delegate_->GetIsDeletionDisabledCallback()), - geolocation_usages_state_(map_, ContentSettingsType::GEOLOCATION), - midi_usages_state_(map_, ContentSettingsType::MIDI_SYSEX), + allowed_local_shared_objects_( + handler_.web_contents()->GetBrowserContext(), + delegate_->GetAdditionalFileSystemTypes(), + delegate_->GetIsDeletionDisabledCallback()), + blocked_local_shared_objects_( + handler_.web_contents()->GetBrowserContext(), + delegate_->GetAdditionalFileSystemTypes(), + delegate_->GetIsDeletionDisabledCallback()), + geolocation_usages_state_(std::make_unique<ContentSettingsUsagesState>( + delegate_, + ContentSettingsType::GEOLOCATION, + handler_.web_contents()->GetVisibleURL())), + midi_usages_state_(std::make_unique<ContentSettingsUsagesState>( + delegate_, + ContentSettingsType::MIDI_SYSEX, + handler_.web_contents()->GetVisibleURL())), load_plugins_link_enabled_(true), microphone_camera_state_(MICROPHONE_CAMERA_NOT_ACCESSED) { - ClearContentSettingsExceptForNavigationRelatedSettings(); - ClearNavigationRelatedContentSettings(); - observer_.Add(map_); } -TabSpecificContentSettings::~TabSpecificContentSettings() { - for (SiteDataObserver& observer : observer_list_) - observer.ContentSettingsDestroyed(); -} +TabSpecificContentSettings::~TabSpecificContentSettings() = default; // static void TabSpecificContentSettings::CreateForWebContents( content::WebContents* web_contents, std::unique_ptr<Delegate> delegate) { - DCHECK(web_contents); - if (!FromWebContents(web_contents)) { - web_contents->SetUserData(UserDataKey(), - base::WrapUnique(new TabSpecificContentSettings( - web_contents, std::move(delegate)))); + TabSpecificContentSettings::WebContentsHandler::CreateForWebContents( + web_contents, std::move(delegate)); +} + +// static +void TabSpecificContentSettings::DeleteForWebContentsForTest( + content::WebContents* web_contents) { + if (web_contents->GetMainFrame()) { + TabSpecificContentSettings::DeleteForCurrentDocument( + web_contents->GetMainFrame()); } + + web_contents->RemoveUserData( + TabSpecificContentSettings::WebContentsHandler::UserDataKey()); } // static @@ -126,11 +387,18 @@ TabSpecificContentSettings* TabSpecificContentSettings::GetForFrame( content::RenderFrameHost* frame = content::RenderFrameHost::FromID(render_process_id, render_frame_id); - WebContents* web_contents = WebContents::FromRenderFrameHost(frame); - if (!web_contents) + if (!frame) return nullptr; + return TabSpecificContentSettings::GetForCurrentDocument( + frame->GetMainFrame()); +} - return TabSpecificContentSettings::FromWebContents(web_contents); +// static +TabSpecificContentSettings* TabSpecificContentSettings::FromWebContents( + content::WebContents* web_contents) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + return TabSpecificContentSettings::GetForCurrentDocument( + web_contents->GetMainFrame()); } // static @@ -197,6 +465,14 @@ void TabSpecificContentSettings::SharedWorkerAccessed( blocked_by_policy); } +// static +content::WebContentsObserver* +TabSpecificContentSettings::GetWebContentsObserverForTest( + content::WebContents* web_contents) { + return TabSpecificContentSettings::WebContentsHandler::FromWebContents( + web_contents); +} + bool TabSpecificContentSettings::IsContentBlocked( ContentSettingsType content_type) const { DCHECK_NE(ContentSettingsType::GEOLOCATION, content_type) @@ -330,22 +606,10 @@ void TabSpecificContentSettings::OnDomStorageAccessed(const GURL& url, else OnContentAllowed(ContentSettingsType::COOKIES); - NotifySiteDataObservers(); -} - -void TabSpecificContentSettings::OnCookiesAccessed( - content::NavigationHandle* navigation, - const content::CookieAccessDetails& details) { - OnCookiesAccessedImpl(details); + handler_.NotifySiteDataObservers(); } void TabSpecificContentSettings::OnCookiesAccessed( - content::RenderFrameHost* rfh, - const content::CookieAccessDetails& details) { - OnCookiesAccessedImpl(details); -} - -void TabSpecificContentSettings::OnCookiesAccessedImpl( const content::CookieAccessDetails& details) { if (details.cookie_list.empty()) return; @@ -357,7 +621,7 @@ void TabSpecificContentSettings::OnCookiesAccessedImpl( OnContentAllowed(ContentSettingsType::COOKIES); } - NotifySiteDataObservers(); + handler_.NotifySiteDataObservers(); } void TabSpecificContentSettings::OnIndexedDBAccessed(const GURL& url, @@ -370,7 +634,7 @@ void TabSpecificContentSettings::OnIndexedDBAccessed(const GURL& url, OnContentAllowed(ContentSettingsType::COOKIES); } - NotifySiteDataObservers(); + handler_.NotifySiteDataObservers(); } void TabSpecificContentSettings::OnCacheStorageAccessed( @@ -386,36 +650,10 @@ void TabSpecificContentSettings::OnCacheStorageAccessed( OnContentAllowed(ContentSettingsType::COOKIES); } - NotifySiteDataObservers(); + handler_.NotifySiteDataObservers(); } void TabSpecificContentSettings::OnServiceWorkerAccessed( - content::NavigationHandle* navigation, - const GURL& scope, - content::AllowServiceWorkerResult allowed) { - DCHECK(scope.is_valid()); - if (allowed) { - allowed_local_shared_objects_.service_workers()->Add( - url::Origin::Create(scope)); - } else { - blocked_local_shared_objects_.service_workers()->Add( - url::Origin::Create(scope)); - } - - if (allowed.javascript_blocked_by_policy()) { - OnContentBlocked(ContentSettingsType::JAVASCRIPT); - } else { - OnContentAllowed(ContentSettingsType::JAVASCRIPT); - } - if (allowed.cookies_blocked_by_policy()) { - OnContentBlocked(ContentSettingsType::COOKIES); - } else { - OnContentAllowed(ContentSettingsType::COOKIES); - } -} - -void TabSpecificContentSettings::OnServiceWorkerAccessed( - content::RenderFrameHost* frame, const GURL& scope, content::AllowServiceWorkerResult allowed) { DCHECK(scope.is_valid()); @@ -466,7 +704,7 @@ void TabSpecificContentSettings::OnWebDatabaseAccessed(const GURL& url, OnContentAllowed(ContentSettingsType::COOKIES); } - NotifySiteDataObservers(); + handler_.NotifySiteDataObservers(); } void TabSpecificContentSettings::OnFileSystemAccessed(const GURL& url, @@ -482,13 +720,13 @@ void TabSpecificContentSettings::OnFileSystemAccessed(const GURL& url, OnContentAllowed(ContentSettingsType::COOKIES); } - NotifySiteDataObservers(); + handler_.NotifySiteDataObservers(); } void TabSpecificContentSettings::OnGeolocationPermissionSet( const GURL& requesting_origin, bool allowed) { - geolocation_usages_state_.OnPermissionSet(requesting_origin, allowed); + geolocation_usages_state_->OnPermissionSet(requesting_origin, allowed); delegate_->UpdateLocationBar(); } @@ -564,44 +802,16 @@ void TabSpecificContentSettings::OnMediaStreamPermissionSet( void TabSpecificContentSettings::OnMidiSysExAccessed( const GURL& requesting_origin) { - midi_usages_state_.OnPermissionSet(requesting_origin, true); + midi_usages_state_->OnPermissionSet(requesting_origin, true); OnContentAllowed(ContentSettingsType::MIDI_SYSEX); } void TabSpecificContentSettings::OnMidiSysExAccessBlocked( const GURL& requesting_origin) { - midi_usages_state_.OnPermissionSet(requesting_origin, false); + midi_usages_state_->OnPermissionSet(requesting_origin, false); OnContentBlocked(ContentSettingsType::MIDI_SYSEX); } -void TabSpecificContentSettings:: - ClearContentSettingsExceptForNavigationRelatedSettings() { - for (auto& status : content_settings_status_) { - if (status.first == ContentSettingsType::COOKIES || - status.first == ContentSettingsType::JAVASCRIPT) - continue; - status.second.blocked = false; - status.second.allowed = false; - } - microphone_camera_state_ = MICROPHONE_CAMERA_NOT_ACCESSED; - camera_was_just_granted_on_site_level_ = false; - mic_was_just_granted_on_site_level_ = false; - load_plugins_link_enabled_ = true; - delegate_->UpdateLocationBar(); -} - -void TabSpecificContentSettings::ClearNavigationRelatedContentSettings() { - blocked_local_shared_objects_.Reset(); - allowed_local_shared_objects_.Reset(); - for (ContentSettingsType type : - {ContentSettingsType::COOKIES, ContentSettingsType::JAVASCRIPT}) { - ContentSettingsStatus& status = content_settings_status_[type]; - status.blocked = false; - status.allowed = false; - } - delegate_->UpdateLocationBar(); -} - void TabSpecificContentSettings::FlashDownloadBlocked() { OnContentBlocked(ContentSettingsType::PLUGINS); } @@ -635,7 +845,7 @@ void TabSpecificContentSettings::OnContentSettingChanged( 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())) { + !details.primary_pattern().Matches(visible_url_)) { return; } @@ -674,8 +884,7 @@ void TabSpecificContentSettings::OnContentSettingChanged( case ContentSettingsType::CLIPBOARD_READ_WRITE: case ContentSettingsType::SENSORS: { ContentSetting setting = map_->GetContentSetting( - web_contents()->GetVisibleURL(), web_contents()->GetVisibleURL(), - content_type, std::string()); + visible_url_, visible_url_, content_type, std::string()); // If an indicator is shown and the content settings has changed, swap the // indicator for the one with the opposite meaning (allowed <=> blocked). if (setting == CONTENT_SETTING_BLOCK && status.allowed) { @@ -696,82 +905,7 @@ void TabSpecificContentSettings::OnContentSettingChanged( 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; - - RendererContentSettingRules rules; - GetRendererContentSettingRules(map_, &rules); - delegate_->SetContentSettingRules(process, rules); -} - -void TabSpecificContentSettings::RenderFrameForInterstitialPageCreated( - content::RenderFrameHost* render_frame_host) { - // We want to tell the renderer-side code to ignore content settings for this - // page. - mojo::AssociatedRemote<content_settings::mojom::ContentSettingsAgent> - content_settings_agent; - render_frame_host->GetRemoteAssociatedInterfaces()->GetInterface( - &content_settings_agent); - content_settings_agent->SetAsInterstitial(); -} - -void TabSpecificContentSettings::DidStartNavigation( - content::NavigationHandle* navigation_handle) { - if (!navigation_handle->IsInMainFrame() || - navigation_handle->IsSameDocument()) { - return; - } - - ClearNavigationRelatedContentSettings(); -} - -void TabSpecificContentSettings::ReadyToCommitNavigation( - content::NavigationHandle* navigation_handle) { - if (!navigation_handle->IsInMainFrame() || - navigation_handle->IsSameDocument()) { - return; - } - - PrefService* prefs = delegate_->GetPrefs(); - if (prefs && - !prefs->GetBoolean( - security_state::prefs::kStricterMixedContentTreatmentEnabled)) { - auto* render_frame_host = navigation_handle->GetRenderFrameHost(); - mojo::AssociatedRemote<content_settings::mojom::ContentSettingsAgent> - content_settings_agent; - render_frame_host->GetRemoteAssociatedInterfaces()->GetInterface( - &content_settings_agent); - content_settings_agent->SetDisabledMixedContentUpgrades(); - } - - // 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; - } - - ClearContentSettingsExceptForNavigationRelatedSettings(); - GeolocationDidNavigate(navigation_handle); - MidiDidNavigate(navigation_handle); - ClearContentSettingsChangedViaPageInfo(); + MaybeSendRendererContentSettingsRules(main_frame_, map_, delegate_); } void TabSpecificContentSettings::AppCacheAccessed(const GURL& manifest_url, @@ -787,39 +921,10 @@ void TabSpecificContentSettings::AppCacheAccessed(const GURL& manifest_url, } } -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::ClearContentSettingsChangedViaPageInfo() { content_settings_changed_via_page_info_.clear(); } -void TabSpecificContentSettings::GeolocationDidNavigate( - content::NavigationHandle* navigation_handle) { - geolocation_usages_state_.ClearStateMap(); - geolocation_usages_state_.DidNavigate(navigation_handle->GetURL(), - navigation_handle->GetPreviousURL()); -} - -void TabSpecificContentSettings::MidiDidNavigate( - content::NavigationHandle* navigation_handle) { - midi_usages_state_.ClearStateMap(); - midi_usages_state_.DidNavigate(navigation_handle->GetURL(), - navigation_handle->GetPreviousURL()); -} - void TabSpecificContentSettings::BlockAllContentForTesting() { content_settings::ContentSettingsRegistry* registry = content_settings::ContentSettingsRegistry::GetInstance(); @@ -834,16 +939,16 @@ void TabSpecificContentSettings::BlockAllContentForTesting() { // Geolocation and media must be blocked separately, as the generic // TabSpecificContentSettings::OnContentBlocked does not apply to them. - OnGeolocationPermissionSet(web_contents()->GetLastCommittedURL(), false); + OnGeolocationPermissionSet(main_frame_->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()); + OnMediaStreamPermissionSet(main_frame_->GetLastCommittedURL(), media_blocked, + std::string(), std::string(), std::string(), + std::string()); } void TabSpecificContentSettings::ContentSettingChangedViaPageInfo( @@ -857,6 +962,6 @@ bool TabSpecificContentSettings::HasContentSettingChangedViaPageInfo( content_settings_changed_via_page_info_.end(); } -WEB_CONTENTS_USER_DATA_KEY_IMPL(TabSpecificContentSettings) +RENDER_DOCUMENT_HOST_USER_DATA_KEY_IMPL(TabSpecificContentSettings) } // namespace content_settings diff --git a/chromium/components/content_settings/browser/tab_specific_content_settings.h b/chromium/components/content_settings/browser/tab_specific_content_settings.h index 39583b71436..38529f2676f 100644 --- a/chromium/components/content_settings/browser/tab_specific_content_settings.h +++ b/chromium/components/content_settings/browser/tab_specific_content_settings.h @@ -14,17 +14,19 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" #include "base/observer_list.h" #include "base/scoped_observer.h" #include "build/build_config.h" #include "components/browsing_data/content/cookie_helper.h" #include "components/browsing_data/content/local_shared_objects_container.h" -#include "components/content_settings/browser/content_settings_usages_state.h" #include "components/content_settings/core/browser/content_settings_observer.h" #include "components/content_settings/core/browser/host_content_settings_map.h" #include "components/content_settings/core/common/content_settings.h" #include "components/content_settings/core/common/content_settings_types.h" #include "content/public/browser/allow_service_worker_result.h" +#include "content/public/browser/render_document_host_user_data.h" +#include "content/public/browser/render_frame_host.h" #include "content/public/browser/web_contents_observer.h" #include "content/public/browser/web_contents_user_data.h" @@ -36,6 +38,8 @@ namespace url { class Origin; } // namespace url +class ContentSettingsUsagesState; + namespace content_settings { // TODO(msramek): Media is storing their state in TabSpecificContentSettings: @@ -44,13 +48,28 @@ namespace content_settings { // content::WebContentsUserData // This class manages state about permissions, content settings, cookies and -// site data for a specific WebContents. It tracks which content was accessed -// and which content was blocked. Based on this it provides information about -// which types of content were accessed and blocked. +// site data for a specific page (main document and all of its child frames). It +// tracks which content was accessed and which content was blocked. Based on +// this it provides information about which types of content were accessed and +// blocked. +// +// Tracking is done per main document so instances of this class will be deleted +// when the main document is deleted. This can happen after the tab navigates +// away to a new document or when the tab itself is deleted, so you should not +// keep references to objects of this class. +// +// When a page enters the back-forward cache its associated +// TabSpecificContentSettings are not cleared and will be restored along with +// the document when navigating back. These stored instances still listen to +// content settings updates and keep their internal state up to date. +// +// Events tied to a main frame navigation will be associated with the newly +// loaded page once the navigation commits or discarded if it does not. +// +// TODO(carlscab): Rename this class to PageSpecificContentSettings class TabSpecificContentSettings - : public content::WebContentsObserver, - public content_settings::Observer, - public content::WebContentsUserData<TabSpecificContentSettings> { + : public content_settings::Observer, + public content::RenderDocumentHostUserData<TabSpecificContentSettings> { public: // Fields describing the current mic/camera state. If a page has attempted to // access a device, the XXX_ACCESSED bit will be set. If access was blocked, @@ -85,6 +104,10 @@ class TabSpecificContentSettings // Gets the settings map for the current web contents. virtual HostContentSettingsMap* GetSettingsMap() = 0; + virtual ContentSetting GetEmbargoSetting( + const GURL& request_origin, + ContentSettingsType permission) = 0; + // Gets any additional file system types which should be used when // constructing a browsing_data::FileSystemHelper. virtual std::vector<storage::FileSystemType> @@ -114,23 +137,20 @@ class TabSpecificContentSettings // |TabSpecificContentSettings|. class SiteDataObserver { public: - explicit SiteDataObserver( - TabSpecificContentSettings* tab_specific_content_settings); + explicit SiteDataObserver(content::WebContents* web_contents); virtual ~SiteDataObserver(); // Called whenever site data is accessed. virtual void OnSiteDataAccessed() = 0; - TabSpecificContentSettings* tab_specific_content_settings() { - return tab_specific_content_settings_; - } + content::WebContents* web_contents() { return web_contents_; } - // Called when the TabSpecificContentSettings is destroyed; nulls out + // Called when the WebContents is destroyed; nulls out // the local reference. - void ContentSettingsDestroyed(); + void WebContentsDestroyed(); private: - TabSpecificContentSettings* tab_specific_content_settings_; + content::WebContents* web_contents_; DISALLOW_COPY_AND_ASSIGN(SiteDataObserver); }; @@ -139,10 +159,14 @@ class TabSpecificContentSettings static void CreateForWebContents(content::WebContents* web_contents, std::unique_ptr<Delegate> delegate); + static void DeleteForWebContentsForTest(content::WebContents* web_contents); // Returns the object given a RenderFrameHost ids. static TabSpecificContentSettings* GetForFrame(int render_process_id, int render_frame_id); + // TODO(carlscab): Get rid of this and use GetForFrame instead + static TabSpecificContentSettings* FromWebContents( + content::WebContents* contents); // Called when a specific Web database in the current page was accessed. If // access was blocked due to the user's content settings, @@ -188,17 +212,15 @@ class TabSpecificContentSettings const url::Origin& constructor_origin, bool blocked_by_policy); - // Resets the |content_settings_status_|, except for - // information which are needed for navigation: ContentSettingsType::COOKIES - // for cookies and service workers, and ContentSettingsType::JAVASCRIPT for - // service workers. - // Only public for tests. - void ClearContentSettingsExceptForNavigationRelatedSettings(); + static content::WebContentsObserver* GetWebContentsObserverForTest( + content::WebContents* web_contents); - // Resets navigation related information (ContentSettingsType::COOKIES and - // ContentSettingsType::JAVASCRIPT). - // Only public for tests. - void ClearNavigationRelatedContentSettings(); + // Returns a WeakPtr to this instance. Given that TabSpecificContentSettings + // instances are tied to a page it is generally unsafe to store these + // references, instead a WeakPtr should be used instead. + base::WeakPtr<TabSpecificContentSettings> AsWeakPtr() { + return weak_factory_.GetWeakPtr(); + } // Notifies that a Flash download has been blocked. void FlashDownloadBlocked(); @@ -259,13 +281,13 @@ class TabSpecificContentSettings // Returns the ContentSettingsUsagesState that controls the // geolocation API usage on this page. const ContentSettingsUsagesState& geolocation_usages_state() const { - return geolocation_usages_state_; + return *geolocation_usages_state_; } // Returns the ContentSettingsUsageState that controls the MIDI usage on // this page. const ContentSettingsUsagesState& midi_usages_state() const { - return midi_usages_state_; + return *midi_usages_state_; } // Returns the |LocalSharedObjectsContainer| instances corresponding to all @@ -329,12 +351,9 @@ class TabSpecificContentSettings void OnMidiSysExAccessed(const GURL& reqesting_origin); void OnMidiSysExAccessBlocked(const GURL& requesting_origin); - // Adds the given |SiteDataObserver|. The |observer| is notified when a - // locale shared object, like for example a cookie, is accessed. - void AddSiteDataObserver(SiteDataObserver* observer); - - // Removes the given |SiteDataObserver|. - void RemoveSiteDataObserver(SiteDataObserver* observer); + void OnCookiesAccessed(const content::CookieAccessDetails& details); + void OnServiceWorkerAccessed(const GURL& scope, + content::AllowServiceWorkerResult allowed); // Block all content. Used for testing content setting bubbles. void BlockAllContentForTesting(); @@ -346,45 +365,121 @@ class TabSpecificContentSettings // since the last navigation. bool HasContentSettingChangedViaPageInfo(ContentSettingsType type) const; - Delegate* delegate() { return delegate_.get(); } + Delegate* delegate() { return delegate_; } private: - friend class content::WebContentsUserData<TabSpecificContentSettings>; + friend class content::RenderDocumentHostUserData<TabSpecificContentSettings>; + + // This class attaches to WebContents to listen to events and route them to + // appropriate TabSpecificContentSettings, store navigation related events + // until the navigation finishes and then transferring the + // navigation-associated state to the newly-created page. + class WebContentsHandler + : public content::WebContentsObserver, + public content::WebContentsUserData<WebContentsHandler> { + public: + static void CreateForWebContents(content::WebContents* web_contents, + std::unique_ptr<Delegate> delegate); - explicit TabSpecificContentSettings(content::WebContents* tab, - std::unique_ptr<Delegate> delegate); + explicit WebContentsHandler(content::WebContents* web_contents, + std::unique_ptr<Delegate> delegate); + ~WebContentsHandler() override; + // Adds the given |SiteDataObserver|. The |observer| is notified when a + // locale shared object, like for example a cookie, is accessed. + void AddSiteDataObserver(SiteDataObserver* observer); - void MaybeSendRendererContentSettingsRules( - content::WebContents* web_contents); + // Removes the given |SiteDataObserver|. + void RemoveSiteDataObserver(SiteDataObserver* observer); - // content::WebContentsObserver overrides. - void RenderFrameForInterstitialPageCreated( - content::RenderFrameHost* render_frame_host) override; - void DidStartNavigation( - content::NavigationHandle* navigation_handle) override; - void ReadyToCommitNavigation( - content::NavigationHandle* navigation_handle) override; - void DidFinishNavigation( - content::NavigationHandle* navigation_handle) override; - void AppCacheAccessed(const GURL& manifest_url, - bool blocked_by_policy) override; - void OnCookiesAccessed(content::NavigationHandle* navigation, - const content::CookieAccessDetails& details) override; - void OnCookiesAccessed(content::RenderFrameHost* rfh, - const content::CookieAccessDetails& details) override; - // Called when a specific Service Worker scope was accessed. - // If access was blocked due to the user's content settings, - // |blocked_by_policy_javascript| or/and |blocked_by_policy_cookie| should be - // true, and this function should invoke OnContentBlocked for JavaScript - // or/and cookies respectively. - void OnServiceWorkerAccessed( - content::NavigationHandle* navigation, - const GURL& scope, - content::AllowServiceWorkerResult allowed) override; - void OnServiceWorkerAccessed( - content::RenderFrameHost* frame, - const GURL& scope, - content::AllowServiceWorkerResult allowed) override; + // Notifies all registered |SiteDataObserver|s. + void NotifySiteDataObservers(); + + private: + friend class content::WebContentsUserData<WebContentsHandler>; + + // Keeps track of cookie and service worker access during a navigation. + // These types of access can happen for the current page or for a new + // navigation (think cookies sent in the HTTP request or service worker + // being run to serve a fetch request). A navigation might fail to + // commit in which case we have to handle it as if it had never + // occurred. So we cache all cookies and service worker accesses that + // happen during a navigation and only apply the changes if the + // navigation commits. + struct InflightNavigationContentSettings { + InflightNavigationContentSettings(); + InflightNavigationContentSettings( + const InflightNavigationContentSettings&); + InflightNavigationContentSettings(InflightNavigationContentSettings&&); + + ~InflightNavigationContentSettings(); + + InflightNavigationContentSettings& operator=( + InflightNavigationContentSettings&&); + + std::vector<content::CookieAccessDetails> cookie_accesses; + std::vector<std::pair<GURL, content::AllowServiceWorkerResult>> + service_worker_accesses; + }; + + // Applies all stored events for the given navigation to the current main + // document. + void TransferNavigationContentSettingsToCommittedDocument( + const InflightNavigationContentSettings& navigation_settings, + content::RenderFrameHost* rfh); + + // content::WebContentsObserver overrides. + void RenderFrameForInterstitialPageCreated( + content::RenderFrameHost* render_frame_host) override; + void DidStartNavigation( + content::NavigationHandle* navigation_handle) override; + void ReadyToCommitNavigation( + content::NavigationHandle* navigation_handle) override; + void DidFinishNavigation( + content::NavigationHandle* navigation_handle) override; + // TODO(carlscab): Change interface to pass target RenderFrameHost + void AppCacheAccessed(const GURL& manifest_url, + bool blocked_by_policy) override; + void OnCookiesAccessed( + content::NavigationHandle* navigation, + const content::CookieAccessDetails& details) override; + void OnCookiesAccessed( + content::RenderFrameHost* rfh, + const content::CookieAccessDetails& details) override; + // Called when a specific Service Worker scope was accessed. + // If access was blocked due to the user's content settings, + // |blocked_by_policy_javascript| or/and |blocked_by_policy_cookie| + // should be true, and this function should invoke OnContentBlocked for + // JavaScript or/and cookies respectively. + void OnServiceWorkerAccessed( + content::NavigationHandle* navigation, + const GURL& scope, + content::AllowServiceWorkerResult allowed) override; + void OnServiceWorkerAccessed( + content::RenderFrameHost* frame, + const GURL& scope, + content::AllowServiceWorkerResult allowed) override; + + std::unique_ptr<Delegate> delegate_; + + HostContentSettingsMap* map_; + + // All currently registered |SiteDataObserver|s. + base::ObserverList<SiteDataObserver>::Unchecked observer_list_; + + // Keeps track of currently inflight navigations. Updates for those are + // kept aside until the navigation commits. + std::unordered_map<content::NavigationHandle*, + InflightNavigationContentSettings> + inflight_navigation_settings_; + + WEB_CONTENTS_USER_DATA_KEY_DECL(); + }; + + explicit TabSpecificContentSettings( + TabSpecificContentSettings::WebContentsHandler& handler, + Delegate* delegate); + + void AppCacheAccessed(const GURL& manifest_url, bool blocked_by_policy); // content_settings::Observer implementation. void OnContentSettingChanged(const ContentSettingsPattern& primary_pattern, @@ -392,25 +487,15 @@ class TabSpecificContentSettings ContentSettingsType content_type, const std::string& resource_identifier) override; - // Notifies all registered |SiteDataObserver|s. - void NotifySiteDataObservers(); - // Clears settings changed by the user via PageInfo since the last navigation. void ClearContentSettingsChangedViaPageInfo(); - // Updates Geolocation settings on navigation. - void GeolocationDidNavigate(content::NavigationHandle* navigation_handle); - - // Updates MIDI settings on navigation. - void MidiDidNavigate(content::NavigationHandle* navigation_handle); + WebContentsHandler& handler_; + content::RenderFrameHost* main_frame_; - // Updates the list of allowed and blocked cookies. - void OnCookiesAccessedImpl(const content::CookieAccessDetails& details); + Delegate* delegate_; - std::unique_ptr<Delegate> delegate_; - - // All currently registered |SiteDataObserver|s. - base::ObserverList<SiteDataObserver>::Unchecked observer_list_; + GURL visible_url_; struct ContentSettingsStatus { bool blocked; @@ -427,10 +512,10 @@ class TabSpecificContentSettings browsing_data::LocalSharedObjectsContainer blocked_local_shared_objects_; // Manages information about Geolocation API usage in this page. - ContentSettingsUsagesState geolocation_usages_state_; + std::unique_ptr<ContentSettingsUsagesState> geolocation_usages_state_; // Manages information about MIDI usages in this page. - ContentSettingsUsagesState midi_usages_state_; + std::unique_ptr<ContentSettingsUsagesState> midi_usages_state_; // Stores whether the user can load blocked plugins on this page. bool load_plugins_link_enabled_; @@ -464,7 +549,9 @@ class TabSpecificContentSettings // navigation. Used to determine whether to display the settings in page info. std::set<ContentSettingsType> content_settings_changed_via_page_info_; - WEB_CONTENTS_USER_DATA_KEY_DECL(); + RENDER_DOCUMENT_HOST_USER_DATA_KEY_DECL(); + + base::WeakPtrFactory<TabSpecificContentSettings> weak_factory_{this}; DISALLOW_COPY_AND_ASSIGN(TabSpecificContentSettings); }; diff --git a/chromium/components/content_settings/browser/tab_specific_content_settings_unittest.cc b/chromium/components/content_settings/browser/tab_specific_content_settings_unittest.cc index 2ac8dc5c645..9477ad9153b 100644 --- a/chromium/components/content_settings/browser/tab_specific_content_settings_unittest.cc +++ b/chromium/components/content_settings/browser/tab_specific_content_settings_unittest.cc @@ -9,10 +9,13 @@ #include "base/strings/string16.h" #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" +#include "components/content_settings/browser/test_tab_specific_content_settings_delegate.h" #include "components/content_settings/core/browser/host_content_settings_map.h" #include "components/security_state/core/security_state.h" #include "components/sync_preferences/testing_pref_service_syncable.h" +#include "content/public/browser/web_contents_observer.h" #include "content/public/test/mock_navigation_handle.h" +#include "content/public/test/navigation_simulator.h" #include "content/public/test/test_renderer_host.h" #include "net/cookies/canonical_cookie.h" #include "net/cookies/cookie_options.h" @@ -25,9 +28,8 @@ namespace { class MockSiteDataObserver : public TabSpecificContentSettings::SiteDataObserver { public: - explicit MockSiteDataObserver( - TabSpecificContentSettings* tab_specific_content_settings) - : SiteDataObserver(tab_specific_content_settings) {} + explicit MockSiteDataObserver(content::WebContents* web_contents) + : SiteDataObserver(web_contents) {} ~MockSiteDataObserver() override = default; @@ -45,11 +47,12 @@ class TabSpecificContentSettingsTest void SetUp() override { RenderViewHostTestHarness::SetUp(); HostContentSettingsMap::RegisterProfilePrefs(prefs_.registry()); - security_state::RegisterProfilePrefs(prefs_.registry()); settings_map_ = base::MakeRefCounted<HostContentSettingsMap>( &prefs_, false, false, false, false); TabSpecificContentSettings::CreateForWebContents( - web_contents(), std::make_unique<TestDelegate>(this)); + web_contents(), + std::make_unique<TestTabSpecificContentSettingsDelegate>( + &prefs_, settings_map_.get())); } void TearDown() override { @@ -59,57 +62,18 @@ class TabSpecificContentSettingsTest HostContentSettingsMap* settings_map() { return settings_map_.get(); } - private: - class TestDelegate : public TabSpecificContentSettings::Delegate { - public: - explicit TestDelegate(TabSpecificContentSettingsTest* test) : test_(test) {} - - void UpdateLocationBar() override {} - - void SetContentSettingRules( - content::RenderProcessHost* process, - const RendererContentSettingRules& rules) override {} - - PrefService* GetPrefs() override { return &test_->prefs_; } - - HostContentSettingsMap* GetSettingsMap() override { - return test_->settings_map_.get(); - } - - std::vector<storage::FileSystemType> GetAdditionalFileSystemTypes() - override { - return {}; - } - - browsing_data::CookieHelper::IsDeletionDisabledCallback - GetIsDeletionDisabledCallback() override { - return base::NullCallback(); - } - - bool IsMicrophoneCameraStateChanged( - TabSpecificContentSettings::MicrophoneCameraState - microphone_camera_state, - const std::string& media_stream_selected_audio_device, - const std::string& media_stream_selected_video_device) override { - return false; - } - - TabSpecificContentSettings::MicrophoneCameraState GetMicrophoneCameraState() - override { - return TabSpecificContentSettings::MICROPHONE_CAMERA_NOT_ACCESSED; - } - - void OnContentBlocked(ContentSettingsType type) override {} - - private: - TabSpecificContentSettingsTest* test_; - }; + content::WebContentsObserver* GetHandle() { + return TabSpecificContentSettings::GetWebContentsObserverForTest( + web_contents()); + } + private: sync_preferences::TestingPrefServiceSyncable prefs_; scoped_refptr<HostContentSettingsMap> settings_map_; }; TEST_F(TabSpecificContentSettingsTest, BlockedContent) { + NavigateAndCommit(GURL("http://google.com")); TabSpecificContentSettings* content_settings = TabSpecificContentSettings::FromWebContents(web_contents()); @@ -136,13 +100,14 @@ TEST_F(TabSpecificContentSettingsTest, BlockedContent) { std::unique_ptr<net::CanonicalCookie> cookie1(net::CanonicalCookie::Create( origin, "A=B", base::Time::Now(), base::nullopt /* server_time */)); ASSERT_TRUE(cookie1); - static_cast<content::WebContentsObserver*>(content_settings) - ->OnCookiesAccessed(web_contents()->GetMainFrame(), - {content::CookieAccessDetails::Type::kChange, - origin, - origin, - {*cookie1}, - false}); + GetHandle()->OnCookiesAccessed(web_contents()->GetMainFrame(), + {content::CookieAccessDetails::Type::kChange, + origin, + origin, + {*cookie1}, + false}); + content_settings = + TabSpecificContentSettings::FromWebContents(web_contents()); #if !defined(OS_ANDROID) content_settings->OnContentBlocked(ContentSettingsType::IMAGES); #endif @@ -173,64 +138,54 @@ TEST_F(TabSpecificContentSettingsTest, BlockedContent) { content_settings->IsContentBlocked(ContentSettingsType::MEDIASTREAM_MIC)); EXPECT_TRUE(content_settings->IsContentBlocked( ContentSettingsType::MEDIASTREAM_CAMERA)); - static_cast<content::WebContentsObserver*>(content_settings) - ->OnCookiesAccessed(web_contents()->GetMainFrame(), - {content::CookieAccessDetails::Type::kChange, - origin, - origin, - {*cookie1}, - false}); + GetHandle()->OnCookiesAccessed(web_contents()->GetMainFrame(), + {content::CookieAccessDetails::Type::kChange, + origin, + origin, + {*cookie1}, + false}); // Block a cookie. std::unique_ptr<net::CanonicalCookie> cookie2(net::CanonicalCookie::Create( origin, "C=D", base::Time::Now(), base::nullopt /* server_time */)); ASSERT_TRUE(cookie2); - static_cast<content::WebContentsObserver*>(content_settings) - ->OnCookiesAccessed(web_contents()->GetMainFrame(), - {content::CookieAccessDetails::Type::kChange, - origin, - origin, - {*cookie2}, - true}); + GetHandle()->OnCookiesAccessed(web_contents()->GetMainFrame(), + {content::CookieAccessDetails::Type::kChange, + origin, + origin, + {*cookie2}, + true}); EXPECT_TRUE(content_settings->IsContentBlocked(ContentSettingsType::COOKIES)); // Block a javascript during a navigation. - content::MockNavigationHandle navigation_handle; - static_cast<content::WebContentsObserver*>(content_settings) - ->OnServiceWorkerAccessed( - &navigation_handle, GURL("http://google.com"), - content::AllowServiceWorkerResult::FromPolicy(true, false)); - EXPECT_TRUE( + // Create a pending navigation. + std::unique_ptr<content::NavigationSimulator> simulator = + content::NavigationSimulator::CreateBrowserInitiated( + GURL("http://google.com"), web_contents()); + simulator->SetTransition(ui::PAGE_TRANSITION_LINK); + simulator->Start(); + GetHandle()->OnServiceWorkerAccessed( + simulator->GetNavigationHandle(), GURL("http://google.com"), + content::AllowServiceWorkerResult::FromPolicy(true, false)); + content_settings = + TabSpecificContentSettings::FromWebContents(web_contents()); + EXPECT_FALSE( content_settings->IsContentBlocked(ContentSettingsType::JAVASCRIPT)); + simulator->Commit(); + content_settings = + TabSpecificContentSettings::FromWebContents(web_contents()); // Block a javascript when page starts to start ServiceWorker. - static_cast<content::WebContentsObserver*>(content_settings) - ->OnServiceWorkerAccessed( - web_contents()->GetMainFrame(), GURL("http://google.com"), - content::AllowServiceWorkerResult::FromPolicy(true, false)); + GetHandle()->OnServiceWorkerAccessed( + web_contents()->GetMainFrame(), GURL("http://google.com"), + content::AllowServiceWorkerResult::FromPolicy(true, false)); EXPECT_TRUE( content_settings->IsContentBlocked(ContentSettingsType::JAVASCRIPT)); // Reset blocked content settings. - content_settings->ClearContentSettingsExceptForNavigationRelatedSettings(); - - // Reset blocked content settings. - content_settings->ClearContentSettingsExceptForNavigationRelatedSettings(); -#if !defined(OS_ANDROID) - EXPECT_FALSE(content_settings->IsContentBlocked(ContentSettingsType::IMAGES)); - EXPECT_FALSE( - content_settings->IsContentBlocked(ContentSettingsType::PLUGINS)); -#endif - EXPECT_TRUE( - content_settings->IsContentBlocked(ContentSettingsType::JAVASCRIPT)); - EXPECT_TRUE(content_settings->IsContentBlocked(ContentSettingsType::COOKIES)); - EXPECT_FALSE(content_settings->IsContentBlocked(ContentSettingsType::POPUPS)); - EXPECT_FALSE( - content_settings->IsContentBlocked(ContentSettingsType::MEDIASTREAM_MIC)); - EXPECT_FALSE(content_settings->IsContentBlocked( - ContentSettingsType::MEDIASTREAM_CAMERA)); - - content_settings->ClearNavigationRelatedContentSettings(); + NavigateAndCommit(GURL("http://google.com")); + content_settings = + TabSpecificContentSettings::FromWebContents(web_contents()); #if !defined(OS_ANDROID) EXPECT_FALSE(content_settings->IsContentBlocked(ContentSettingsType::IMAGES)); EXPECT_FALSE( @@ -248,6 +203,7 @@ TEST_F(TabSpecificContentSettingsTest, BlockedContent) { } TEST_F(TabSpecificContentSettingsTest, BlockedFileSystems) { + NavigateAndCommit(GURL("http://google.com")); TabSpecificContentSettings* content_settings = TabSpecificContentSettings::FromWebContents(web_contents()); @@ -262,6 +218,7 @@ TEST_F(TabSpecificContentSettingsTest, BlockedFileSystems) { } TEST_F(TabSpecificContentSettingsTest, AllowedContent) { + NavigateAndCommit(GURL("http://google.com")); TabSpecificContentSettings* content_settings = TabSpecificContentSettings::FromWebContents(web_contents()); @@ -281,13 +238,12 @@ TEST_F(TabSpecificContentSettingsTest, AllowedContent) { std::unique_ptr<net::CanonicalCookie> cookie1(net::CanonicalCookie::Create( origin, "A=B", base::Time::Now(), base::nullopt /* server_time */)); ASSERT_TRUE(cookie1); - static_cast<content::WebContentsObserver*>(content_settings) - ->OnCookiesAccessed(web_contents()->GetMainFrame(), - {content::CookieAccessDetails::Type::kChange, - origin, - origin, - {*cookie1}, - false}); + GetHandle()->OnCookiesAccessed(web_contents()->GetMainFrame(), + {content::CookieAccessDetails::Type::kChange, + origin, + origin, + {*cookie1}, + false}); ASSERT_TRUE(content_settings->IsContentAllowed(ContentSettingsType::COOKIES)); ASSERT_FALSE( content_settings->IsContentBlocked(ContentSettingsType::COOKIES)); @@ -296,18 +252,18 @@ TEST_F(TabSpecificContentSettingsTest, AllowedContent) { std::unique_ptr<net::CanonicalCookie> cookie2(net::CanonicalCookie::Create( origin, "C=D", base::Time::Now(), base::nullopt /* server_time */)); ASSERT_TRUE(cookie2); - static_cast<content::WebContentsObserver*>(content_settings) - ->OnCookiesAccessed(web_contents()->GetMainFrame(), - {content::CookieAccessDetails::Type::kChange, - origin, - origin, - {*cookie2}, - true}); + GetHandle()->OnCookiesAccessed(web_contents()->GetMainFrame(), + {content::CookieAccessDetails::Type::kChange, + origin, + origin, + {*cookie2}, + true}); ASSERT_TRUE(content_settings->IsContentAllowed(ContentSettingsType::COOKIES)); ASSERT_TRUE(content_settings->IsContentBlocked(ContentSettingsType::COOKIES)); } TEST_F(TabSpecificContentSettingsTest, EmptyCookieList) { + NavigateAndCommit(GURL("http://google.com")); TabSpecificContentSettings* content_settings = TabSpecificContentSettings::FromWebContents(web_contents()); @@ -315,11 +271,10 @@ TEST_F(TabSpecificContentSettingsTest, EmptyCookieList) { content_settings->IsContentAllowed(ContentSettingsType::COOKIES)); ASSERT_FALSE( content_settings->IsContentBlocked(ContentSettingsType::COOKIES)); - static_cast<content::WebContentsObserver*>(content_settings) - ->OnCookiesAccessed( - web_contents()->GetMainFrame(), - {content::CookieAccessDetails::Type::kRead, GURL("http://google.com"), - GURL("http://google.com"), net::CookieList(), true}); + GetHandle()->OnCookiesAccessed( + web_contents()->GetMainFrame(), + {content::CookieAccessDetails::Type::kRead, GURL("http://google.com"), + GURL("http://google.com"), net::CookieList(), true}); ASSERT_FALSE( content_settings->IsContentAllowed(ContentSettingsType::COOKIES)); ASSERT_FALSE( @@ -327,9 +282,10 @@ TEST_F(TabSpecificContentSettingsTest, EmptyCookieList) { } TEST_F(TabSpecificContentSettingsTest, SiteDataObserver) { + NavigateAndCommit(GURL("http://google.com")); TabSpecificContentSettings* content_settings = TabSpecificContentSettings::FromWebContents(web_contents()); - MockSiteDataObserver mock_observer(content_settings); + MockSiteDataObserver mock_observer(web_contents()); EXPECT_CALL(mock_observer, OnSiteDataAccessed()).Times(6); bool blocked_by_policy = false; @@ -337,13 +293,12 @@ TEST_F(TabSpecificContentSettingsTest, SiteDataObserver) { std::unique_ptr<net::CanonicalCookie> cookie(net::CanonicalCookie::Create( origin, "A=B", base::Time::Now(), base::nullopt /* server_time */)); ASSERT_TRUE(cookie); - static_cast<content::WebContentsObserver*>(content_settings) - ->OnCookiesAccessed(web_contents()->GetMainFrame(), - {content::CookieAccessDetails::Type::kChange, - origin, - origin, - {*cookie}, - blocked_by_policy}); + GetHandle()->OnCookiesAccessed(web_contents()->GetMainFrame(), + {content::CookieAccessDetails::Type::kChange, + origin, + origin, + {*cookie}, + blocked_by_policy}); net::CookieList cookie_list; std::unique_ptr<net::CanonicalCookie> other_cookie( @@ -353,11 +308,10 @@ TEST_F(TabSpecificContentSettingsTest, SiteDataObserver) { ASSERT_TRUE(other_cookie); cookie_list.push_back(*other_cookie); - static_cast<content::WebContentsObserver*>(content_settings) - ->OnCookiesAccessed( - web_contents()->GetMainFrame(), - {content::CookieAccessDetails::Type::kRead, GURL("http://google.com"), - GURL("http://google.com"), cookie_list, blocked_by_policy}); + GetHandle()->OnCookiesAccessed( + web_contents()->GetMainFrame(), + {content::CookieAccessDetails::Type::kRead, GURL("http://google.com"), + GURL("http://google.com"), cookie_list, blocked_by_policy}); content_settings->OnFileSystemAccessed(GURL("http://google.com"), blocked_by_policy); content_settings->OnIndexedDBAccessed(GURL("http://google.com"), @@ -369,19 +323,19 @@ TEST_F(TabSpecificContentSettingsTest, SiteDataObserver) { } TEST_F(TabSpecificContentSettingsTest, LocalSharedObjectsContainer) { + NavigateAndCommit(GURL("http://google.com")); TabSpecificContentSettings* content_settings = TabSpecificContentSettings::FromWebContents(web_contents()); bool blocked_by_policy = false; auto cookie = net::CanonicalCookie::Create(GURL("http://google.com"), "k=v", base::Time::Now(), base::nullopt /* server_time */); - static_cast<content::WebContentsObserver*>(content_settings) - ->OnCookiesAccessed(web_contents()->GetMainFrame(), - {content::CookieAccessDetails::Type::kRead, - GURL("http://google.com"), - GURL("http://google.com"), - {*cookie}, - blocked_by_policy}); + GetHandle()->OnCookiesAccessed(web_contents()->GetMainFrame(), + {content::CookieAccessDetails::Type::kRead, + GURL("http://google.com"), + GURL("http://google.com"), + {*cookie}, + blocked_by_policy}); content_settings->OnFileSystemAccessed(GURL("https://www.google.com"), blocked_by_policy); content_settings->OnIndexedDBAccessed(GURL("https://localhost"), @@ -406,6 +360,7 @@ TEST_F(TabSpecificContentSettingsTest, LocalSharedObjectsContainer) { } TEST_F(TabSpecificContentSettingsTest, LocalSharedObjectsContainerCookie) { + NavigateAndCommit(GURL("http://google.com")); TabSpecificContentSettings* content_settings = TabSpecificContentSettings::FromWebContents(web_contents()); bool blocked_by_policy = false; @@ -421,24 +376,22 @@ TEST_F(TabSpecificContentSettingsTest, LocalSharedObjectsContainerCookie) { auto cookie4 = net::CanonicalCookie::Create( GURL("http://www.google.com"), "k4=v; Domain=.www.google.com", base::Time::Now(), base::nullopt /* server_time */); - static_cast<content::WebContentsObserver*>(content_settings) - ->OnCookiesAccessed(web_contents()->GetMainFrame(), - {content::CookieAccessDetails::Type::kRead, - GURL("http://www.google.com"), - GURL("http://www.google.com"), - {*cookie1, *cookie2, *cookie3, *cookie4}, - blocked_by_policy}); + GetHandle()->OnCookiesAccessed(web_contents()->GetMainFrame(), + {content::CookieAccessDetails::Type::kRead, + GURL("http://www.google.com"), + GURL("http://www.google.com"), + {*cookie1, *cookie2, *cookie3, *cookie4}, + blocked_by_policy}); auto cookie5 = net::CanonicalCookie::Create(GURL("https://www.google.com"), "k5=v", base::Time::Now(), base::nullopt /* server_time */); - static_cast<content::WebContentsObserver*>(content_settings) - ->OnCookiesAccessed(web_contents()->GetMainFrame(), - {content::CookieAccessDetails::Type::kRead, - GURL("https://www.google.com"), - GURL("https://www.google.com"), - {*cookie5}, - blocked_by_policy}); + GetHandle()->OnCookiesAccessed(web_contents()->GetMainFrame(), + {content::CookieAccessDetails::Type::kRead, + GURL("https://www.google.com"), + GURL("https://www.google.com"), + {*cookie5}, + blocked_by_policy}); const auto& objects = content_settings->allowed_local_shared_objects(); EXPECT_EQ(5u, objects.GetObjectCount()); diff --git a/chromium/components/content_settings/browser/test_tab_specific_content_settings_delegate.cc b/chromium/components/content_settings/browser/test_tab_specific_content_settings_delegate.cc new file mode 100644 index 00000000000..bfe9e77af54 --- /dev/null +++ b/chromium/components/content_settings/browser/test_tab_specific_content_settings_delegate.cc @@ -0,0 +1,63 @@ +// Copyright 2020 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 "components/content_settings/browser/test_tab_specific_content_settings_delegate.h" + +namespace content_settings { + +TestTabSpecificContentSettingsDelegate::TestTabSpecificContentSettingsDelegate( + PrefService* prefs, + HostContentSettingsMap* settings_map) + : prefs_(prefs), settings_map_(settings_map) {} + +TestTabSpecificContentSettingsDelegate:: + ~TestTabSpecificContentSettingsDelegate() = default; + +void TestTabSpecificContentSettingsDelegate::UpdateLocationBar() {} + +void TestTabSpecificContentSettingsDelegate::SetContentSettingRules( + content::RenderProcessHost* process, + const RendererContentSettingRules& rules) {} + +PrefService* TestTabSpecificContentSettingsDelegate::GetPrefs() { + return prefs_; +} + +HostContentSettingsMap* +TestTabSpecificContentSettingsDelegate::GetSettingsMap() { + return settings_map_.get(); +} + +ContentSetting TestTabSpecificContentSettingsDelegate::GetEmbargoSetting( + const GURL& request_origin, + ContentSettingsType permission) { + return ContentSetting::CONTENT_SETTING_ASK; +} + +std::vector<storage::FileSystemType> +TestTabSpecificContentSettingsDelegate::GetAdditionalFileSystemTypes() { + return {}; +} + +browsing_data::CookieHelper::IsDeletionDisabledCallback +TestTabSpecificContentSettingsDelegate::GetIsDeletionDisabledCallback() { + return base::NullCallback(); +} + +bool TestTabSpecificContentSettingsDelegate::IsMicrophoneCameraStateChanged( + TabSpecificContentSettings::MicrophoneCameraState microphone_camera_state, + const std::string& media_stream_selected_audio_device, + const std::string& media_stream_selected_video_device) { + return false; +} + +TabSpecificContentSettings::MicrophoneCameraState +TestTabSpecificContentSettingsDelegate::GetMicrophoneCameraState() { + return TabSpecificContentSettings::MICROPHONE_CAMERA_NOT_ACCESSED; +} + +void TestTabSpecificContentSettingsDelegate::OnContentBlocked( + ContentSettingsType type) {} + +} // namespace content_settings diff --git a/chromium/components/content_settings/browser/test_tab_specific_content_settings_delegate.h b/chromium/components/content_settings/browser/test_tab_specific_content_settings_delegate.h new file mode 100644 index 00000000000..9fde1406745 --- /dev/null +++ b/chromium/components/content_settings/browser/test_tab_specific_content_settings_delegate.h @@ -0,0 +1,47 @@ +// Copyright 2020 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. + +#ifndef COMPONENTS_CONTENT_SETTINGS_BROWSER_TEST_TAB_SPECIFIC_CONTENT_SETTINGS_DELEGATE_H_ +#define COMPONENTS_CONTENT_SETTINGS_BROWSER_TEST_TAB_SPECIFIC_CONTENT_SETTINGS_DELEGATE_H_ + +#include "base/memory/scoped_refptr.h" +#include "components/content_settings/browser/tab_specific_content_settings.h" + +namespace content_settings { + +class TestTabSpecificContentSettingsDelegate + : public TabSpecificContentSettings::Delegate { + public: + TestTabSpecificContentSettingsDelegate(PrefService* prefs, + HostContentSettingsMap* settings_map); + ~TestTabSpecificContentSettingsDelegate() override; + + // TabSpecificContentSettings::Delegate: + void UpdateLocationBar() override; + void SetContentSettingRules( + content::RenderProcessHost* process, + const RendererContentSettingRules& rules) override; + PrefService* GetPrefs() override; + HostContentSettingsMap* GetSettingsMap() override; + ContentSetting GetEmbargoSetting(const GURL& request_origin, + ContentSettingsType permission) override; + std::vector<storage::FileSystemType> GetAdditionalFileSystemTypes() override; + browsing_data::CookieHelper::IsDeletionDisabledCallback + GetIsDeletionDisabledCallback() override; + bool IsMicrophoneCameraStateChanged( + TabSpecificContentSettings::MicrophoneCameraState microphone_camera_state, + const std::string& media_stream_selected_audio_device, + const std::string& media_stream_selected_video_device) override; + TabSpecificContentSettings::MicrophoneCameraState GetMicrophoneCameraState() + override; + void OnContentBlocked(ContentSettingsType type) override; + + private: + PrefService* prefs_; + scoped_refptr<HostContentSettingsMap> settings_map_; +}; + +} // namespace content_settings + +#endif // COMPONENTS_CONTENT_SETTINGS_BROWSER_TEST_TAB_SPECIFIC_CONTENT_SETTINGS_DELEGATE_H_ diff --git a/chromium/components/content_settings/browser/ui/BUILD.gn b/chromium/components/content_settings/browser/ui/BUILD.gn new file mode 100644 index 00000000000..0f3e91d4da0 --- /dev/null +++ b/chromium/components/content_settings/browser/ui/BUILD.gn @@ -0,0 +1,21 @@ +# Copyright 2020 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. + +source_set("ui") { + sources = [ + "cookie_controls_controller.cc", + "cookie_controls_controller.h", + "cookie_controls_view.h", + ] + + deps = [ + "//base", + "//components/browsing_data/content", + "//components/content_settings/browser", + "//components/content_settings/core/browser", + "//components/prefs", + "//content/public/browser", + "//content/public/common", + ] +} diff --git a/chromium/components/content_settings/browser/ui/cookie_controls_controller.cc b/chromium/components/content_settings/browser/ui/cookie_controls_controller.cc new file mode 100644 index 00000000000..ef908305ac0 --- /dev/null +++ b/chromium/components/content_settings/browser/ui/cookie_controls_controller.cc @@ -0,0 +1,161 @@ +// Copyright 2019 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 "components/content_settings/browser/ui/cookie_controls_controller.h" + +#include <memory> + +#include "base/bind.h" +#include "base/metrics/user_metrics.h" +#include "base/metrics/user_metrics_action.h" +#include "components/browsing_data/content/local_shared_objects_container.h" +#include "components/content_settings/browser/tab_specific_content_settings.h" +#include "components/content_settings/browser/ui/cookie_controls_view.h" +#include "components/content_settings/core/browser/content_settings_utils.h" +#include "components/content_settings/core/browser/cookie_settings.h" +#include "components/content_settings/core/common/content_settings.h" +#include "components/content_settings/core/common/cookie_controls_enforcement.h" +#include "components/content_settings/core/common/cookie_controls_status.h" +#include "components/content_settings/core/common/pref_names.h" +#include "components/prefs/pref_service.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/reload_type.h" +#include "content/public/browser/web_contents.h" +#include "content/public/common/url_constants.h" + +using base::UserMetricsAction; + +namespace content_settings { + +CookieControlsController::CookieControlsController( + scoped_refptr<CookieSettings> cookie_settings, + scoped_refptr<CookieSettings> original_cookie_settings) + : cookie_settings_(cookie_settings), + original_cookie_settings_(original_cookie_settings) { + cookie_observer_.Add(cookie_settings_.get()); +} + +CookieControlsController::~CookieControlsController() = default; + +void CookieControlsController::OnUiClosing() { + auto* web_contents = GetWebContents(); + if (should_reload_ && web_contents && !web_contents->IsBeingDestroyed()) + web_contents->GetController().Reload(content::ReloadType::NORMAL, true); + should_reload_ = false; +} + +void CookieControlsController::Update(content::WebContents* web_contents) { + DCHECK(web_contents); + if (!tab_observer_ || GetWebContents() != web_contents) + tab_observer_ = std::make_unique<TabObserver>(this, web_contents); + auto status = GetStatus(web_contents); + int blocked_count = GetBlockedCookieCount(); + for (auto& observer : observers_) + observer.OnStatusChanged(status.first, status.second, blocked_count); +} + +std::pair<CookieControlsStatus, CookieControlsEnforcement> +CookieControlsController::GetStatus(content::WebContents* web_contents) { + if (!cookie_settings_->IsCookieControlsEnabled()) { + return {CookieControlsStatus::kDisabled, + CookieControlsEnforcement::kNoEnforcement}; + } + const GURL& url = web_contents->GetURL(); + if (url.SchemeIs(content::kChromeUIScheme) || + url.SchemeIs(kExtensionScheme)) { + return {CookieControlsStatus::kDisabled, + CookieControlsEnforcement::kNoEnforcement}; + } + + SettingSource source; + bool is_allowed = cookie_settings_->IsThirdPartyAccessAllowed( + web_contents->GetURL(), &source); + + CookieControlsStatus status = is_allowed + ? CookieControlsStatus::kDisabledForSite + : CookieControlsStatus::kEnabled; + CookieControlsEnforcement enforcement; + if (source == SETTING_SOURCE_POLICY) { + enforcement = CookieControlsEnforcement::kEnforcedByPolicy; + } else if (is_allowed && original_cookie_settings_ && + original_cookie_settings_->ShouldBlockThirdPartyCookies() && + original_cookie_settings_->IsThirdPartyAccessAllowed( + web_contents->GetURL(), nullptr /* source */)) { + // TODO(crbug.com/1015767): Rules from regular mode can't be temporarily + // overridden in incognito. + enforcement = CookieControlsEnforcement::kEnforcedByCookieSetting; + } else { + enforcement = CookieControlsEnforcement::kNoEnforcement; + } + return {status, enforcement}; +} + +void CookieControlsController::OnCookieBlockingEnabledForSite( + bool block_third_party_cookies) { + if (block_third_party_cookies) { + base::RecordAction(UserMetricsAction("CookieControls.Bubble.TurnOn")); + should_reload_ = false; + cookie_settings_->ResetThirdPartyCookieSetting(GetWebContents()->GetURL()); + } else { + base::RecordAction(UserMetricsAction("CookieControls.Bubble.TurnOff")); + should_reload_ = true; + cookie_settings_->SetThirdPartyCookieSetting( + GetWebContents()->GetURL(), ContentSetting::CONTENT_SETTING_ALLOW); + } +} + +int CookieControlsController::GetBlockedCookieCount() { + auto* tscs = + content_settings::TabSpecificContentSettings::GetForCurrentDocument( + tab_observer_->web_contents()->GetMainFrame()); + if (tscs) { + return tscs->blocked_local_shared_objects().GetObjectCount(); + } else { + return 0; + } +} + +void CookieControlsController::PresentBlockedCookieCounter() { + int blocked_cookies = GetBlockedCookieCount(); + for (auto& observer : observers_) + observer.OnBlockedCookiesCountChanged(blocked_cookies); +} + +void CookieControlsController::OnThirdPartyCookieBlockingChanged( + bool block_third_party_cookies) { + if (GetWebContents()) + Update(GetWebContents()); +} + +void CookieControlsController::OnCookieSettingChanged() { + if (GetWebContents()) + Update(GetWebContents()); +} + +content::WebContents* CookieControlsController::GetWebContents() { + if (!tab_observer_) + return nullptr; + return tab_observer_->web_contents(); +} + +void CookieControlsController::AddObserver(CookieControlsView* obs) { + observers_.AddObserver(obs); +} + +void CookieControlsController::RemoveObserver(CookieControlsView* obs) { + observers_.RemoveObserver(obs); +} + +CookieControlsController::TabObserver::TabObserver( + CookieControlsController* cookie_controls, + content::WebContents* web_contents) + : content_settings::TabSpecificContentSettings::SiteDataObserver( + web_contents), + cookie_controls_(cookie_controls) {} + +void CookieControlsController::TabObserver::OnSiteDataAccessed() { + cookie_controls_->PresentBlockedCookieCounter(); +} + +} // namespace content_settings diff --git a/chromium/components/content_settings/browser/ui/cookie_controls_controller.h b/chromium/components/content_settings/browser/ui/cookie_controls_controller.h new file mode 100644 index 00000000000..f7aeb504d6d --- /dev/null +++ b/chromium/components/content_settings/browser/ui/cookie_controls_controller.h @@ -0,0 +1,106 @@ +// Copyright 2019 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. + +#ifndef COMPONENTS_CONTENT_SETTINGS_BROWSER_UI_COOKIE_CONTROLS_CONTROLLER_H_ +#define COMPONENTS_CONTENT_SETTINGS_BROWSER_UI_COOKIE_CONTROLS_CONTROLLER_H_ + +#include "base/memory/scoped_refptr.h" +#include "base/observer_list.h" +#include "components/content_settings/browser/tab_specific_content_settings.h" +#include "components/content_settings/core/browser/cookie_settings.h" +#include "components/content_settings/core/common/cookie_controls_enforcement.h" +#include "components/content_settings/core/common/cookie_controls_status.h" +#include "components/prefs/pref_change_registrar.h" +#include "content/public/browser/web_contents.h" + +namespace content { +class WebContents; +} // namespace content + +namespace content_settings { + +class CookieSettings; +class CookieControlsView; + +// Handles the tab specific state for cookie controls. +class CookieControlsController : content_settings::CookieSettings::Observer { + public: + CookieControlsController( + scoped_refptr<content_settings::CookieSettings> cookie_settings, + scoped_refptr<content_settings::CookieSettings> original_cookie_settings); + CookieControlsController(const CookieControlsController& other) = delete; + CookieControlsController& operator=(const CookieControlsController& other) = + delete; + ~CookieControlsController() override; + + // Called when the web_contents has changed. + void Update(content::WebContents* web_contents); + + // Called when CookieControlsView is closing. + void OnUiClosing(); + + // Called when the user clicks on the button to enable/disable cookie + // blocking. + void OnCookieBlockingEnabledForSite(bool block_third_party_cookies); + + void AddObserver(CookieControlsView* obs); + void RemoveObserver(CookieControlsView* obs); + + private: + // The observed WebContents changes during the lifetime of the + // CookieControlsController. SiteDataObserver can't change the observed + // object, so we need an inner class that can be recreated when necessary. + // TODO(dullweber): Make it possible to change the observed class and maybe + // convert SiteDataObserver to a pure virtual interface. + class TabObserver + : public content_settings::TabSpecificContentSettings::SiteDataObserver { + public: + TabObserver(CookieControlsController* cookie_controls, + content::WebContents* web_contents); + + // TabSpecificContentSettings::SiteDataObserver: + void OnSiteDataAccessed() override; + + private: + CookieControlsController* cookie_controls_; + + DISALLOW_COPY_AND_ASSIGN(TabObserver); + }; + + void OnThirdPartyCookieBlockingChanged( + bool block_third_party_cookies) override; + void OnCookieSettingChanged() override; + + // Determine the CookieControlsStatus based on |web_contents|. + std::pair<CookieControlsStatus, CookieControlsEnforcement> GetStatus( + content::WebContents* web_contents); + + // Updates the blocked cookie count of |icon_|. + void PresentBlockedCookieCounter(); + + // Returns the number of blocked cookies. + int GetBlockedCookieCount(); + + content::WebContents* GetWebContents(); + + std::unique_ptr<TabObserver> tab_observer_; + scoped_refptr<content_settings::CookieSettings> cookie_settings_; + // Cookie_settings for the original profile associated with + // |cookie_settings_|, if there is one. For example, in Chrome, this + // corresponds to the regular profile when |cookie_settings_| is incognito. + // This may be null. + scoped_refptr<content_settings::CookieSettings> original_cookie_settings_; + + ScopedObserver<content_settings::CookieSettings, + content_settings::CookieSettings::Observer> + cookie_observer_{this}; + + bool should_reload_ = false; + + base::ObserverList<CookieControlsView> observers_; +}; + +} // namespace content_settings + +#endif // COMPONENTS_CONTENT_SETTINGS_BROWSER_UI_COOKIE_CONTROLS_CONTROLLER_H_ diff --git a/chromium/components/content_settings/browser/ui/cookie_controls_view.h b/chromium/components/content_settings/browser/ui/cookie_controls_view.h new file mode 100644 index 00000000000..f833db0b6d7 --- /dev/null +++ b/chromium/components/content_settings/browser/ui/cookie_controls_view.h @@ -0,0 +1,25 @@ +// Copyright 2019 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. + +#ifndef COMPONENTS_CONTENT_SETTINGS_BROWSER_UI_COOKIE_CONTROLS_VIEW_H_ +#define COMPONENTS_CONTENT_SETTINGS_BROWSER_UI_COOKIE_CONTROLS_VIEW_H_ + +#include "base/observer_list_types.h" +#include "components/content_settings/core/common/cookie_controls_enforcement.h" +#include "components/content_settings/core/common/cookie_controls_status.h" + +namespace content_settings { + +// Interface for the CookieControls UI. +class CookieControlsView : public base::CheckedObserver { + public: + virtual void OnStatusChanged(CookieControlsStatus status, + CookieControlsEnforcement enforcement, + int blocked_cookies) = 0; + virtual void OnBlockedCookiesCountChanged(int blocked_cookies) = 0; +}; + +} // namespace content_settings + +#endif // COMPONENTS_CONTENT_SETTINGS_BROWSER_UI_COOKIE_CONTROLS_VIEW_H_ |