blob: 588e46610dbdb100648d90a794614dc5164b332c [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/test/bind.h"
#include "content/browser/permissions/permission_controller_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_content_browser_client.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/shell/browser/shell.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/system/functions.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/mojom/storage_access/storage_access_handle.mojom.h"
namespace {
class MockContentBrowserClient final
: public content::ContentBrowserTestContentBrowserClient {
public:
explicit MockContentBrowserClient(bool is_full_cookie_access_allowed)
: is_full_cookie_access_allowed_(is_full_cookie_access_allowed) {}
bool IsFullCookieAccessAllowed(
content::BrowserContext* browser_context,
content::WebContents* web_contents,
const GURL& url,
const blink::StorageKey& storage_key,
net::CookieSettingOverrides overrides) override {
return is_full_cookie_access_allowed_;
}
private:
const bool is_full_cookie_access_allowed_{false};
};
} // namespace
namespace content {
class StorageAccessBrowserTest : public ContentBrowserTest,
public testing::WithParamInterface<bool> {
public:
void SetUpOnMainThread() override {
client_ =
std::make_unique<MockContentBrowserClient>(is_cookie_access_allowed());
host_resolver()->AddRule("*", "127.0.0.1");
embedded_https_test_server().SetSSLConfig(
net::EmbeddedTestServer::CERT_TEST_NAMES);
embedded_https_test_server().ServeFilesFromSourceDirectory(
"content/test/data");
ASSERT_TRUE(embedded_https_test_server().Start());
}
void TearDownOnMainThread() override { client_.reset(); }
bool is_cookie_access_allowed() const { return GetParam(); }
base::expected<void, std::optional<std::string>> expected_handle_result()
const {
if (is_cookie_access_allowed()) {
return base::ok();
}
if constexpr (DCHECK_IS_ON()) {
return base::unexpected(
"Binding a StorageAccessHandle requires third-party cookie access.");
}
return base::unexpected(std::nullopt);
}
protected:
[[nodiscard]] base::expected<void, std::optional<std::string>>
BindStorageAccessHandle() {
// Setup message interceptor.
std::optional<std::string> received_error;
mojo::SetDefaultProcessErrorHandler(
base::BindLambdaForTesting([&](const std::string& error) {
ASSERT_EQ(received_error, std::nullopt);
received_error = error;
}));
// Load website.
EXPECT_TRUE(NavigateToURL(shell(), embedded_https_test_server().GetURL(
"a.test", "/simple_page.html")));
// We need access to the interface broker to test bad messages, so must
// unbind the existing one and bind our own.
EXPECT_TRUE(
host()->browser_interface_broker_receiver_for_testing().Unbind());
mojo::Remote<blink::mojom::BrowserInterfaceBroker> broker_remote;
mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>
broker_receiver = broker_remote.BindNewPipeAndPassReceiver();
host()->BindBrowserInterfaceBrokerReceiver(std::move(broker_receiver));
// Try to bind our StorageAccessHandle.
mojo::Remote<blink::mojom::StorageAccessHandle> storage_remote;
broker_remote->GetInterface(storage_remote.BindNewPipeAndPassReceiver());
broker_remote.FlushForTesting();
// Cleanup message interceptor.
mojo::SetDefaultProcessErrorHandler(base::NullCallback());
if (received_error || !storage_remote.is_connected()) {
return base::unexpected(received_error);
}
return base::ok();
}
[[nodiscard]] bool BindDomStorage() {
// Load website with third-party iframe.
CHECK(NavigateToURL(
shell(),
embedded_https_test_server().GetURL(
"a.test", "/cross_site_iframe_factory.html?a.test(b.test)")));
FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
->GetPrimaryFrameTree()
.root();
CHECK_EQ(1U, root->child_count());
FrameTreeNode* child = root->child_at(0);
CHECK(child->current_frame_host()->GetStorageKey().IsThirdPartyContext());
// We should always be able to load the area for the frame's storage key.
mojo::Remote<blink::mojom::StorageArea> third_party_remote;
child->current_frame_host()
->GetStoragePartition()
->GetDOMStorageContext()
->OpenLocalStorage(
child->current_frame_host()->GetStorageKey(),
child->current_frame_host()->GetFrameToken(),
third_party_remote.BindNewPipeAndPassReceiver(),
ChildProcessSecurityPolicyImpl::GetInstance()->CreateHandle(
child->current_frame_host()->GetProcess()->GetDeprecatedID()),
base::DoNothing());
third_party_remote.FlushForTesting();
EXPECT_TRUE(third_party_remote.is_connected());
// We might be able to bind a first-party storage area too.
mojo::Remote<blink::mojom::StorageArea> first_party_remote;
child->current_frame_host()
->GetStoragePartition()
->GetDOMStorageContext()
->OpenLocalStorage(
blink::StorageKey::CreateFirstParty(
child->current_frame_host()->GetStorageKey().origin()),
child->current_frame_host()->GetFrameToken(),
first_party_remote.BindNewPipeAndPassReceiver(),
ChildProcessSecurityPolicyImpl::GetInstance()->CreateHandle(
child->current_frame_host()->GetProcess()->GetDeprecatedID()),
base::DoNothing());
first_party_remote.FlushForTesting();
return first_party_remote.is_connected();
}
RenderFrameHostImpl* host() {
return static_cast<RenderFrameHostImpl*>(
static_cast<WebContentsImpl*>(shell()->web_contents())
->GetPrimaryFrameTree()
.root()
->current_frame_host());
}
private:
std::unique_ptr<MockContentBrowserClient> client_;
};
IN_PROC_BROWSER_TEST_P(StorageAccessBrowserTest, BindStorageAccessHandle) {
EXPECT_EQ(BindStorageAccessHandle(), expected_handle_result());
}
IN_PROC_BROWSER_TEST_P(StorageAccessBrowserTest, BindDomStorage) {
EXPECT_EQ(BindDomStorage(), is_cookie_access_allowed());
}
INSTANTIATE_TEST_SUITE_P(, StorageAccessBrowserTest, testing::Bool());
} // namespace content