chromium/content/browser/back_forward_cache_no_store_browsertest.cc

// 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 "content/browser/back_forward_cache_browsertest.h"

#include <memory>

#include "build/build_config.h"
#include "build/buildflag.h"
#include "build/chromecast_buildflags.h"
#include "content/browser/renderer_host/navigation_request.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/test_navigation_observer.h"
#include "content/shell/browser/shell.h"
#include "net/test/embedded_test_server/controllable_http_response.h"
#include "net/test/spawned_test_server/spawned_test_server.h"
#include "net/test/test_data_directory.h"
#include "third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h"

// This file contains back-/forward-cache tests for the
// `Cache-control: no-store` header. It was forked from
// https://source.chromium.org/chromium/chromium/src/+/main:content/browser/back_forward_cache_browsertest.cc;drc=b339487e39ad6ae93af30fa8fcb37dc61bd138ec
//
// When adding tests please also add WPTs. See
// third_party/blink/web_tests/external/wpt/html/browsers/browsing-the-web/back-forward-cache/README.md

namespace content {

NotRestoredReason;
NotRestoredReasons;
BlocklistedFeature;

namespace {

const char kResponseWithNoCache[] =;

}  // namespace

// TODO(crbug.com/40285326): This fails with the field trial testing config.
class BackForwardCacheBrowserTestNoTestingConfig
    : public BackForwardCacheBrowserTest {};

IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTestNoTestingConfig,
                       MainFrameWithNoStoreNotCached) {}

IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTestNoTestingConfig,
                       SubframeWithNoStoreCached) {}

// When CCNS is present and WebSocket is used, both features should be recorded
// and the test should not hit CHECK.
// TODO(crbug.com/40241677): WebSocket server is flaky Android.
#if BUILDFLAG(IS_ANDROID)
#define MAYBE_CCNSAndWebSocketBothRecorded
#else
#define MAYBE_CCNSAndWebSocketBothRecorded
#endif
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTestNoTestingConfig,
                       MAYBE_CCNSAndWebSocketBothRecorded) {}

namespace {

class BackForwardCacheBrowserTestAllowCacheControlNoStore
    : public BackForwardCacheBrowserTest {};

}  // namespace

// Test that a page with cache-control:no-store enters bfcache with the flag on,
// but does not get restored and gets evicted.
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTestAllowCacheControlNoStore,
                       PagesWithCacheControlNoStoreEnterBfcacheAndEvicted) {}

// Test that a page with cache-control:no-store enters bfcache with the flag on,
// and if a cookie is modified while it is in bfcache via JavaScript, gets
// evicted with cookie modified marked.
IN_PROC_BROWSER_TEST_F(
    BackForwardCacheBrowserTestAllowCacheControlNoStore,
    PagesWithCacheControlNoStoreCookieModifiedThroughJavaScript) {}

// Test that a page with cache-control:no-store enters bfcache with the flag on,
// and if a cookie is modified, it gets evicted with cookie changed, but if
// navigated away again and navigated back, it gets evicted without cookie
// change marked.
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTestAllowCacheControlNoStore,
                       PagesWithCacheControlNoStoreCookieModifiedBackTwice) {}

// Test that a page with cache-control:no-store enters bfcache with the flag on,
// and even if a cookie is modified on a different domain than the entry, the
// entry is not marked as cookie modified.
IN_PROC_BROWSER_TEST_F(
    BackForwardCacheBrowserTestAllowCacheControlNoStore,
    PagesWithCacheControlNoStoreCookieModifiedThroughJavaScriptOnDifferentDomain) {}

// Test that a page with cache-control:no-store records other not restored
// reasons along with kCacheControlNoStore when eviction happens.
IN_PROC_BROWSER_TEST_F(
    BackForwardCacheBrowserTestAllowCacheControlNoStore,
    PagesWithCacheControlNoStoreRecordOtherReasonsWhenEvictionHappens) {}

// Test that a page with cache-control:no-store records other not restored
// reasons along with kCacheControlNoStore when there are other blocking reasons
// upon entering bfcache.
IN_PROC_BROWSER_TEST_F(
    BackForwardCacheBrowserTestAllowCacheControlNoStore,
    PagesWithCacheControlNoStoreRecordOtherReasonsUponEntrance) {}

// Test that a page with cache-control:no-store records eviction reasons along
// with kCacheControlNoStore when the entry is evicted for other reasons.
IN_PROC_BROWSER_TEST_F(
    BackForwardCacheBrowserTestAllowCacheControlNoStore,
    PagesWithCacheControlNoStoreRecordOtherReasonsForEviction) {}

namespace {
const char kResponseWithNoCacheWithCookie[] =;

const char kResponseWithNoCacheWithHTTPOnlyCookie[] =;

const char kResponseWithNoCacheWithHTTPOnlyCookie2[] =;

const char kResponseWithNoCacheWithRedirectionWithHTTPOnlyCookie[] =;
}  // namespace

// Test that a page with cache-control:no-store enters bfcache with the flag on,
// and if a cookie is modified while it is in bfcache via response header, gets
// evicted with cookie modified marked.
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTestAllowCacheControlNoStore,
                       PagesWithCacheControlNoStoreSetFromResponseHeader) {}

// Test that a page with cache-control:no-store enters bfcache with the flag on,
// and if HTTPOnly cookie is modified while it is in bfcache, gets evicted with
// HTTPOnly cookie modified marked.
IN_PROC_BROWSER_TEST_F(
    BackForwardCacheBrowserTestAllowCacheControlNoStore,
    PagesWithCacheControlNoStoreSetFromResponseHeaderHTTPOnlyCookie) {}

// Test that a page with cache-control:no-store enters bfcache with the flag on,
// and if a HTTPOnly cookie is modified, it gets evicted with cookie changed,
// but if navigated away again and navigated back, it gets evicted without
// HTTPOnly cookie change marked.
IN_PROC_BROWSER_TEST_F(
    BackForwardCacheBrowserTestAllowCacheControlNoStore,
    PagesWithCacheControlNoStoreHTTPOnlyCookieModifiedBackTwice) {}

namespace {
// Causes a fetch request, start and complete in the target frame.
void SendFetchRequest(const ToRenderFrameHost& execution_target,
                      const GURL& url) {}

// Causes an XHR, start and complete in the target frame.
void SendXhrRequest(const ToRenderFrameHost& execution_target,
                    const GURL& url) {}

// Creates an iframe in the target frame with this url. It waits until the frame
// has loaded.
void CreateIframe(const ToRenderFrameHost& execution_target, GURL url) {}
}  // namespace

enum class RequestType {};

// Testing the BFCache behavior when the document sends a JavaScript network
// request and receiving response with "Cache-Control: no-store" header.
class BackForwardCacheWithJsNetworkRequestReceivingCCNSResourceBrowserTest
    : public BackForwardCacheBrowserTest,
      public ::testing::WithParamInterface<RequestType> {};
INSTANTIATE_TEST_SUITE_P();

// Test that a page without CCNS that makes a request that receives CCNS
// response does not log the
// `kJsNetworkRequestReceivedCacheControlNoStoreResource` reason.
IN_PROC_BROWSER_TEST_P(
    BackForwardCacheWithJsNetworkRequestReceivingCCNSResourceBrowserTest,
    CCNSResponseNotLogged) {}

// Test that a page with CCNS that makes a JavaScript network request which
// receives CCNS response logs the
// `kJsNetworkRequestReceivedCacheControlNoStoreResource` reason.
IN_PROC_BROWSER_TEST_P(
    BackForwardCacheWithJsNetworkRequestReceivingCCNSResourceBrowserTest,
    CCNSResponseLoggedMainFrame) {}

// Test that a page with CCNS that makes a request which receives CCNS response
// in a same-as-root-origin subframe of a cross-origin subframe logs the
// `kJsNetworkRequestReceivedCacheControlNoStoreResource` reason.
IN_PROC_BROWSER_TEST_P(
    BackForwardCacheWithJsNetworkRequestReceivingCCNSResourceBrowserTest,
    CCNSResponseSameOriginSubFrameLogged) {}

// Test that a page with CCNS that makes a request which receives CCNS response
// in a same-origin subframe logs the
// `kJsNetworkRequestReceivedCacheControlNoStoreResource` reason in the correct
// place in the tree of reasons.
IN_PROC_BROWSER_TEST_P(
    BackForwardCacheWithJsNetworkRequestReceivingCCNSResourceBrowserTest,
    CCNSResponseSubFrameTree) {}

// Test that a page with CCNS that makes a request which receives CCNS response
// in a cross-origin subframe does not log the
// `kJsNetworkRequestReceivedCacheControlNoStoreResource` reason.
IN_PROC_BROWSER_TEST_P(
    BackForwardCacheWithJsNetworkRequestReceivingCCNSResourceBrowserTest,
    CCNSResponseCrossOriginSubFrameNotLogged) {}

// A subclass of `ContentBrowserTestContentBrowserClient` for testing the logic
// that checks if cookie is enabled.
class CookieDisabledContentBrowserClient
    : public ContentBrowserTestContentBrowserClient {};
class BackForwardCacheBrowserTestRestoreCacheControlNoStoreUnlessCookieChange
    : public BackForwardCacheBrowserTest {};

// Test that a page with cache-control:no-store enters bfcache with the flag on,
// and gets restored if cookies do not change.
IN_PROC_BROWSER_TEST_F(
    BackForwardCacheBrowserTestRestoreCacheControlNoStoreUnlessCookieChange,
    PagesWithCacheControlNoStoreRestoreFromBackForwardCache) {}

// Test that a page with CCNS that makes a fetch that receives CCNS response
// is blocked even when CCNS pages are allowed to be restored. This only tests
// fetch, the blocking mechanism is the same for all kinds of requests, so if it
// works for one it will work for all.
IN_PROC_BROWSER_TEST_F(
    BackForwardCacheBrowserTestRestoreCacheControlNoStoreUnlessCookieChange,
    CCNSResponseBlocks) {}

IN_PROC_BROWSER_TEST_F(
    BackForwardCacheBrowserTestRestoreCacheControlNoStoreUnlessCookieChange,
    PagesWithCacheControlNoStoreEvictedIfCookieChange) {}

// Test that a page with cache-control:no-store enters bfcache with the flag on,
// and gets evicted with both JavaScript and HTTPOnly cookie changes. Only
// HTTPOnly cookie reason should be recorded.
IN_PROC_BROWSER_TEST_F(
    BackForwardCacheBrowserTestRestoreCacheControlNoStoreUnlessCookieChange,
    PagesWithCacheControlNoStoreEvictedWithBothCookieReasons) {}

// Test that a page with cache-control:no-store gets restored if the only cookie
// modification comes from the response of the `NavigationRequest`.
IN_PROC_BROWSER_TEST_F(
    BackForwardCacheBrowserTestRestoreCacheControlNoStoreUnlessCookieChange,
    PagesWithCacheControlNoStoreNotBFCachedWithCookieSetInResponse) {}

// Test that a page with `Cache-control: no-store` header gets evicted if some
// cookie is modified while the server receives the request but has not
// completed the response yet.
IN_PROC_BROWSER_TEST_F(
    BackForwardCacheBrowserTestRestoreCacheControlNoStoreUnlessCookieChange,
    PagesWithCacheControlNoStoreNotBFCachedWithCookieSetAfterRequestIsMade) {}

// Test that a page with cache-control:no-store gets evicted if some cookie is
// modified before navigating away.
IN_PROC_BROWSER_TEST_F(
    BackForwardCacheBrowserTestRestoreCacheControlNoStoreUnlessCookieChange,
    PagesWithCacheControlNoStoreNotBFCachedWithCookieSetBeforeNavigateAway) {}

// Test that a page with cache-control:no-store gets evicted if some cookie is
// modified from another tab before navigating away.
IN_PROC_BROWSER_TEST_F(
    BackForwardCacheBrowserTestRestoreCacheControlNoStoreUnlessCookieChange,
    PagesWithCacheControlNoStoreNotBFCachedWithCookieSetFromAnotherTabBeforeNavigateAway) {}

// Test that a page with cache-control:no-store gets restored if the cookie is
// modified by another tab before the navigation completes.
IN_PROC_BROWSER_TEST_F(
    BackForwardCacheBrowserTestRestoreCacheControlNoStoreUnlessCookieChange,
    PagesWithCacheControlNoStoreRestoredIfCookieChangeIsMadeBeforeRedirection) {}

// Test that the cookie change information is retained after same document
// navigation.
IN_PROC_BROWSER_TEST_F(
    BackForwardCacheBrowserTestRestoreCacheControlNoStoreUnlessCookieChange,
    PagesWithCacheControlNoStoreNotBFCachedWithCookieSetBeforeSameDocumentNavigation) {}

// Test that a page with `Cache-control: no-store` header gets evicted without
// crashes if some cookie is modified immediately before the back navigation.
// TODO: this test could be potentially flaky if the notification to
// CookieChangeListener is only received after the entire back navigation
// completes. If any flaky case is reported in the future, we should fix that by
// ensuring the eviction to happen after the NavigationRequest starts to process
// response but before it finishes committing the navigation.
// See discussion from https://crrev.com/c/4408607.
IN_PROC_BROWSER_TEST_F(
    BackForwardCacheBrowserTestRestoreCacheControlNoStoreUnlessCookieChange,
    PagesWithCacheControlNoStoreNotBFCachedWithCookieSetImmediatelyBeforeNavigateBack) {}

IN_PROC_BROWSER_TEST_F(
    BackForwardCacheBrowserTestRestoreCacheControlNoStoreUnlessCookieChange,
    PagesWithCacheControlNoStoreIsNotCacheIfCookieIsDisabled) {}

class BackForwardCacheBrowserTestRestoreUnlessHTTPOnlyCookieChange
    : public BackForwardCacheBrowserTest {};

// Test that a page without cache-control:no-store can enter BackForwardCache
// and gets restored if HTTPOnly Cookie changes.
IN_PROC_BROWSER_TEST_F(
    BackForwardCacheBrowserTestRestoreUnlessHTTPOnlyCookieChange,
    NoCacheControlNoStoreButHTTPOnlyCookieChange) {}

// Test that a page with cache-control:no-store enters bfcache with the flag on,
// and does not get evicted if normal cookies change.
IN_PROC_BROWSER_TEST_F(
    BackForwardCacheBrowserTestRestoreUnlessHTTPOnlyCookieChange,
    PagesWithCacheControlNoStoreNotEvictedIfNormalCookieChange) {}

// Test that a page with cache-control:no-store enters bfcache with the flag on,
// and gets evicted if HTTPOnly cookie changes.
IN_PROC_BROWSER_TEST_F(
    BackForwardCacheBrowserTestRestoreUnlessHTTPOnlyCookieChange,
    PagesWithCacheControlNoStoreEvictedIfHTTPOnlyCookieChange) {}

// Test that a page with cache-control:no-store enters bfcache with the flag on,
// and gets evicted if HTTPOnly cookie changes.
IN_PROC_BROWSER_TEST_F(
    BackForwardCacheBrowserTestRestoreUnlessHTTPOnlyCookieChange,
    PagesWithCacheControlNoStoreEvictedIfJSAndHTTPOnlyCookieChange) {}

// Test that a page with cache-control:no-store gets restored if the HTTPOnly
// cookie modification comes from the response of the `NavigationRequest`.
IN_PROC_BROWSER_TEST_F(
    BackForwardCacheBrowserTestRestoreUnlessHTTPOnlyCookieChange,
    PagesWithCacheControlNoStoreNotBFCachedWithHTTPOnlyCookieSetInResponse) {}

// Test that a page with cache-control:no-store gets evicted if some HTTPOnly
// cookie is modified from another tab before navigating away.
IN_PROC_BROWSER_TEST_F(
    BackForwardCacheBrowserTestRestoreUnlessHTTPOnlyCookieChange,
    PagesWithCacheControlNoStoreNotBFCachedWithHTTPOnlyCookieSetFromAnotherTabBeforeNavigateAway) {}

// Test that the cookie change information is retained after same document
// navigation.
IN_PROC_BROWSER_TEST_F(
    BackForwardCacheBrowserTestRestoreUnlessHTTPOnlyCookieChange,
    PagesWithCacheControlNoStoreNotBFCachedWithHTTPOnlyCookieSetBeforeSameDocumentNavigation) {}

}  // namespace content