| // Copyright 2021 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/embedder_support/content_settings_utils.h" |
| |
| #include "base/feature_list.h" |
| #include "components/content_settings/browser/page_specific_content_settings.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/content_settings_utils.h" |
| #include "components/content_settings/core/common/cookie_settings_base.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "net/base/features.h" |
| #include "net/cookies/cookie_partition_key.h" |
| #include "net/cookies/cookie_setting_override.h" |
| #include "net/cookies/cookie_util.h" |
| #include "net/cookies/site_for_cookies.h" |
| #include "url/gurl.h" |
| #include "url/origin.h" |
| |
| namespace embedder_support { |
| |
| using StorageType = |
| content_settings::mojom::ContentSettingsManager::StorageType; |
| |
| namespace { |
| |
| // If storage partitioning is active, third-party partitioned storage is |
| // allowed by default, and access is only blocked due to general third-party |
| // cookie blocking (and not due to a user specified pattern) then storage |
| // access can be allowed. |
| bool PartitionedStorageByDefaultAllowed( |
| const content_settings::CookieSettingsBase::CookieSettingWithMetadata& |
| cookie_settings) { |
| return base::FeatureList::IsEnabled( |
| net::features::kThirdPartyStoragePartitioning) && |
| base::FeatureList::IsEnabled( |
| net::features::kThirdPartyPartitionedStorageAllowedByDefault) && |
| cookie_settings.BlockedByThirdPartyCookieBlocking(); |
| } |
| |
| bool AllowWorkerStorageAccess( |
| StorageType storage_type, |
| const GURL& url, |
| const std::vector<content::GlobalRenderFrameHostId>& render_frames, |
| const content_settings::CookieSettings* cookie_settings, |
| const blink::StorageKey& storage_key) { |
| // TODO(crbug.com/40247160): Consider whether the following check should |
| // somehow determine real CookieSettingOverrides rather than default to none. |
| content_settings::CookieSettingsBase::CookieSettingWithMetadata |
| cookie_settings_metadata; |
| |
| bool allow = cookie_settings->IsFullCookieAccessAllowed( |
| url, net::SiteForCookies::FromUrl(url), url::Origin::Create(url), |
| net::CookieSettingOverrides(), storage_key.ToCookiePartitionKey(), |
| &cookie_settings_metadata); |
| |
| if (!allow && PartitionedStorageByDefaultAllowed(cookie_settings_metadata)) { |
| allow = true; |
| } |
| |
| // Allow storage when --test-third-party-cookie-phaseout is used, but ensure |
| // that only partitioned storage is available. This developer flag is meant to |
| // simulate Chrome's behavior when 3P cookies are turned down to help |
| // developers test their site. |
| if (!allow && net::cookie_util::IsForceThirdPartyCookieBlockingEnabled()) { |
| allow = true; |
| } |
| |
| for (const auto& it : render_frames) { |
| auto* rfh = content::RenderFrameHost::FromID(it); |
| if (!rfh) { |
| continue; |
| } |
| content_settings::PageSpecificContentSettings::StorageAccessed( |
| storage_type, it, rfh->GetStorageKey(), !allow); |
| } |
| |
| return allow; |
| } |
| } // namespace |
| |
| content::AllowServiceWorkerResult AllowServiceWorker( |
| const GURL& scope, |
| const net::SiteForCookies& site_for_cookies, |
| const std::optional<url::Origin>& top_frame_origin, |
| const blink::StorageKey& storage_key, |
| const content_settings::CookieSettings* cookie_settings, |
| const HostContentSettingsMap* settings_map) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| // TODO(crbug.com/40847840): Remove this check once we figure out what is |
| // wrong. |
| DCHECK(settings_map); |
| GURL first_party_url = top_frame_origin ? top_frame_origin->GetURL() : GURL(); |
| // Check if JavaScript is allowed. |
| content_settings::SettingInfo info; |
| ContentSetting setting = settings_map->GetContentSetting( |
| first_party_url, first_party_url, ContentSettingsType::JAVASCRIPT, &info); |
| bool allow_javascript = setting == CONTENT_SETTING_ALLOW; |
| |
| // We need to manually create a cookie_partition_key without a nonce since |
| // nonced contexts (e.g. FencedFrames or Credentialless iFrames) do not have |
| // unpartitioned cookie access, BUT may allow Service Workers. This nonceless |
| // cookie_partition_key allows this function to return the correct result for |
| // such contexts. |
| std::optional<const net::CookiePartitionKey> cookie_partition_key = |
| net::CookiePartitionKey::FromStorageKeyComponents( |
| storage_key.top_level_site(), |
| net::CookiePartitionKey::BoolToAncestorChainBit( |
| storage_key.IsThirdPartyContext()), |
| /*nonce=*/std::nullopt); |
| |
| // Check if cookies are allowed. Storage Access API grants and Top-Level |
| // Storage Access API grants may only be considered if storage is partitioned |
| // (or if Storage Access API is intended to grant access to storage - which is |
| // a deviation from the spec, but at least one embedder wants that ability). |
| // TODO(crbug.com/40247160): Consider whether the following check should |
| // also consider the third-party cookie user bypass override. |
| content_settings::CookieSettingsBase::CookieSettingWithMetadata |
| cookie_settings_metadata; |
| |
| bool allow_cookies = cookie_settings->IsFullCookieAccessAllowed( |
| scope, site_for_cookies, top_frame_origin, net::CookieSettingOverrides(), |
| cookie_partition_key, |
| |
| &cookie_settings_metadata); |
| |
| if (!allow_cookies && |
| PartitionedStorageByDefaultAllowed(cookie_settings_metadata)) { |
| allow_cookies = true; |
| } |
| |
| // Allow storage when --test-third-party-cookie-phaseout is used, but ensure |
| // that only partitioned storage is available. This developer flag is meant to |
| // simulate Chrome's behavior when 3P cookies are turned down to help |
| // developers test their site. |
| if (!allow_cookies && |
| net::cookie_util::IsForceThirdPartyCookieBlockingEnabled()) { |
| allow_cookies = true; |
| } |
| |
| return content::AllowServiceWorkerResult::FromPolicy(!allow_javascript, |
| !allow_cookies); |
| } |
| |
| bool AllowSharedWorker( |
| const GURL& worker_url, |
| const net::SiteForCookies& site_for_cookies, |
| const std::optional<url::Origin>& top_frame_origin, |
| const std::string& name, |
| const blink::StorageKey& storage_key, |
| const blink::mojom::SharedWorkerSameSiteCookies same_site_cookies, |
| int render_process_id, |
| int render_frame_id, |
| const content_settings::CookieSettings* cookie_settings) { |
| content_settings::CookieSettingsBase::CookieSettingWithMetadata |
| cookie_settings_metadata; |
| |
| // We need to manually create a cookie_partition_key without a nonce since |
| // nonced contexts (e.g. FencedFrames or Credentialless iFrames) do not have |
| // unpartitioned cookie access, BUT may allow Shared Workers. This nonceless |
| // cookie_partition_key allows this function to return the correct result for |
| // such contexts. |
| std::optional<const net::CookiePartitionKey> cookie_partition_key = |
| net::CookiePartitionKey::FromStorageKeyComponents( |
| storage_key.top_level_site(), |
| net::CookiePartitionKey::BoolToAncestorChainBit( |
| storage_key.IsThirdPartyContext()), |
| /*nonce=*/std::nullopt); |
| |
| bool allow = cookie_settings->IsFullCookieAccessAllowed( |
| worker_url, site_for_cookies, top_frame_origin, |
| net::CookieSettingOverrides(), cookie_partition_key, |
| &cookie_settings_metadata); |
| |
| if (!allow && PartitionedStorageByDefaultAllowed(cookie_settings_metadata)) { |
| allow = true; |
| } |
| |
| // Allow storage when --test-third-party-cookie-phaseout is used, but ensure |
| // that only partitioned storage is available. This developer flag is meant to |
| // simulate Chrome's behavior when 3P cookies are turned down to help |
| // developers test their site. |
| if (!allow && net::cookie_util::IsForceThirdPartyCookieBlockingEnabled()) { |
| allow = true; |
| } |
| |
| content_settings::PageSpecificContentSettings::SharedWorkerAccessed( |
| render_process_id, render_frame_id, worker_url, name, storage_key, |
| same_site_cookies, !allow); |
| return allow; |
| } |
| |
| bool AllowWorkerFileSystem( |
| const GURL& url, |
| const std::vector<content::GlobalRenderFrameHostId>& render_frames, |
| const content_settings::CookieSettings* cookie_settings, |
| const blink::StorageKey& storage_key) { |
| return AllowWorkerStorageAccess(StorageType::FILE_SYSTEM, url, render_frames, |
| cookie_settings, storage_key); |
| } |
| |
| bool AllowWorkerIndexedDB( |
| const GURL& url, |
| const std::vector<content::GlobalRenderFrameHostId>& render_frames, |
| const content_settings::CookieSettings* cookie_settings, |
| const blink::StorageKey& storage_key) { |
| return AllowWorkerStorageAccess(StorageType::INDEXED_DB, url, render_frames, |
| cookie_settings, storage_key); |
| } |
| |
| bool AllowWorkerCacheStorage( |
| const GURL& url, |
| const std::vector<content::GlobalRenderFrameHostId>& render_frames, |
| const content_settings::CookieSettings* cookie_settings, |
| const blink::StorageKey& storage_key) { |
| return AllowWorkerStorageAccess(StorageType::CACHE, url, render_frames, |
| cookie_settings, storage_key); |
| } |
| |
| bool AllowWorkerWebLocks( |
| const GURL& url, |
| const content_settings::CookieSettings* cookie_settings, |
| const blink::StorageKey& storage_key) { |
| return AllowWorkerStorageAccess(StorageType::WEB_LOCKS, url, {}, |
| cookie_settings, storage_key); |
| } |
| |
| } // namespace embedder_support |