diff options
Diffstat (limited to 'chromium/components/content_settings')
44 files changed, 1649 insertions, 545 deletions
diff --git a/chromium/components/content_settings/android/BUILD.gn b/chromium/components/content_settings/android/BUILD.gn index 05939eea464..1c5229bc235 100644 --- a/chromium/components/content_settings/android/BUILD.gn +++ b/chromium/components/content_settings/android/BUILD.gn @@ -5,28 +5,43 @@ import("//build/config/android/rules.gni") generate_jni("content_settings_jni_headers") { - sources = [ "java/src/org/chromium/components/content_settings/ContentSettingsFeatureList.java" ] + sources = [ + "java/src/org/chromium/components/content_settings/ContentSettingsFeatureList.java", + "java/src/org/chromium/components/content_settings/CookieControlsBridge.java", + ] } android_library("java") { sources = [ "java/src/org/chromium/components/content_settings/ContentSettingsFeatureList.java", + "java/src/org/chromium/components/content_settings/CookieControlsBridge.java", "java/src/org/chromium/components/content_settings/CookieControlsObserver.java", ] deps = [ ":content_settings_enums_java", "//base:base_java", "//base:jni_java", + "//components/embedder_support/android:browser_context_java", + "//content/public/android:content_java", ] annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ] } source_set("android") { - sources = [ "content_settings_feature_list.cc" ] + sources = [ + "content_settings_feature_list.cc", + "cookie_controls_bridge.cc", + "cookie_controls_bridge.h", + ] deps = [ ":content_settings_jni_headers", "//base", + "//components/content_settings/browser/ui", + "//components/content_settings/core/browser", "//components/content_settings/core/common", + "//components/embedder_support/android:browser_context", + "//components/permissions", + "//content/public/browser", ] } @@ -45,3 +60,9 @@ java_cpp_enum("content_settings_enums_javagen") { ] visibility = [ ":*" ] # Depend on through :content_settings_enums_java } + +java_cpp_strings("java_pref_names_srcjar") { + sources = [ "//components/content_settings/core/common/pref_names.cc" ] + + template = "java_templates/PrefNames.java.tmpl" +} diff --git a/chromium/components/content_settings/android/DEPS b/chromium/components/content_settings/android/DEPS new file mode 100644 index 00000000000..d722cb7774c --- /dev/null +++ b/chromium/components/content_settings/android/DEPS @@ -0,0 +1,6 @@ +include_rules = [ + "+components/embedder_support/android", + "+components/permissions", + "+content/public/android", + "+content/public/browser", +] diff --git a/chromium/components/content_settings/android/cookie_controls_bridge.cc b/chromium/components/content_settings/android/cookie_controls_bridge.cc new file mode 100644 index 00000000000..6c243e8e07e --- /dev/null +++ b/chromium/components/content_settings/android/cookie_controls_bridge.cc @@ -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. + +#include "components/content_settings/android/cookie_controls_bridge.h" + +#include <memory> + +#include "components/content_settings/android/content_settings_jni_headers/CookieControlsBridge_jni.h" +#include "components/content_settings/core/browser/cookie_settings.h" +#include "components/embedder_support/android/browser_context/browser_context_handle.h" +#include "components/permissions/permissions_client.h" +#include "content/public/browser/browser_context.h" + +namespace content_settings { + +using base::android::JavaParamRef; + +CookieControlsBridge::CookieControlsBridge( + JNIEnv* env, + const JavaParamRef<jobject>& obj, + const base::android::JavaParamRef<jobject>& jweb_contents_android, + const base::android::JavaParamRef<jobject>& + joriginal_browser_context_handle) + : jobject_(obj) { + content::WebContents* web_contents = + content::WebContents::FromJavaWebContents(jweb_contents_android); + content::BrowserContext* original_context = + browser_context::BrowserContextFromJavaHandle( + joriginal_browser_context_handle); + auto* permissions_client = permissions::PermissionsClient::Get(); + controller_ = std::make_unique<CookieControlsController>( + permissions_client->GetCookieSettings(web_contents->GetBrowserContext()), + original_context ? permissions_client->GetCookieSettings(original_context) + : nullptr); + observer_.Add(controller_.get()); + controller_->Update(web_contents); +} + +void CookieControlsBridge::OnStatusChanged( + CookieControlsStatus new_status, + CookieControlsEnforcement new_enforcement, + int blocked_cookies) { + if (status_ != new_status || enforcement_ != new_enforcement) { + status_ = new_status; + enforcement_ = new_enforcement; + JNIEnv* env = base::android::AttachCurrentThread(); + // Only call status callback if status has changed + Java_CookieControlsBridge_onCookieBlockingStatusChanged( + env, jobject_, static_cast<int>(status_), + static_cast<int>(enforcement_)); + } + + OnBlockedCookiesCountChanged(blocked_cookies); +} + +void CookieControlsBridge::OnBlockedCookiesCountChanged(int blocked_cookies) { + // The blocked cookie count changes quite frequently, so avoid unnecessary + // UI updates if possible. + if (blocked_cookies_ == blocked_cookies) + return; + + blocked_cookies_ = blocked_cookies; + JNIEnv* env = base::android::AttachCurrentThread(); + Java_CookieControlsBridge_onBlockedCookiesCountChanged( + env, jobject_, blocked_cookies_.value_or(0)); +} + +void CookieControlsBridge::SetThirdPartyCookieBlockingEnabledForSite( + JNIEnv* env, + bool block_cookies) { + controller_->OnCookieBlockingEnabledForSite(block_cookies); +} + +void CookieControlsBridge::OnUiClosing(JNIEnv* env) { + controller_->OnUiClosing(); +} + +CookieControlsBridge::~CookieControlsBridge() = default; + +void CookieControlsBridge::Destroy(JNIEnv* env, + const JavaParamRef<jobject>& obj) { + delete this; +} + +jboolean JNI_CookieControlsBridge_IsCookieControlsEnabled( + JNIEnv* env, + const base::android::JavaParamRef<jobject>& jbrowser_context_handle) { + content::BrowserContext* context = + browser_context::BrowserContextFromJavaHandle(jbrowser_context_handle); + return permissions::PermissionsClient::Get() + ->GetCookieSettings(context) + ->IsCookieControlsEnabled(); +} + +static jlong JNI_CookieControlsBridge_Init( + JNIEnv* env, + const JavaParamRef<jobject>& obj, + const base::android::JavaParamRef<jobject>& jweb_contents_android, + const base::android::JavaParamRef<jobject>& + joriginal_browser_context_handle) { + return reinterpret_cast<intptr_t>(new CookieControlsBridge( + env, obj, jweb_contents_android, joriginal_browser_context_handle)); +} + +} // namespace content_settings diff --git a/chromium/components/content_settings/android/cookie_controls_bridge.h b/chromium/components/content_settings/android/cookie_controls_bridge.h new file mode 100644 index 00000000000..f22a0905bd6 --- /dev/null +++ b/chromium/components/content_settings/android/cookie_controls_bridge.h @@ -0,0 +1,59 @@ +// 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_ANDROID_COOKIE_CONTROLS_BRIDGE_H_ +#define COMPONENTS_CONTENT_SETTINGS_ANDROID_COOKIE_CONTROLS_BRIDGE_H_ + +#include "base/android/jni_weak_ref.h" +#include "base/optional.h" +#include "components/content_settings/browser/ui/cookie_controls_controller.h" +#include "components/content_settings/browser/ui/cookie_controls_view.h" +#include "components/content_settings/core/common/cookie_controls_status.h" + +namespace content_settings { + +// Communicates between CookieControlsController (C++ backend) and PageInfoView +// (Java UI). +class CookieControlsBridge : public CookieControlsView { + public: + // Creates a CookeControlsBridge for interaction with a + // CookieControlsController. + CookieControlsBridge( + JNIEnv* env, + const base::android::JavaParamRef<jobject>& obj, + const base::android::JavaParamRef<jobject>& jweb_contents_android, + const base::android::JavaParamRef<jobject>& + joriginal_browser_context_handle); + + ~CookieControlsBridge() override; + + // Called by the Java counterpart when it is getting garbage collected. + void Destroy(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj); + + void SetThirdPartyCookieBlockingEnabledForSite(JNIEnv* env, + bool block_cookies); + + void OnUiClosing(JNIEnv* env); + + // CookieControlsView: + void OnStatusChanged(CookieControlsStatus status, + CookieControlsEnforcement enforcement, + int blocked_cookies) override; + void OnBlockedCookiesCountChanged(int blocked_cookies) override; + + private: + base::android::ScopedJavaGlobalRef<jobject> jobject_; + CookieControlsStatus status_ = CookieControlsStatus::kUninitialized; + CookieControlsEnforcement enforcement_ = + CookieControlsEnforcement::kNoEnforcement; + base::Optional<int> blocked_cookies_; + std::unique_ptr<CookieControlsController> controller_; + ScopedObserver<CookieControlsController, CookieControlsView> observer_{this}; + + DISALLOW_COPY_AND_ASSIGN(CookieControlsBridge); +}; + +} // namespace content_settings + +#endif // COMPONENTS_CONTENT_SETTINGS_ANDROID_COOKIE_CONTROLS_BRIDGE_H_ diff --git a/chromium/components/content_settings/android/java/src/org/chromium/components/content_settings/CookieControlsBridge.java b/chromium/components/content_settings/android/java/src/org/chromium/components/content_settings/CookieControlsBridge.java new file mode 100644 index 00000000000..2e23b96ba65 --- /dev/null +++ b/chromium/components/content_settings/android/java/src/org/chromium/components/content_settings/CookieControlsBridge.java @@ -0,0 +1,86 @@ +// 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. + +package org.chromium.components.content_settings; + +import androidx.annotation.Nullable; + +import org.chromium.base.annotations.CalledByNative; +import org.chromium.base.annotations.JNINamespace; +import org.chromium.base.annotations.NativeMethods; +import org.chromium.components.embedder_support.browser_context.BrowserContextHandle; +import org.chromium.content_public.browser.WebContents; + +/** + * Communicates between CookieControlsController (C++ backend) and PageInfoView (Java UI). + */ +@JNINamespace("content_settings") +public class CookieControlsBridge { + private long mNativeCookieControlsBridge; + private CookieControlsObserver mObserver; + + /** + * Initializes a CookieControlsBridge instance. + * @param observer An observer to call with updates from the cookie controller. + * @param webContents The WebContents instance to observe. + * @param originalBrowserContext The "original" browser context. In Chrome, this corresponds to + * the regular profile when webContents is incognito. + */ + public CookieControlsBridge(CookieControlsObserver observer, WebContents webContents, + @Nullable BrowserContextHandle originalBrowserContext) { + mObserver = observer; + mNativeCookieControlsBridge = CookieControlsBridgeJni.get().init( + CookieControlsBridge.this, webContents, originalBrowserContext); + } + + public void setThirdPartyCookieBlockingEnabledForSite(boolean blockCookies) { + if (mNativeCookieControlsBridge != 0) { + CookieControlsBridgeJni.get().setThirdPartyCookieBlockingEnabledForSite( + mNativeCookieControlsBridge, blockCookies); + } + } + + public void onUiClosing() { + if (mNativeCookieControlsBridge != 0) { + CookieControlsBridgeJni.get().onUiClosing(mNativeCookieControlsBridge); + } + } + + /** + * Destroys the native counterpart of this class. + */ + public void destroy() { + if (mNativeCookieControlsBridge != 0) { + CookieControlsBridgeJni.get().destroy( + mNativeCookieControlsBridge, CookieControlsBridge.this); + mNativeCookieControlsBridge = 0; + } + } + + public static boolean isCookieControlsEnabled(BrowserContextHandle handle) { + return CookieControlsBridgeJni.get().isCookieControlsEnabled(handle); + } + + @CalledByNative + private void onCookieBlockingStatusChanged( + @CookieControlsStatus int status, @CookieControlsEnforcement int enforcement) { + mObserver.onCookieBlockingStatusChanged(status, enforcement); + } + + @CalledByNative + private void onBlockedCookiesCountChanged(int blockedCookies) { + mObserver.onBlockedCookiesCountChanged(blockedCookies); + } + + @NativeMethods + interface Natives { + long init(CookieControlsBridge caller, WebContents webContents, + BrowserContextHandle originalContextHandle); + void destroy(long nativeCookieControlsBridge, CookieControlsBridge caller); + void setThirdPartyCookieBlockingEnabledForSite( + long nativeCookieControlsBridge, boolean blockCookies); + void onUiClosing(long nativeCookieControlsBridge); + boolean isCookieControlsEnabled(BrowserContextHandle browserContextHandle); + } +} diff --git a/chromium/components/content_settings/android/java_templates/PrefNames.java.tmpl b/chromium/components/content_settings/android/java_templates/PrefNames.java.tmpl new file mode 100644 index 00000000000..0fc897821bb --- /dev/null +++ b/chromium/components/content_settings/android/java_templates/PrefNames.java.tmpl @@ -0,0 +1,14 @@ +// 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. + +package org.chromium.components.content_settings; + +/** Contains content settings related preference names (keys). */ +public final class PrefNames {{ + +{NATIVE_STRINGS} + + // Prevents instantiation. + private PrefNames() {{}} +}} 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_ diff --git a/chromium/components/content_settings/core/browser/BUILD.gn b/chromium/components/content_settings/core/browser/BUILD.gn index f4d4b5ab6d9..6d543a430d7 100644 --- a/chromium/components/content_settings/core/browser/BUILD.gn +++ b/chromium/components/content_settings/core/browser/BUILD.gn @@ -30,6 +30,7 @@ jumbo_static_library("browser") { "content_settings_pref.h", "content_settings_pref_provider.cc", "content_settings_pref_provider.h", + "content_settings_provider.cc", "content_settings_provider.h", "content_settings_registry.cc", "content_settings_registry.h", diff --git a/chromium/components/content_settings/core/browser/content_settings_policy_provider.cc b/chromium/components/content_settings/core/browser/content_settings_policy_provider.cc index e3a67aabb60..1b5f782d66b 100644 --- a/chromium/components/content_settings/core/browser/content_settings_policy_provider.cc +++ b/chromium/components/content_settings/core/browser/content_settings_policy_provider.cc @@ -20,16 +20,21 @@ #include "components/content_settings/core/browser/website_settings_info.h" #include "components/content_settings/core/browser/website_settings_registry.h" #include "components/content_settings/core/common/content_settings_pattern.h" +#include "components/content_settings/core/common/features.h" #include "components/content_settings/core/common/pref_names.h" #include "components/pref_registry/pref_registry_syncable.h" #include "components/prefs/pref_service.h" +namespace content_settings { + namespace { struct PrefsForManagedContentSettingsMapEntry { const char* pref_name; ContentSettingsType content_type; ContentSetting setting; + content_settings::WildcardsInPrimaryPattern wildcards_in_primary_pattern = + content_settings::WildcardsInPrimaryPattern::ALLOWED; }; const PrefsForManagedContentSettingsMapEntry @@ -57,9 +62,11 @@ const PrefsForManagedContentSettingsMapEntry {prefs::kManagedNotificationsBlockedForUrls, ContentSettingsType::NOTIFICATIONS, CONTENT_SETTING_BLOCK}, {prefs::kManagedPluginsAllowedForUrls, ContentSettingsType::PLUGINS, - CONTENT_SETTING_ALLOW}, + CONTENT_SETTING_ALLOW, + content_settings::WildcardsInPrimaryPattern::NOT_ALLOWED}, {prefs::kManagedPluginsBlockedForUrls, ContentSettingsType::PLUGINS, - CONTENT_SETTING_BLOCK}, + CONTENT_SETTING_BLOCK, + content_settings::WildcardsInPrimaryPattern::NOT_ALLOWED}, {prefs::kManagedPopupsAllowedForUrls, ContentSettingsType::POPUPS, CONTENT_SETTING_ALLOW}, {prefs::kManagedPopupsBlockedForUrls, ContentSettingsType::POPUPS, @@ -71,9 +78,30 @@ const PrefsForManagedContentSettingsMapEntry {prefs::kManagedLegacyCookieAccessAllowedForDomains, ContentSettingsType::LEGACY_COOKIE_ACCESS, CONTENT_SETTING_ALLOW}}; -} // namespace +class VectorRuleIterator : public RuleIterator { + public: + VectorRuleIterator(const std::vector<Rule>::const_iterator& begin, + const std::vector<Rule>::const_iterator& end) + : current_rule(begin), end_rule(end) {} -namespace content_settings { + ~VectorRuleIterator() override {} + + bool HasNext() const override { return current_rule != end_rule; } + + Rule Next() override { + Rule rule(current_rule->primary_pattern, current_rule->secondary_pattern, + current_rule->value.Clone(), current_rule->expiration, + current_rule->session_model); + current_rule++; + return rule; + } + + private: + std::vector<Rule>::const_iterator current_rule; + std::vector<Rule>::const_iterator end_rule; +}; + +} // namespace // The preferences used to manage the default policy value for // ContentSettingsTypes. @@ -236,6 +264,18 @@ std::unique_ptr<RuleIterator> PolicyProvider::GetRuleIterator( return value_map_.GetRuleIterator(content_type, resource_identifier, &lock_); } +std::unique_ptr<RuleIterator> PolicyProvider::GetDiscardedRuleIterator( + ContentSettingsType content_type, + const ResourceIdentifier& resource_identifier, + bool incognito) const { + auto it = discarded_rules_value_map_.find(content_type); + if (it == discarded_rules_value_map_.end()) { + return std::make_unique<EmptyRuleIterator>(EmptyRuleIterator()); + } + return std::make_unique<VectorRuleIterator>(it->second.begin(), + it->second.end()); +} + void PolicyProvider::GetContentSettingsFromPreferences( OriginIdentifierValueMap* value_map) { for (size_t i = 0; i < base::size(kPrefsForManagedContentSettingsMap); ++i) { @@ -296,6 +336,18 @@ void PolicyProvider::GetContentSettingsFromPreferences( ->Get(content_type) ->SupportsEmbeddedExceptions()); + if (base::FeatureList::IsEnabled( + content_settings::kDisallowWildcardsInPluginContentSettings) && + kPrefsForManagedContentSettingsMap[i].wildcards_in_primary_pattern == + WildcardsInPrimaryPattern::NOT_ALLOWED && + pattern_pair.first.HasHostWildcards()) { + discarded_rules_value_map_[content_type].push_back( + Rule(pattern_pair.first, secondary_pattern, + base::Value(kPrefsForManagedContentSettingsMap[i].setting), + base::Time(), content_settings::SessionModel::Durable)); + continue; + } + // Don't set a timestamp for policy settings. value_map->SetValue( pattern_pair.first, secondary_pattern, content_type, diff --git a/chromium/components/content_settings/core/browser/content_settings_policy_provider.h b/chromium/components/content_settings/core/browser/content_settings_policy_provider.h index 8b9e30d7413..f92702ba187 100644 --- a/chromium/components/content_settings/core/browser/content_settings_policy_provider.h +++ b/chromium/components/content_settings/core/browser/content_settings_policy_provider.h @@ -36,6 +36,11 @@ class PolicyProvider : public ObservableProvider { const ResourceIdentifier& resource_identifier, bool incognito) const override; + std::unique_ptr<RuleIterator> GetDiscardedRuleIterator( + ContentSettingsType content_type, + const ResourceIdentifier& resource_identifier, + bool incognito) const override; + bool SetWebsiteSetting( const ContentSettingsPattern& primary_pattern, const ContentSettingsPattern& secondary_pattern, @@ -73,6 +78,8 @@ class PolicyProvider : public ObservableProvider { OriginIdentifierValueMap value_map_; + std::map<ContentSettingsType, std::vector<Rule>> discarded_rules_value_map_; + PrefService* prefs_; PrefChangeRegistrar pref_change_registrar_; diff --git a/chromium/components/content_settings/core/browser/content_settings_pref.cc b/chromium/components/content_settings/core/browser/content_settings_pref.cc index 9e8c7bf15d7..cd57f1a2a0b 100644 --- a/chromium/components/content_settings/core/browser/content_settings_pref.cc +++ b/chromium/components/content_settings/core/browser/content_settings_pref.cc @@ -10,6 +10,7 @@ #include "base/auto_reset.h" #include "base/bind.h" +#include "base/logging.h" #include "base/metrics/histogram_macros.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" diff --git a/chromium/components/content_settings/core/browser/content_settings_provider.cc b/chromium/components/content_settings/core/browser/content_settings_provider.cc new file mode 100644 index 00000000000..f46f9682a4e --- /dev/null +++ b/chromium/components/content_settings/core/browser/content_settings_provider.cc @@ -0,0 +1,14 @@ +// Copyright (c) 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/core/browser/content_settings_provider.h" + +namespace content_settings { +std::unique_ptr<RuleIterator> ProviderInterface::GetDiscardedRuleIterator( + ContentSettingsType content_type, + const ResourceIdentifier& resource_identifier, + bool incognito) const { + return std::make_unique<EmptyRuleIterator>(); +} +} // namespace content_settings
\ No newline at end of file diff --git a/chromium/components/content_settings/core/browser/content_settings_provider.h b/chromium/components/content_settings/core/browser/content_settings_provider.h index edd22466003..b23e3c7bf80 100644 --- a/chromium/components/content_settings/core/browser/content_settings_provider.h +++ b/chromium/components/content_settings/core/browser/content_settings_provider.h @@ -38,6 +38,16 @@ class ProviderInterface { const ResourceIdentifier& resource_identifier, bool incognito) const = 0; + // Returns a |RuleIterator| over the discarded content setting rules stored + // by this provider. If |incognito| is true, the iterator returns only the + // content settings which are applicable to the incognito mode. Otherwise, + // it returns the content settings which are applicable only to the normal + // mode. + virtual std::unique_ptr<RuleIterator> GetDiscardedRuleIterator( + ContentSettingsType content_type, + const ResourceIdentifier& resource_identifier, + bool incognito) const; + // Asks the provider to set the website setting for a particular // |primary_pattern|, |secondary_pattern|, |content_type| tuple. If the // provider accepts the setting it returns true and takes the ownership of the diff --git a/chromium/components/content_settings/core/browser/content_settings_registry.cc b/chromium/components/content_settings/core/browser/content_settings_registry.cc index 3aa7c4054cf..6941b2f1cba 100644 --- a/chromium/components/content_settings/core/browser/content_settings_registry.cc +++ b/chromium/components/content_settings/core/browser/content_settings_registry.cc @@ -561,7 +561,7 @@ void ContentSettingsRegistry::Init() { WhitelistedSchemes(), ValidSettings(CONTENT_SETTING_ALLOW, CONTENT_SETTING_ASK, CONTENT_SETTING_BLOCK, CONTENT_SETTING_SESSION_ONLY), - WebsiteSettingsInfo::COOKIES_SCOPE, + WebsiteSettingsInfo::REQUESTING_ORIGIN_AND_TOP_LEVEL_ORIGIN_SCOPE, WebsiteSettingsRegistry::ALL_PLATFORMS, ContentSettingsInfo::INHERIT_IN_INCOGNITO, ContentSettingsInfo::PERSISTENT, diff --git a/chromium/components/content_settings/core/browser/content_settings_rule.cc b/chromium/components/content_settings/core/browser/content_settings_rule.cc index 416cbee50c7..20c44d4b911 100644 --- a/chromium/components/content_settings/core/browser/content_settings_rule.cc +++ b/chromium/components/content_settings/core/browser/content_settings_rule.cc @@ -63,4 +63,14 @@ Rule ConcatenationIterator::Next() { return to_return; } +EmptyRuleIterator::~EmptyRuleIterator() = default; + +bool EmptyRuleIterator::HasNext() const { + return false; +} + +Rule EmptyRuleIterator::Next() { + return Rule(); +} + } // namespace content_settings diff --git a/chromium/components/content_settings/core/browser/content_settings_rule.h b/chromium/components/content_settings/core/browser/content_settings_rule.h index ef1c0bec686..c58fe6c8d58 100644 --- a/chromium/components/content_settings/core/browser/content_settings_rule.h +++ b/chromium/components/content_settings/core/browser/content_settings_rule.h @@ -46,6 +46,16 @@ class RuleIterator { virtual Rule Next() = 0; }; +class EmptyRuleIterator : public RuleIterator { + public: + ~EmptyRuleIterator() override; + + protected: + // RuleIterator: + bool HasNext() const override; + Rule Next() override; +}; + class ConcatenationIterator : public RuleIterator { public: // |auto_lock| can be null if no locking is needed. diff --git a/chromium/components/content_settings/core/browser/cookie_settings.cc b/chromium/components/content_settings/core/browser/cookie_settings.cc index 7246be6c667..a7868548eaa 100644 --- a/chromium/components/content_settings/core/browser/cookie_settings.cc +++ b/chromium/components/content_settings/core/browser/cookie_settings.cc @@ -208,6 +208,11 @@ void CookieSettings::GetCookieSettingInternal( ContentSetting setting = ValueToContentSetting(value.get()); bool block = block_third && is_third_party_request; + if (!block) { + FireStorageAccessHistogram( + net::cookie_util::StorageAccessResult::ACCESS_ALLOWED); + } + #if !defined(OS_IOS) // IOS doesn't use blink and as such cannot check our feature flag. Disabling // by default there should be no-op as the lack of Blink also means no grants @@ -221,11 +226,19 @@ void CookieSettings::GetCookieSettingInternal( url, first_party_url, ContentSettingsType::STORAGE_ACCESS, std::string()); - if (setting == CONTENT_SETTING_ALLOW) + if (setting == CONTENT_SETTING_ALLOW) { block = false; + FireStorageAccessHistogram(net::cookie_util::StorageAccessResult:: + ACCESS_ALLOWED_STORAGE_ACCESS_GRANT); + } } #endif + if (block) { + FireStorageAccessHistogram( + net::cookie_util::StorageAccessResult::ACCESS_BLOCKED); + } + *cookie_setting = block ? CONTENT_SETTING_BLOCK : setting; } diff --git a/chromium/components/content_settings/core/browser/cookie_settings_unittest.cc b/chromium/components/content_settings/core/browser/cookie_settings_unittest.cc index 7d1944bafc2..fabcf2b9f19 100644 --- a/chromium/components/content_settings/core/browser/cookie_settings_unittest.cc +++ b/chromium/components/content_settings/core/browser/cookie_settings_unittest.cc @@ -7,6 +7,7 @@ #include <cstddef> #include "base/scoped_observer.h" +#include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" #include "base/values.h" @@ -22,11 +23,16 @@ #include "extensions/buildflags/buildflags.h" #include "net/base/features.h" #include "net/cookies/cookie_constants.h" +#include "net/cookies/cookie_util.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" #if !defined(OS_IOS) #include "third_party/blink/public/common/features.h" +namespace { +constexpr char kAllowedRequestsHistogram[] = + "API.StorageAccess.AllowedRequests"; +} #endif namespace content_settings { @@ -425,6 +431,25 @@ TEST_F(CookieSettingsTest, CookiesBlockEverythingExceptAllowed) { } #if !defined(OS_IOS) +TEST_F(CookieSettingsTest, GetCookieSettingAllowedTelemetry) { + const GURL top_level_url = GURL(kFirstPartySite); + const GURL url = GURL(kAllowedSite); + + prefs_.SetBoolean(prefs::kBlockThirdPartyCookies, false); + + base::HistogramTester histogram_tester; + histogram_tester.ExpectTotalCount(kAllowedRequestsHistogram, 0); + + ContentSetting setting; + cookie_settings_->GetCookieSetting(url, top_level_url, nullptr, &setting); + EXPECT_EQ(setting, CONTENT_SETTING_ALLOW); + histogram_tester.ExpectTotalCount(kAllowedRequestsHistogram, 1); + histogram_tester.ExpectBucketCount( + kAllowedRequestsHistogram, + static_cast<int>(net::cookie_util::StorageAccessResult::ACCESS_ALLOWED), + 1); +} + // When the Storage Access API is disabled, block third party cookie setting // should behave like normal. TEST_F(CookieSettingsTest, GetCookieSettingDisabledSAA) { @@ -453,6 +478,9 @@ TEST_F(CookieSettingsTest, GetCookieSettingDefaultSAA) { const GURL top_level_url = GURL(kFirstPartySite); const GURL url = GURL(kAllowedSite); + base::HistogramTester histogram_tester; + histogram_tester.ExpectTotalCount(kAllowedRequestsHistogram, 0); + prefs_.SetBoolean(prefs::kBlockThirdPartyCookies, true); settings_map_->SetContentSettingCustomScope( @@ -464,6 +492,11 @@ TEST_F(CookieSettingsTest, GetCookieSettingDefaultSAA) { ContentSetting setting; cookie_settings_->GetCookieSetting(url, top_level_url, nullptr, &setting); EXPECT_EQ(setting, CONTENT_SETTING_BLOCK); + histogram_tester.ExpectTotalCount(kAllowedRequestsHistogram, 1); + histogram_tester.ExpectBucketCount( + kAllowedRequestsHistogram, + static_cast<int>(net::cookie_util::StorageAccessResult::ACCESS_BLOCKED), + 1); } // When enabled, the Storage Access API should unblock storage access that would @@ -476,6 +509,9 @@ TEST_F(CookieSettingsTest, GetCookieSettingEnabledSAA) { const GURL url = GURL(kAllowedSite); const GURL third_url = GURL(kBlockedSite); + base::HistogramTester histogram_tester; + histogram_tester.ExpectTotalCount(kAllowedRequestsHistogram, 0); + prefs_.SetBoolean(prefs::kBlockThirdPartyCookies, true); settings_map_->SetContentSettingCustomScope( @@ -490,6 +526,12 @@ TEST_F(CookieSettingsTest, GetCookieSettingEnabledSAA) { ContentSetting setting; cookie_settings_->GetCookieSetting(url, top_level_url, nullptr, &setting); EXPECT_EQ(setting, CONTENT_SETTING_ALLOW); + histogram_tester.ExpectTotalCount(kAllowedRequestsHistogram, 1); + histogram_tester.ExpectBucketCount( + kAllowedRequestsHistogram, + static_cast<int>(net::cookie_util::StorageAccessResult:: + ACCESS_ALLOWED_STORAGE_ACCESS_GRANT), + 1); // Invalid pair the |top_level_url| granting access to |url| is now // being loaded under |url| as the top level url. diff --git a/chromium/components/content_settings/core/browser/host_content_settings_map.cc b/chromium/components/content_settings/core/browser/host_content_settings_map.cc index 7f1d698632c..65d3892343a 100644 --- a/chromium/components/content_settings/core/browser/host_content_settings_map.cc +++ b/chromium/components/content_settings/core/browser/host_content_settings_map.cc @@ -433,6 +433,30 @@ void HostContentSettingsMap::GetSettingsForOneType( } } +void HostContentSettingsMap::GetDiscardedSettingsForOneType( + ContentSettingsType content_type, + const std::string& resource_identifier, + ContentSettingsForOneType* settings) const { + DCHECK(SupportsResourceIdentifier(content_type) || + resource_identifier.empty()); + DCHECK(settings); + UsedContentSettingsProviders(); + + for (const auto& provider_pair : content_settings_providers_) { + std::unique_ptr<content_settings::RuleIterator> discarded_rule_iterator( + provider_pair.second->GetDiscardedRuleIterator( + content_type, resource_identifier, is_off_the_record_)); + while (discarded_rule_iterator->HasNext()) { + content_settings::Rule discarded_rule = discarded_rule_iterator->Next(); + settings->emplace_back( + discarded_rule.primary_pattern, discarded_rule.secondary_pattern, + std::move(discarded_rule.value), + kProviderNamesSourceMap[provider_pair.first].provider_name, + is_off_the_record_, discarded_rule.expiration); + } + } +} + void HostContentSettingsMap::SetDefaultContentSetting( ContentSettingsType content_type, ContentSetting setting) { diff --git a/chromium/components/content_settings/core/browser/host_content_settings_map.h b/chromium/components/content_settings/core/browser/host_content_settings_map.h index 0342ee0957d..94c9e8398c7 100644 --- a/chromium/components/content_settings/core/browser/host_content_settings_map.h +++ b/chromium/components/content_settings/core/browser/host_content_settings_map.h @@ -162,6 +162,11 @@ class HostContentSettingsMap : public content_settings::Observer, base::Optional<content_settings::SessionModel> session_model = base::nullopt) const; + void GetDiscardedSettingsForOneType( + ContentSettingsType content_type, + const std::string& resource_identifier, + ContentSettingsForOneType* settings) const; + // Sets the default setting for a particular content type. This method must // not be invoked on an incognito map. // diff --git a/chromium/components/content_settings/core/browser/uma_util.h b/chromium/components/content_settings/core/browser/uma_util.h index 011f1bc0950..7276e548d6f 100644 --- a/chromium/components/content_settings/core/browser/uma_util.h +++ b/chromium/components/content_settings/core/browser/uma_util.h @@ -5,7 +5,6 @@ #ifndef COMPONENTS_CONTENT_SETTINGS_CORE_BROWSER_UMA_UTIL_H_ #define COMPONENTS_CONTENT_SETTINGS_CORE_BROWSER_UMA_UTIL_H_ -#include "base/logging.h" #include "base/macros.h" #include "components/content_settings/core/common/content_settings.h" diff --git a/chromium/components/content_settings/core/common/content_settings.mojom b/chromium/components/content_settings/core/common/content_settings.mojom index 8ef573d3aaa..cb8b3d2aaa2 100644 --- a/chromium/components/content_settings/core/common/content_settings.mojom +++ b/chromium/components/content_settings/core/common/content_settings.mojom @@ -4,6 +4,7 @@ module content_settings.mojom; +import "mojo/public/mojom/base/time.mojom"; import "mojo/public/mojom/base/values.mojom"; [Native] @@ -65,6 +66,7 @@ struct ContentSettingPatternSource { ContentSettingsPattern primary_pattern; ContentSettingsPattern secondary_pattern; mojo_base.mojom.Value setting_value; + mojo_base.mojom.Time expiration; string source; bool incognito; }; diff --git a/chromium/components/content_settings/core/common/content_settings_mojom_traits.cc b/chromium/components/content_settings/core/common/content_settings_mojom_traits.cc index 7db7683e5a8..3cdb9450d83 100644 --- a/chromium/components/content_settings/core/common/content_settings_mojom_traits.cc +++ b/chromium/components/content_settings/core/common/content_settings_mojom_traits.cc @@ -89,7 +89,7 @@ bool StructTraits<content_settings::mojom::ContentSettingPatternSourceDataView, return data.ReadPrimaryPattern(&out->primary_pattern) && data.ReadSecondaryPattern(&out->secondary_pattern) && data.ReadSettingValue(&out->setting_value) && - data.ReadSource(&out->source); + data.ReadExpiration(&out->expiration) && data.ReadSource(&out->source); } // static diff --git a/chromium/components/content_settings/core/common/content_settings_mojom_traits.h b/chromium/components/content_settings/core/common/content_settings_mojom_traits.h index d342c755a42..2192c55d2b7 100644 --- a/chromium/components/content_settings/core/common/content_settings_mojom_traits.h +++ b/chromium/components/content_settings/core/common/content_settings_mojom_traits.h @@ -103,6 +103,10 @@ struct StructTraits< return r.setting_value; } + static const base::Time& expiration(const ContentSettingPatternSource& r) { + return r.expiration; + } + static const std::string& source(const ContentSettingPatternSource& r) { return r.source; } diff --git a/chromium/components/content_settings/core/common/content_settings_pattern.cc b/chromium/components/content_settings/core/common/content_settings_pattern.cc index b8eacf8deb5..85b7b24701f 100644 --- a/chromium/components/content_settings/core/common/content_settings_pattern.cc +++ b/chromium/components/content_settings/core/common/content_settings_pattern.cc @@ -604,6 +604,10 @@ ContentSettingsPattern::Relation ContentSettingsPattern::Compare( return path_relation; } +bool ContentSettingsPattern::HasHostWildcards() const { + return parts_.has_domain_wildcard; +} + bool ContentSettingsPattern::operator==( const ContentSettingsPattern& other) const { return Compare(other) == IDENTITY; diff --git a/chromium/components/content_settings/core/common/content_settings_pattern.h b/chromium/components/content_settings/core/common/content_settings_pattern.h index 09338d1492e..6d168ef8408 100644 --- a/chromium/components/content_settings/core/common/content_settings_pattern.h +++ b/chromium/components/content_settings/core/common/content_settings_pattern.h @@ -22,6 +22,11 @@ class PatternParser; namespace mojom { class ContentSettingsPatternDataView; } + +enum class WildcardsInPrimaryPattern { + NOT_ALLOWED, + ALLOWED, +}; } // A pattern used in content setting rules. See |IsValid| for a description of @@ -218,6 +223,9 @@ class ContentSettingsPattern { // |Relation| of the two patterns. Relation Compare(const ContentSettingsPattern& other) const; + // True if the host in the pattern has a wildcard. + bool HasHostWildcards() const; + // Returns true if the pattern and the |other| pattern are identical. bool operator==(const ContentSettingsPattern& other) const; diff --git a/chromium/components/content_settings/core/common/content_settings_pattern_unittest.cc b/chromium/components/content_settings/core/common/content_settings_pattern_unittest.cc index 8ae5c634931..636c82e56a2 100644 --- a/chromium/components/content_settings/core/common/content_settings_pattern_unittest.cc +++ b/chromium/components/content_settings/core/common/content_settings_pattern_unittest.cc @@ -808,3 +808,22 @@ TEST(ContentSettingsPatternTest, FileSchemeHasPath) { EXPECT_TRUE(Pattern("file:///foo/bar/").HasPath()); EXPECT_TRUE(Pattern("file:///foo/bar/test.html").HasPath()); } + +TEST(ContentSettingsPatternTest, PatternHasWildcards) { + // scheme wildcard are allowed + EXPECT_FALSE(Pattern("mail.google.com:443/home").HasHostWildcards()); + EXPECT_FALSE(Pattern("*://mail.google.com:443/home").HasHostWildcards()); + // domain wildcard + EXPECT_TRUE(Pattern("https://[*.]google.com:443/home").HasHostWildcards()); + // path wildcard are allowed + EXPECT_FALSE(Pattern("https://mail.google.com:443/*").HasHostWildcards()); + // port wildcards are allowed + EXPECT_FALSE(Pattern("https://mail.google.com/home").HasHostWildcards()); + EXPECT_FALSE(Pattern("https://mail.google.com:*/home").HasHostWildcards()); + // full wildcard pattern + EXPECT_TRUE(Pattern("*").HasHostWildcards()); + // full wildcard pattern + EXPECT_TRUE(ContentSettingsPattern::Wildcard().HasHostWildcards()); + // no wildcards + EXPECT_FALSE(Pattern("https://mail.google.com:443/home").HasHostWildcards()); +} diff --git a/chromium/components/content_settings/core/common/features.cc b/chromium/components/content_settings/core/common/features.cc index 17dceed1a0d..4900f2f794c 100644 --- a/chromium/components/content_settings/core/common/features.cc +++ b/chromium/components/content_settings/core/common/features.cc @@ -21,4 +21,9 @@ const base::FeatureParam<bool> kImprovedCookieControlsDefaultInIncognito{ const base::Feature kImprovedCookieControlsForThirdPartyCookieBlocking{ "ImprovedCookieControlsForThirdPartyCookieBlocking", base::FEATURE_DISABLED_BY_DEFAULT}; -}
\ No newline at end of file + +const base::Feature kDisallowWildcardsInPluginContentSettings{ + "DisallowWildcardsInPluginContentSettings", + base::FEATURE_ENABLED_BY_DEFAULT}; + +} // namespace content_settings
\ No newline at end of file diff --git a/chromium/components/content_settings/core/common/features.h b/chromium/components/content_settings/core/common/features.h index 9d6965be065..162841fe3d2 100644 --- a/chromium/components/content_settings/core/common/features.h +++ b/chromium/components/content_settings/core/common/features.h @@ -26,6 +26,10 @@ extern const base::FeatureParam<bool> kImprovedCookieControlsDefaultInIncognito; COMPONENT_EXPORT(CONTENT_SETTINGS_FEATURES) extern const base::Feature kImprovedCookieControlsForThirdPartyCookieBlocking; +// Feature to disallow wildcard pattern matching for plugin content settings +COMPONENT_EXPORT(CONTENT_SETTINGS_FEATURES) +extern const base::Feature kDisallowWildcardsInPluginContentSettings; + } // namespace content_settings -#endif // COMPONENTS_CONTENT_SETTINGS_CORE_COMMON_FEATURES_H_
\ No newline at end of file +#endif // COMPONENTS_CONTENT_SETTINGS_CORE_COMMON_FEATURES_H_ diff --git a/chromium/components/content_settings/core/common/pref_names.h b/chromium/components/content_settings/core/common/pref_names.h index a4c4bffdbd1..84bf8ba3d83 100644 --- a/chromium/components/content_settings/core/common/pref_names.h +++ b/chromium/components/content_settings/core/common/pref_names.h @@ -5,7 +5,6 @@ #ifndef COMPONENTS_CONTENT_SETTINGS_CORE_COMMON_PREF_NAMES_H_ #define COMPONENTS_CONTENT_SETTINGS_CORE_COMMON_PREF_NAMES_H_ -#include "base/logging.h" namespace prefs { diff --git a/chromium/components/content_settings/renderer/content_settings_agent_impl.cc b/chromium/components/content_settings/renderer/content_settings_agent_impl.cc index 33389449005..660223c0bf2 100644 --- a/chromium/components/content_settings/renderer/content_settings_agent_impl.cc +++ b/chromium/components/content_settings/renderer/content_settings_agent_impl.cc @@ -214,30 +214,29 @@ void ContentSettingsAgentImpl::BindContentSettingsManager( } void ContentSettingsAgentImpl::DidCommitProvisionalLoad( - bool is_same_document_navigation, ui::PageTransition transition) { blink::WebLocalFrame* frame = render_frame()->GetWebFrame(); if (frame->Parent()) return; // Not a top-level navigation. - if (!is_same_document_navigation) { - // Clear "block" flags for the new page. This needs to happen before any of - // |allowScript()|, |allowScriptFromSource()|, |allowImage()|, or - // |allowPlugins()| is called for the new page so that these functions can - // correctly detect that a piece of content flipped from "not blocked" to - // "blocked". - ClearBlockedContentSettings(); - - // The BrowserInterfaceBroker is reset on navigation, so we will need to - // re-acquire the ContentSettingsManager. - content_settings_manager_.reset(); - } + // Clear "block" flags for the new page. This needs to happen before any of + // |allowScript()|, |allowScriptFromSource()|, |allowImage()|, or + // |allowPlugins()| is called for the new page so that these functions can + // correctly detect that a piece of content flipped from "not blocked" to + // "blocked". + ClearBlockedContentSettings(); + + // The BrowserInterfaceBroker is reset on navigation, so we will need to + // re-acquire the ContentSettingsManager. + content_settings_manager_.reset(); +#if DCHECK_IS_ON() GURL url = frame->GetDocument().Url(); // If we start failing this DCHECK, please makes sure we don't regress // this bug: http://code.google.com/p/chromium/issues/detail?id=79304 DCHECK(frame->GetDocument().GetSecurityOrigin().ToString() == "null" || !url.SchemeIs(url::kDataScheme)); +#endif } void ContentSettingsAgentImpl::OnDestruct() { diff --git a/chromium/components/content_settings/renderer/content_settings_agent_impl.h b/chromium/components/content_settings/renderer/content_settings_agent_impl.h index 2522fdb1971..3484035136c 100644 --- a/chromium/components/content_settings/renderer/content_settings_agent_impl.h +++ b/chromium/components/content_settings/renderer/content_settings_agent_impl.h @@ -137,8 +137,7 @@ class ContentSettingsAgentImpl ContentSettingsInterstitialPages); // RenderFrameObserver implementation. - void DidCommitProvisionalLoad(bool is_same_document_navigation, - ui::PageTransition transition) override; + void DidCommitProvisionalLoad(ui::PageTransition transition) override; void OnDestruct() override; // mojom::ContentSettingsAgent: diff --git a/chromium/components/content_settings/renderer/content_settings_agent_impl_browsertest.cc b/chromium/components/content_settings/renderer/content_settings_agent_impl_browsertest.cc index ad56e6f35e4..c0e36cd97fa 100644 --- a/chromium/components/content_settings/renderer/content_settings_agent_impl_browsertest.cc +++ b/chromium/components/content_settings/renderer/content_settings_agent_impl_browsertest.cc @@ -152,8 +152,7 @@ class CommitTimeConditionChecker : public content::RenderFrameObserver { protected: // RenderFrameObserver: void OnDestruct() override {} - void DidCommitProvisionalLoad(bool is_same_document_navigation, - ui::PageTransition transition) override { + void DidCommitProvisionalLoad(ui::PageTransition transition) override { EXPECT_EQ(expectation_, predicate_.Run()); } |