Commit a02f2df5 authored by Brandon Walderman's avatar Brandon Walderman Committed by Commit Bot

Implement Storage Access commands for TestDriver

This change implements the Set Storage Access command for TestDriver in
Blink and content_shell:
https://privacycg.github.io/storage-access/#automation

This is needed to support more in-depth WPT tests for the Storage
Access API. The current WPT tests for this feature cover basic IDL
behavior, but are not able to verify that the requestStorageAccess API
itself is functional because the browser must first be put into a state
where third-party cookies are blocked.

A new set_storage_access method has been added to testdriver.js. It
functions similarly to the existing set_permission method. This calls a
Blink-internal method that forwards the passed in arguments to the
content shell via Mojo. A new WebTestStorageAccessManager class in the
content shell converts the arguments into ContentSettingsPatterns and
passes these settings to the CookieManager. The
WebTestStorageAccessManager also enables third-party cookie blocking so
that these rules will take effect.

Bug: 1096803
Change-Id: I635687e7d00cf95fa2cf54fb86e1f65a0fe85f3f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2253280Reviewed-by: default avatarMartin Šrámek <msramek@chromium.org>
Reviewed-by: default avatarMike West <mkwst@chromium.org>
Commit-Queue: Brandon Walderman <brwalder@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#790441}
parent eb04b682
......@@ -154,6 +154,8 @@ if (support_web_tests) {
"browser/web_test/web_test_push_messaging_service.h",
"browser/web_test/web_test_shell_platform_delegate.cc",
"browser/web_test/web_test_shell_platform_delegate.h",
"browser/web_test/web_test_storage_access_manager.cc",
"browser/web_test/web_test_storage_access_manager.h",
"browser/web_test/web_test_tts_platform.cc",
"browser/web_test/web_test_tts_platform.h",
]
......
include_rules = [
"+components/content_settings/core/common",
"+content/shell/common/web_test",
]
......
......@@ -20,6 +20,7 @@
#include "content/shell/browser/web_test/web_test_download_manager_delegate.h"
#include "content/shell/browser/web_test/web_test_permission_manager.h"
#include "content/shell/browser/web_test/web_test_push_messaging_service.h"
#include "content/shell/browser/web_test/web_test_storage_access_manager.h"
#include "content/test/mock_background_sync_controller.h"
#include "content/test/mock_client_hints_controller_delegate.h"
#include "services/device/public/cpp/test/scoped_geolocation_overrider.h"
......@@ -92,6 +93,13 @@ WebTestPermissionManager* WebTestBrowserContext::GetWebTestPermissionManager() {
GetPermissionControllerDelegate());
}
WebTestStorageAccessManager*
WebTestBrowserContext::GetWebTestStorageAccessManager() {
if (!storage_access_.get())
storage_access_ = std::make_unique<WebTestStorageAccessManager>(this);
return storage_access_.get();
}
ClientHintsControllerDelegate*
WebTestBrowserContext::GetClientHintsControllerDelegate() {
if (!client_hints_controller_delegate_) {
......
......@@ -22,6 +22,7 @@ class PushMessagingService;
class WebTestBackgroundFetchDelegate;
class WebTestPermissionManager;
class WebTestPushMessagingService;
class WebTestStorageAccessManager;
class WebTestBrowserContext final : public ShellBrowserContext {
public:
......@@ -37,6 +38,7 @@ class WebTestBrowserContext final : public ShellBrowserContext {
ClientHintsControllerDelegate* GetClientHintsControllerDelegate() override;
WebTestPermissionManager* GetWebTestPermissionManager();
WebTestStorageAccessManager* GetWebTestStorageAccessManager();
private:
std::unique_ptr<WebTestPushMessagingService> push_messaging_service_;
......@@ -46,6 +48,7 @@ class WebTestBrowserContext final : public ShellBrowserContext {
std::unique_ptr<device::ScopedGeolocationOverrider> geolocation_overrider_;
std::unique_ptr<ClientHintsControllerDelegate>
client_hints_controller_delegate_;
std::unique_ptr<WebTestStorageAccessManager> storage_access_;
DISALLOW_COPY_AND_ASSIGN(WebTestBrowserContext);
};
......
......@@ -40,6 +40,7 @@
#include "content/shell/browser/web_test/web_test_client_impl.h"
#include "content/shell/browser/web_test/web_test_control_host.h"
#include "content/shell/browser/web_test/web_test_permission_manager.h"
#include "content/shell/browser/web_test/web_test_storage_access_manager.h"
#include "content/shell/browser/web_test/web_test_tts_platform.h"
#include "content/shell/common/web_test/web_test_bluetooth_fake_adapter_setter.mojom.h"
#include "content/shell/common/web_test/web_test_switches.h"
......@@ -225,6 +226,12 @@ void WebTestContentBrowserClient::ExposeInterfacesToRenderer(
base::Unretained(this)),
ui_task_runner);
registry->AddInterface(
base::BindRepeating(
&WebTestContentBrowserClient::BindStorageAccessAutomation,
base::Unretained(this)),
ui_task_runner);
associated_registry->AddInterface(
base::BindRepeating(&WebTestContentBrowserClient::BindWebTestControlHost,
base::Unretained(this)));
......@@ -240,6 +247,13 @@ void WebTestContentBrowserClient::BindPermissionAutomation(
std::move(receiver));
}
void WebTestContentBrowserClient::BindStorageAccessAutomation(
mojo::PendingReceiver<blink::test::mojom::StorageAccessAutomation>
receiver) {
GetWebTestBrowserContext()->GetWebTestStorageAccessManager()->Bind(
std::move(receiver));
}
void WebTestContentBrowserClient::OverrideWebkitPrefs(
RenderViewHost* render_view_host,
WebPreferences* prefs) {
......
......@@ -21,6 +21,7 @@
#include "third_party/blink/public/mojom/clipboard/clipboard.mojom.h"
#include "third_party/blink/public/mojom/clipboard/raw_clipboard.mojom.h"
#include "third_party/blink/public/mojom/permissions/permission_automation.mojom-forward.h"
#include "third_party/blink/public/mojom/storage_access/storage_access_automation.mojom-forward.h"
namespace content {
class FakeBluetoothChooser;
......@@ -126,6 +127,10 @@ class WebTestContentBrowserClient : public ShellContentBrowserClient {
void BindPermissionAutomation(
mojo::PendingReceiver<blink::test::mojom::PermissionAutomation> receiver);
void BindStorageAccessAutomation(
mojo::PendingReceiver<blink::test::mojom::StorageAccessAutomation>
receiver);
void BindWebTestControlHost(
mojo::PendingAssociatedReceiver<mojom::WebTestControlHost> receiver);
......
// 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 "content/shell/browser/web_test/web_test_storage_access_manager.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/storage_partition.h"
#include <list>
#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/callback.h"
namespace content {
WebTestStorageAccessManager::WebTestStorageAccessManager(
BrowserContext* browser_context)
: browser_context_(browser_context) {}
WebTestStorageAccessManager::~WebTestStorageAccessManager() = default;
void WebTestStorageAccessManager::SetStorageAccess(
const std::string& origin,
const std::string& embedding_origin,
const bool blocked,
blink::test::mojom::StorageAccessAutomation::SetStorageAccessCallback
callback) {
const ContentSetting setting =
blocked ? CONTENT_SETTING_BLOCK : CONTENT_SETTING_ALLOW;
auto primary_pattern = ContentSettingsPattern::FromString(origin);
if (!primary_pattern.IsValid()) {
std::move(callback).Run(false);
}
auto secondary_pattern = ContentSettingsPattern::FromString(embedding_origin);
if (!secondary_pattern.IsValid()) {
std::move(callback).Run(false);
}
content_settings_for_automation_.push_back(
ContentSettingPatternSource(primary_pattern, secondary_pattern,
base::Value(setting), std::string(), false));
// TODO(https://crbug.com/1106098) - Storage Access API should support all
// storage types in content shell
// Storage access API (SAA) settings for cookies are implemented in the
// network::CookieSettings class. Settings for other storage types such as
// local storage and indexeddb are implemented in
// content_settings::CookieSettings. Content Shell does not
// use the content_settings::CookieSettings class so SAA affects only
// cookie access here. Other storage types are always allowed in
// Content Shell.
// Since cookies are the only storage type governed by SAA in Content Shell,
// this class handles cookie rules only. If Content Shell or SAA are
// updated in the future so that more storage types are governed by SAA in
// Content Shell, then we should update this class to handle those other
// types are well.
auto* storage_partition =
BrowserContext::GetDefaultStoragePartition(browser_context_);
auto* cookie_manager = storage_partition->GetCookieManagerForBrowserProcess();
// Enable third-party cookies blocking if we have not done so yet. This will
// cause the content settings to take effect.
if (!third_party_cookies_blocked_) {
cookie_manager->BlockThirdPartyCookies(true);
third_party_cookies_blocked_ = true;
}
// Update the cookie manager's copy of the content settings.
cookie_manager->SetContentSettings(content_settings_for_automation_);
std::move(callback).Run(true);
}
void WebTestStorageAccessManager::Bind(
mojo::PendingReceiver<blink::test::mojom::StorageAccessAutomation>
receiver) {
receivers_.Add(this, std::move(receiver));
}
} // namespace content
// 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 CONTENT_SHELL_BROWSER_WEB_TEST_WEB_TEST_STORAGE_ACCESS_MANAGER_H_
#define CONTENT_SHELL_BROWSER_WEB_TEST_WEB_TEST_STORAGE_ACCESS_MANAGER_H_
#include <stddef.h>
#include "base/callback_forward.h"
#include "base/containers/id_map.h"
#include "base/macros.h"
#include "base/synchronization/lock.h"
#include "components/content_settings/core/common/content_settings.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
#include "third_party/blink/public/mojom/storage_access/storage_access_automation.mojom.h"
namespace content {
class BrowserContext;
class WebTestStorageAccessManager
: public blink::test::mojom::StorageAccessAutomation {
public:
explicit WebTestStorageAccessManager(BrowserContext* browser_context);
~WebTestStorageAccessManager() override;
// blink::test::mojom::StorageAccessAutomation
void SetStorageAccess(
const std::string& origin,
const std::string& embedding_origin,
const bool blocked,
blink::test::mojom::StorageAccessAutomation::SetStorageAccessCallback)
override;
void Bind(mojo::PendingReceiver<blink::test::mojom::StorageAccessAutomation>
receiver);
private:
BrowserContext* browser_context_;
mojo::ReceiverSet<blink::test::mojom::StorageAccessAutomation> receivers_;
ContentSettingsForOneType content_settings_for_automation_;
bool third_party_cookies_blocked_ = false;
DISALLOW_COPY_AND_ASSIGN(WebTestStorageAccessManager);
};
} // namespace content
#endif // CONTENT_SHELL_BROWSER_WEB_TEST_WEB_TEST_STORAGE_ACCESS_MANAGER_H_
......@@ -158,6 +158,7 @@ mojom("mojom_platform") {
"speech/speech_recognition_result.mojom",
"speech/speech_recognizer.mojom",
"speech/speech_synthesis.mojom",
"storage_access/storage_access_automation.mojom",
"timing/performance_mark_or_measure.mojom",
"timing/resource_timing.mojom",
"timing/worker_timing_container.mojom",
......
file://chrome/browser/storage_access_api/OWNERS
per-file *.mojom=set noparent
per-file *.mojom=file://ipc/SECURITY_OWNERS
// 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.
module blink.test.mojom;
// Provides a way for tests to change the storage access policy.
// See https://privacycg.github.io/storage-access/#automation
interface StorageAccessAutomation {
// Sets whether |origin| has access to cookies from |embedding_origin|.
SetStorageAccess(string origin,
string embedding_origin,
bool allowed) => (bool success);
};
......@@ -670,6 +670,7 @@ static_idl_files_in_core = get_path_info(
# These IDL definitions are used only for testing.
static_idl_files_in_core_for_testing = get_path_info(
[
"//third_party/blink/renderer/core/dom/testing/internals_storage_access.idl",
"//third_party/blink/renderer/core/fetch/testing/internals_fetch.idl",
"//third_party/blink/renderer/core/fetch/testing/worker_internals_fetch.idl",
"//third_party/blink/renderer/core/testing/callback_function_test.idl",
......
......@@ -229,6 +229,8 @@ jumbo_source_set("testing") {
sources = [
"$blink_core_output_dir/testing/internal_settings_generated.cc",
"$blink_core_output_dir/testing/internal_settings_generated.h",
"dom/testing/internals_storage_access.cc",
"dom/testing/internals_storage_access.h",
"fetch/testing/internals_fetch.cc",
"fetch/testing/internals_fetch.h",
"fetch/testing/worker_internals_fetch.cc",
......
......@@ -787,6 +787,7 @@ webcore_testing_idl_with_modules_dependency_files =
webcore_testing_dependency_idl_files =
get_path_info([
"dom/testing/internals_storage_access.idl",
"fetch/testing/internals_fetch.idl",
"fetch/testing/worker_internals_fetch.idl",
"testing/origin_trials_test_partial.idl",
......
// 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 "third_party/blink/renderer/core/dom/testing/internals_storage_access.h"
#include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h"
#include "third_party/blink/public/mojom/storage_access/storage_access_automation.mojom-blink.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
namespace blink {
// static
ScriptPromise InternalsStorageAccess::setStorageAccess(
ScriptState* script_state,
Internals&,
const String& origin,
const String& embedding_origin,
const bool blocked,
ExceptionState& exception_state) {
mojo::Remote<test::mojom::blink::StorageAccessAutomation>
storage_access_automation;
Platform::Current()->GetBrowserInterfaceBroker()->GetInterface(
storage_access_automation.BindNewPipeAndPassReceiver());
DCHECK(storage_access_automation.is_bound());
auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
ScriptPromise promise = resolver->Promise();
auto* raw_storage_access_automation = storage_access_automation.get();
raw_storage_access_automation->SetStorageAccess(
origin, embedding_origin, blocked,
WTF::Bind(
// While we only really need |resolver|, we also take the
// mojo::Remote<> so that it remains alive after this function exits.
[](ScriptPromiseResolver* resolver,
mojo::Remote<test::mojom::blink::StorageAccessAutomation>,
bool success) {
if (success)
resolver->Resolve();
else
resolver->Reject();
},
WrapPersistent(resolver), std::move(storage_access_automation)));
return promise;
}
} // namespace blink
// 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 THIRD_PARTY_BLINK_RENDERER_CORE_DOM_TESTING_INTERNALS_STORAGE_ACCESS_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_DOM_TESTING_INTERNALS_STORAGE_ACCESS_H_
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
#include "third_party/blink/renderer/platform/wtf/forward.h"
namespace blink {
class ExceptionState;
class Internals;
class ScriptPromise;
class ScriptState;
class InternalsStorageAccess {
STATIC_ONLY(InternalsStorageAccess);
public:
static ScriptPromise setStorageAccess(ScriptState*,
Internals&,
const String& origin,
const String& embedding_origin,
const bool blocked,
ExceptionState&);
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_DOM_TESTING_INTERNALS_STORAGE_ACCESS_H_
// 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.
[
ImplementedAs=InternalsStorageAccess
] partial interface Internals {
[CallWith=ScriptState, RaisesException] Promise<void> setStorageAccess(USVString origin, USVString embeddingOrigin, boolean blocked);
};
......@@ -355,6 +355,32 @@
set_user_verified: function(authenticator_id, uv) {
return window.test_driver_internal.set_user_verified(authenticator_id, uv);
},
/**
* Sets the storage access rule for an origin when embedded
* in a third-party context.
*
* {@link https://privacycg.github.io/storage-access/#set-storage-access-command}
*
* @param {String} origin - A third-party origin to block or allow.
* May be "*" to indicate all origins.
* @param {String} embedding_origin - an embedding (first-party) origin
* on which {origin}'s access should
* be blocked or allowed.
* May be "*" to indicate all origins.
* @param {String} state - The storage access setting.
* Must be either "allowed" or "blocked".
*
* @returns {Promise} Fulfilled after the storage access rule has been
* set, or rejected if setting the rule fails.
*/
set_storage_access: function(origin, embedding_origin, state) {
if (state !== "allowed" && state !== "blocked") {
throw new Error("storage access status must be 'allowed' or 'blocked'");
}
const blocked = state === "blocked";
return window.test_driver_internal.set_storage_access(origin, embedding_origin, blocked);
},
};
window.test_driver_internal = {
......@@ -569,5 +595,13 @@
set_user_verified: function(authenticator_id, uv) {
return Promise.reject(new Error("unimplemented"));
},
/**
* Sets the storage access policy for a third-party origin when loaded
* in the current first party context
*/
set_storage_access: function(origin, embedding_origin, blocked) {
return Promise.reject(new Error("unimplemented"));
},
};
})();
def main(request, response):
name = request.GET.first(b"name")
value = request.GET.first(b"value")
testcase = request.GET.first(b"testcase")
response_headers = [(b"Set-Cookie", name + b"=" + value)]
body = b"""
<!DOCTYPE html>
<meta charset="utf-8">
<title>Set Storage Access Subframe</title>
<script src="/resources/testharness.js"></script>
<script>
let querystring = window.location.search.substring(1).split("&");
const allowed = querystring.some(param => param.toLowerCase() === "allowed=true");
test(() => {
if (allowed) {
assert_equals(document.cookie, "%s=%s");
} else {
assert_equals(document.cookie, "");
}
}, "[%s] Cookie access is allowed: " + allowed);
</script>
""" % (name, value, testcase)
return (200, response_headers, body)
<!DOCTYPE html>
<head>
<title>TestDriver - Set Storage Access Command Tests</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="helpers.js"></script>
</head>
<body>
<script>
"use strict";
promise_test(async t => {
// Allow a third-party site embedded in this first-party site.
await window.test_driver.set_storage_access("http://{{domains[www]}}:{{ports[http][0]}}/", "http://{{domains[]}}:{{ports[http][0]}}/", "allowed");
// Block a third-party site embedded in this first-party site.
await window.test_driver.set_storage_access("http://{{domains[www1]}}:{{ports[http][0]}}/", "http://{{domains[]}}:{{ports[http][0]}}/", "blocked");
// Block a third-party site on all first-party sites.
await window.test_driver.set_storage_access("http://{{domains[www2]}}:{{ports[http][0]}}/", "*", "blocked");
}, "Set up storage access rules");
RunTestsInIFrame("http://{{domains[]}}:{{ports[http][0]}}/storage-access-api/resources/set-cookie.py?name=hello0&value=world0&allowed=true&testcase=same-site");
RunTestsInIFrame("http://{{domains[www]}}:{{ports[http][0]}}/storage-access-api/resources/set-cookie.py?name=hello&value=world&allowed=true&testcase=third-party-allowed-on-first-party-site");
RunTestsInIFrame("http://{{domains[www1]}}:{{ports[http][0]}}/storage-access-api/resources/set-cookie.py?name=hello1&value=world1&allowed=false&testcase=third-party-blocked-on-first-party-site");
RunTestsInIFrame("http://{{domains[www2]}}:{{ports[http][0]}}/storage-access-api/resources/set-cookie.py?name=hello2&value=world2&allowed=false&testcase=third-party-blocked-all");
</script>
</body>
......@@ -410,6 +410,10 @@
permission_params.state);
}
window.test_driver_internal.set_storage_access = function(origin, embedding_origin, blocked) {
return internals.setStorageAccess(origin, embedding_origin, blocked);
}
// Enable automation so we don't wait for user input on unimplemented APIs
window.test_driver_internal.in_automation = true;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment