[fenced frames] Support asynchronous mapping of navigation to a urn:uuid
Add a checkpoint before NavigationRequest::BeginNavigationImpl, to do fenced frames url mapping synchronously and proceed with the navigation, or, if the mapping decision is pending, defer the navigation until the mapping decision is made. The mapping decision may be pending for URNs generated as a result of sharedStorage.runURLSelectionOperation() (will be done in a follow-up CL). Design doc: https://docs.google.com/document/d/1SOUQhP-0QDUJJz8G9R2VwsnCH8M1VCJWEF8O2VTY2tw Bug: 1216088 Change-Id: I0aa5445e1fb64046a5a5ba088e3fd9ff0b64176a Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3309157 Reviewed-by: Dominic Farolino <dom@chromium.org> Reviewed-by: Alex Moshchuk <alexmos@chromium.org> Reviewed-by: Kouhei Ueno <kouhei@chromium.org> Commit-Queue: Yao Xiao <yaoxia@chromium.org> Cr-Commit-Position: refs/heads/main@{#956364}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
c171462ce7
commit
6e1f7d3571
content
browser
fenced_frame
interest_group
renderer_host
test
docs
@ -64,7 +64,7 @@ std::vector<GURL> FencedFrameURLMapping::PendingAdComponentsMap::GetURNs()
|
||||
void FencedFrameURLMapping::PendingAdComponentsMap::ExportToMapping(
|
||||
FencedFrameURLMapping& mapping) const {
|
||||
for (const auto& component_ad : component_ads_) {
|
||||
CHECK(!mapping.IsPresent(component_ad.urn));
|
||||
DCHECK(!mapping.IsMapped(component_ad.urn));
|
||||
|
||||
UrnUuidToUrlMap::iterator it =
|
||||
mapping.urn_uuid_to_url_map_
|
||||
@ -108,22 +108,6 @@ FencedFrameURLMapping::MapInfo& FencedFrameURLMapping::MapInfo::operator=(
|
||||
FencedFrameURLMapping::FencedFrameURLMapping() = default;
|
||||
FencedFrameURLMapping::~FencedFrameURLMapping() = default;
|
||||
|
||||
absl::optional<GURL> FencedFrameURLMapping::ConvertFencedFrameURNToURL(
|
||||
const GURL& urn_uuid,
|
||||
absl::optional<PendingAdComponentsMap>& out_ad_components) const {
|
||||
CHECK(IsValidUrnUuidURL(urn_uuid));
|
||||
|
||||
auto it = urn_uuid_to_url_map_.find(urn_uuid);
|
||||
if (it == urn_uuid_to_url_map_.end())
|
||||
return absl::nullopt;
|
||||
|
||||
if (it->second.ad_component_urls) {
|
||||
out_ad_components.emplace(
|
||||
PendingAdComponentsMap(*it->second.ad_component_urls));
|
||||
}
|
||||
return it->second.mapped_url;
|
||||
}
|
||||
|
||||
GURL FencedFrameURLMapping::AddFencedFrameURL(const GURL& url) {
|
||||
DCHECK(url.is_valid());
|
||||
DCHECK(network::IsUrlPotentiallyTrustworthy(url));
|
||||
@ -143,13 +127,95 @@ FencedFrameURLMapping::UrnUuidToUrlMap::iterator
|
||||
FencedFrameURLMapping::AddMappingForUrl(const GURL& url) {
|
||||
// Create a urn::uuid.
|
||||
GURL urn_uuid = GenerateURN();
|
||||
CHECK(!IsPresent(urn_uuid));
|
||||
DCHECK(!IsMapped(urn_uuid));
|
||||
|
||||
return urn_uuid_to_url_map_.emplace(urn_uuid, MapInfo(url)).first;
|
||||
}
|
||||
|
||||
bool FencedFrameURLMapping::IsPresent(const GURL& urn_uuid) {
|
||||
GURL FencedFrameURLMapping::GeneratePendingMappedURN() {
|
||||
GURL urn_uuid = GenerateURN();
|
||||
DCHECK(!IsMapped(urn_uuid));
|
||||
DCHECK(!IsPendingMapped(urn_uuid));
|
||||
pending_urn_uuid_to_url_map_.emplace(
|
||||
urn_uuid, std::set<raw_ptr<MappingResultObserver>>());
|
||||
return urn_uuid;
|
||||
}
|
||||
|
||||
void FencedFrameURLMapping::ConvertFencedFrameURNToURL(
|
||||
const GURL& urn_uuid,
|
||||
MappingResultObserver* observer) {
|
||||
DCHECK(IsValidUrnUuidURL(urn_uuid));
|
||||
|
||||
if (IsPendingMapped(urn_uuid)) {
|
||||
DCHECK(!pending_urn_uuid_to_url_map_.at(urn_uuid).count(observer));
|
||||
pending_urn_uuid_to_url_map_.at(urn_uuid).emplace(observer);
|
||||
return;
|
||||
}
|
||||
|
||||
absl::optional<GURL> result_url;
|
||||
absl::optional<PendingAdComponentsMap> result_ad_components;
|
||||
|
||||
auto it = urn_uuid_to_url_map_.find(urn_uuid);
|
||||
if (it != urn_uuid_to_url_map_.end()) {
|
||||
if (it->second.ad_component_urls) {
|
||||
result_ad_components.emplace(
|
||||
PendingAdComponentsMap(*it->second.ad_component_urls));
|
||||
}
|
||||
result_url = it->second.mapped_url;
|
||||
}
|
||||
|
||||
observer->OnFencedFrameURLMappingComplete(std::move(result_url),
|
||||
std::move(result_ad_components));
|
||||
}
|
||||
|
||||
void FencedFrameURLMapping::RemoveObserverForURN(
|
||||
const GURL& urn_uuid,
|
||||
MappingResultObserver* observer) {
|
||||
auto it = pending_urn_uuid_to_url_map_.find(urn_uuid);
|
||||
DCHECK(it != pending_urn_uuid_to_url_map_.end());
|
||||
|
||||
auto observer_it = it->second.find(observer);
|
||||
DCHECK(observer_it != it->second.end());
|
||||
|
||||
it->second.erase(observer_it);
|
||||
}
|
||||
|
||||
void FencedFrameURLMapping::OnURNMappingResultDetermined(
|
||||
const GURL& urn_uuid,
|
||||
const absl::optional<GURL>& mapped_url) {
|
||||
auto it = pending_urn_uuid_to_url_map_.find(urn_uuid);
|
||||
DCHECK(it != pending_urn_uuid_to_url_map_.end());
|
||||
|
||||
DCHECK(!IsMapped(urn_uuid));
|
||||
|
||||
if (mapped_url)
|
||||
urn_uuid_to_url_map_.emplace(urn_uuid, mapped_url.value());
|
||||
|
||||
std::set<raw_ptr<MappingResultObserver>>& observers = it->second;
|
||||
|
||||
for (raw_ptr<MappingResultObserver> observer : observers) {
|
||||
observer->OnFencedFrameURLMappingComplete(
|
||||
mapped_url,
|
||||
/*pending_ad_components_map=*/absl::nullopt);
|
||||
}
|
||||
|
||||
pending_urn_uuid_to_url_map_.erase(it);
|
||||
}
|
||||
|
||||
bool FencedFrameURLMapping::HasObserverForTesting(
|
||||
const GURL& urn_uuid,
|
||||
MappingResultObserver* observer) {
|
||||
return IsPendingMapped(urn_uuid) &&
|
||||
pending_urn_uuid_to_url_map_.at(urn_uuid).count(observer);
|
||||
}
|
||||
|
||||
bool FencedFrameURLMapping::IsMapped(const GURL& urn_uuid) const {
|
||||
return urn_uuid_to_url_map_.find(urn_uuid) != urn_uuid_to_url_map_.end();
|
||||
}
|
||||
|
||||
bool FencedFrameURLMapping::IsPendingMapped(const GURL& urn_uuid) const {
|
||||
return pending_urn_uuid_to_url_map_.find(urn_uuid) !=
|
||||
pending_urn_uuid_to_url_map_.end();
|
||||
}
|
||||
|
||||
} // namespace content
|
||||
|
@ -6,6 +6,7 @@
|
||||
#define CONTENT_BROWSER_FENCED_FRAME_FENCED_FRAME_URL_MAPPING_H_
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@ -17,10 +18,9 @@ namespace content {
|
||||
|
||||
extern const char kURNUUIDprefix[];
|
||||
|
||||
// Keeps a mapping of fenced frames URN:UUID and URL. See
|
||||
// Keeps a mapping of fenced frames URN:UUID and URL. Also keeps a set of
|
||||
// pending mapped URN:UUIDs to support asynchronous mapping. See
|
||||
// https://github.com/shivanigithub/fenced-frame/blob/master/OpaqueSrc.md
|
||||
// TODO(crbug.com/1216088) Also support asynchronous mapping of urn:uuid to
|
||||
// url.
|
||||
class CONTENT_EXPORT FencedFrameURLMapping {
|
||||
public:
|
||||
// When the result of an ad auction is a main ad URL with a set of ad
|
||||
@ -75,24 +75,34 @@ class CONTENT_EXPORT FencedFrameURLMapping {
|
||||
std::vector<AdComponent> component_ads_;
|
||||
};
|
||||
|
||||
class MappingResultObserver {
|
||||
public:
|
||||
virtual ~MappingResultObserver() = default;
|
||||
|
||||
// Called as soon as the URN mapping decision is made.
|
||||
//
|
||||
// On success, `mapped_url` will be populated with the result URL. If the
|
||||
// original URN is the result of an InterestGroup auction (which is only
|
||||
// possible when the initial URN is already mapped, and the
|
||||
// `OnFencedFrameURLMappingComplete` is invoked synchronously),
|
||||
// `pending_ad_components_map` will be populated with a
|
||||
// `PendingAdComponentsMap`. In that case, the observer needs to use the
|
||||
// `PendingAdComponentsMap` to provide ad component URNs to the fenced frame
|
||||
// via its commit parameters, and to add those URNs to the
|
||||
// `FencedFrameURLMapping` of the committed frame.
|
||||
//
|
||||
// On failure, both `mapped_url` and `pending_ad_components_map` will be
|
||||
// absl::nullopt.
|
||||
virtual void OnFencedFrameURLMappingComplete(
|
||||
absl::optional<GURL> mapped_url,
|
||||
absl::optional<PendingAdComponentsMap> pending_ad_components_map) = 0;
|
||||
};
|
||||
|
||||
FencedFrameURLMapping();
|
||||
~FencedFrameURLMapping();
|
||||
FencedFrameURLMapping(FencedFrameURLMapping&) = delete;
|
||||
FencedFrameURLMapping& operator=(FencedFrameURLMapping&) = delete;
|
||||
|
||||
// Converts given |urn_uuid| to the mapped URL. Only returns a GURL if a
|
||||
// mapping exists for |urn_uuid|. Should only be invoked with a valid url
|
||||
// with the urn scheme.
|
||||
//
|
||||
// If the passed in URN is the result of an InterestGroup auction,
|
||||
// `out_ad_components` will be populated with a PendingAdComponentsMap.
|
||||
// In that case, the caller needs to use the PendingAdComponentsMap to provide
|
||||
// ad component URNs to the fenced frame via its commit parameters, and to add
|
||||
// those URNs the FencedFrameURLMapping of the committed frame.
|
||||
absl::optional<GURL> ConvertFencedFrameURNToURL(
|
||||
const GURL& urn_uuid,
|
||||
absl::optional<PendingAdComponentsMap>& out_ad_components) const;
|
||||
|
||||
// Adds a mapping for |url| to a URN:UUID that will be generated by this
|
||||
// function. Should only be invoked with a valid URL which is one of the
|
||||
// "potentially trustworthy URLs".
|
||||
@ -110,6 +120,39 @@ class CONTENT_EXPORT FencedFrameURLMapping {
|
||||
|
||||
static bool IsValidUrnUuidURL(const GURL& url);
|
||||
|
||||
// Generate a URN that is not yet mapped to a URL. Used by the Shared Storage
|
||||
// API to return the URN for `sharedStorage.runURLSelectionOperation` before
|
||||
// the URL selection decision is made.
|
||||
GURL GeneratePendingMappedURN();
|
||||
|
||||
// Register an observer for `urn_uuid`. The observer will be notified with the
|
||||
// mapping result and will be auto unregistered. If `urn_uuid` already exists
|
||||
// in `urn_uuid_to_url_map_`, or if it is not recognized at all, the observer
|
||||
// will be notified synchronously; if the mapping is pending (i.e. `urn_uuid`
|
||||
// exists in `pending_urn_uuid_to_url_map_`), the observer will be notified
|
||||
// asynchronously as soon as when the mapping decision is made.
|
||||
void ConvertFencedFrameURNToURL(const GURL& urn_uuid,
|
||||
MappingResultObserver* observer);
|
||||
|
||||
// Explicitly unregister the observer for `urn_uuid`. This is only needed if
|
||||
// the observer is going to become invalid and the mapping is still pending.
|
||||
void RemoveObserverForURN(const GURL& urn_uuid,
|
||||
MappingResultObserver* observer);
|
||||
|
||||
// Called when the mapping decision is made for `urn_uuid`. On success,
|
||||
// `mapped_url` will be the result url; on failure,`mapped_url` will be
|
||||
// absl::nullopt. Should only be invoked with a `urn_uuid` pending to be
|
||||
// mapped. This method will trigger the observers'
|
||||
// OnFencedFrameURLMappingComplete() method associated with the `urn_uuid`,
|
||||
// unregister those observers, and remove `urn_uuid` from
|
||||
// `pending_urn_uuid_to_url_map_`. If the mapping succeeded, the `urn_uuid`
|
||||
// will be added to `urn_uuid_to_url_map_`.
|
||||
void OnURNMappingResultDetermined(const GURL& urn_uuid,
|
||||
const absl::optional<GURL>& mapped_url);
|
||||
|
||||
bool HasObserverForTesting(const GURL& urn_uuid,
|
||||
MappingResultObserver* observer);
|
||||
|
||||
private:
|
||||
// Contains the URL a particular URN is mapped to, along with any extra data
|
||||
// associated with the URL that needs to be used on commit.
|
||||
@ -140,9 +183,16 @@ class CONTENT_EXPORT FencedFrameURLMapping {
|
||||
// as the key.
|
||||
UrnUuidToUrlMap::iterator AddMappingForUrl(const GURL& url);
|
||||
|
||||
bool IsPresent(const GURL& urn_uuid);
|
||||
bool IsMapped(const GURL& urn_uuid) const;
|
||||
bool IsPendingMapped(const GURL& urn_uuid) const;
|
||||
|
||||
// The URNs that are already mapped to URLs, along with their mapping info.
|
||||
UrnUuidToUrlMap urn_uuid_to_url_map_;
|
||||
|
||||
// The URNs that are not yet mapped to URLs, along with the associated
|
||||
// observers to be notified when the mapping decision is made.
|
||||
std::map<GURL, std::set<raw_ptr<MappingResultObserver>>>
|
||||
pending_urn_uuid_to_url_map_;
|
||||
};
|
||||
|
||||
} // namespace content
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "content/test/test_fenced_frame_url_mapping_result_observer.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "third_party/abseil-cpp/absl/types/optional.h"
|
||||
#include "third_party/blink/public/common/interest_group/ad_auction_constants.h"
|
||||
@ -46,11 +47,12 @@ void ValidatePendingAdComponentsMap(
|
||||
|
||||
// The URNs should not yet be in `fenced_frame_url_mapping`, so they can
|
||||
// safely be added to it.
|
||||
absl::optional<FencedFrameURLMapping::PendingAdComponentsMap>
|
||||
nested_ad_components;
|
||||
EXPECT_FALSE(fenced_frame_url_mapping->ConvertFencedFrameURNToURL(
|
||||
ad_component_urns[i], nested_ad_components));
|
||||
EXPECT_FALSE(nested_ad_components);
|
||||
TestFencedFrameURLMappingResultObserver observer;
|
||||
fenced_frame_url_mapping->ConvertFencedFrameURNToURL(ad_component_urns[i],
|
||||
&observer);
|
||||
EXPECT_TRUE(observer.mapping_complete_observed());
|
||||
EXPECT_FALSE(observer.mapped_url());
|
||||
EXPECT_FALSE(observer.pending_ad_components_map());
|
||||
}
|
||||
|
||||
// Add the `pending_ad_components` to a mapping. If `add_to_new_map` is true,
|
||||
@ -67,35 +69,35 @@ void ValidatePendingAdComponentsMap(
|
||||
for (size_t i = 0; i < ad_component_urns.size(); ++i) {
|
||||
// The URNs should now be in `fenced_frame_url_mapping`. Look up the
|
||||
// corresponding URL, and make sure it's mapped to the correct URL.
|
||||
absl::optional<FencedFrameURLMapping::PendingAdComponentsMap>
|
||||
nested_pending_ad_components;
|
||||
absl::optional<GURL> mapped_url =
|
||||
fenced_frame_url_mapping->ConvertFencedFrameURNToURL(
|
||||
ad_component_urns[i], nested_pending_ad_components);
|
||||
ASSERT_TRUE(mapped_url);
|
||||
TestFencedFrameURLMappingResultObserver observer;
|
||||
fenced_frame_url_mapping->ConvertFencedFrameURNToURL(ad_component_urns[i],
|
||||
&observer);
|
||||
EXPECT_TRUE(observer.mapping_complete_observed());
|
||||
|
||||
if (i < expected_mapped_urls.size()) {
|
||||
EXPECT_EQ(expected_mapped_urls[i], mapped_url);
|
||||
EXPECT_EQ(expected_mapped_urls[i], observer.mapped_url());
|
||||
} else {
|
||||
EXPECT_EQ(GURL(url::kAboutBlankURL), mapped_url);
|
||||
EXPECT_EQ(GURL(url::kAboutBlankURL), observer.mapped_url());
|
||||
}
|
||||
|
||||
// Each added URN should also have a populated
|
||||
// `nested_pending_ad_components` structure, to prevent ads from knowing if
|
||||
// they were loaded in a fenced frame as an ad component or as the main ad.
|
||||
// Any information passed to ads violates the k-anonymity requirement.
|
||||
ASSERT_TRUE(nested_pending_ad_components);
|
||||
// `observer.pending_ad_components_map()` structure, to prevent ads from
|
||||
// knowing if they were loaded in a fenced frame as an ad component or as
|
||||
// the main ad. Any information passed to ads violates the k-anonymity
|
||||
// requirement.
|
||||
EXPECT_TRUE(observer.pending_ad_components_map());
|
||||
|
||||
// If this it not an about:blank URL, then when loaded in a fenced frame, it
|
||||
// can recursively access its own nested ad components array, so recursively
|
||||
// check those as well.
|
||||
if (*mapped_url != GURL(url::kAboutBlankURL)) {
|
||||
if (*observer.mapped_url() != GURL(url::kAboutBlankURL)) {
|
||||
// Nested URL maps map everything to "about:blank". They exist solely so
|
||||
// that top-level and nested component ads can't tell which one they are,
|
||||
// to prevent smuggling data based on whether an ad is loaded in a
|
||||
// top-level ad URL or a component ad URL.
|
||||
ValidatePendingAdComponentsMap(
|
||||
fenced_frame_url_mapping, add_to_new_map,
|
||||
*nested_pending_ad_components,
|
||||
*observer.pending_ad_components_map(),
|
||||
/*expected_mapped_urls=*/std::vector<GURL>());
|
||||
}
|
||||
}
|
||||
@ -107,22 +109,93 @@ TEST(FencedFrameURLMappingTest, AddAndConvert) {
|
||||
FencedFrameURLMapping fenced_frame_url_mapping;
|
||||
GURL test_url("https://foo.test");
|
||||
GURL urn_uuid = fenced_frame_url_mapping.AddFencedFrameURL(test_url);
|
||||
absl::optional<FencedFrameURLMapping::PendingAdComponentsMap> ad_components;
|
||||
EXPECT_EQ(test_url, fenced_frame_url_mapping
|
||||
.ConvertFencedFrameURNToURL(urn_uuid, ad_components)
|
||||
.value());
|
||||
EXPECT_EQ(absl::nullopt, ad_components);
|
||||
|
||||
TestFencedFrameURLMappingResultObserver observer;
|
||||
fenced_frame_url_mapping.ConvertFencedFrameURNToURL(urn_uuid, &observer);
|
||||
EXPECT_TRUE(observer.mapping_complete_observed());
|
||||
EXPECT_EQ(test_url, observer.mapped_url());
|
||||
EXPECT_EQ(absl::nullopt, observer.pending_ad_components_map());
|
||||
}
|
||||
|
||||
TEST(FencedFrameURLMappingTest, NonExistentUUID) {
|
||||
FencedFrameURLMapping fenced_frame_url_mapping;
|
||||
GURL urn_uuid("urn:uuid:c36973b5-e5d9-de59-e4c4-364f137b3c7a");
|
||||
absl::optional<FencedFrameURLMapping::PendingAdComponentsMap> ad_components;
|
||||
absl::optional<GURL> result =
|
||||
fenced_frame_url_mapping.ConvertFencedFrameURNToURL(urn_uuid,
|
||||
ad_components);
|
||||
EXPECT_EQ(absl::nullopt, result);
|
||||
EXPECT_EQ(absl::nullopt, ad_components);
|
||||
|
||||
TestFencedFrameURLMappingResultObserver observer;
|
||||
fenced_frame_url_mapping.ConvertFencedFrameURNToURL(urn_uuid, &observer);
|
||||
EXPECT_TRUE(observer.mapping_complete_observed());
|
||||
EXPECT_EQ(absl::nullopt, observer.mapped_url());
|
||||
EXPECT_EQ(absl::nullopt, observer.pending_ad_components_map());
|
||||
}
|
||||
|
||||
TEST(FencedFrameURLMappingTest, PendingMappedUUID_MappingSuccess) {
|
||||
FencedFrameURLMapping fenced_frame_url_mapping;
|
||||
const GURL urn_uuid = fenced_frame_url_mapping.GeneratePendingMappedURN();
|
||||
|
||||
TestFencedFrameURLMappingResultObserver observer;
|
||||
fenced_frame_url_mapping.ConvertFencedFrameURNToURL(urn_uuid, &observer);
|
||||
EXPECT_FALSE(observer.mapping_complete_observed());
|
||||
|
||||
fenced_frame_url_mapping.OnURNMappingResultDetermined(
|
||||
urn_uuid, GURL("https://foo.com"));
|
||||
|
||||
EXPECT_TRUE(observer.mapping_complete_observed());
|
||||
EXPECT_EQ(GURL("https://foo.com"), observer.mapped_url());
|
||||
EXPECT_EQ(absl::nullopt, observer.pending_ad_components_map());
|
||||
}
|
||||
|
||||
TEST(FencedFrameURLMappingTest, PendingMappedUUID_MappingFailure) {
|
||||
FencedFrameURLMapping fenced_frame_url_mapping;
|
||||
const GURL urn_uuid = fenced_frame_url_mapping.GeneratePendingMappedURN();
|
||||
|
||||
TestFencedFrameURLMappingResultObserver observer;
|
||||
fenced_frame_url_mapping.ConvertFencedFrameURNToURL(urn_uuid, &observer);
|
||||
EXPECT_FALSE(observer.mapping_complete_observed());
|
||||
|
||||
fenced_frame_url_mapping.OnURNMappingResultDetermined(urn_uuid,
|
||||
absl::nullopt);
|
||||
|
||||
EXPECT_TRUE(observer.mapping_complete_observed());
|
||||
EXPECT_EQ(absl::nullopt, observer.mapped_url());
|
||||
EXPECT_EQ(absl::nullopt, observer.pending_ad_components_map());
|
||||
}
|
||||
|
||||
TEST(FencedFrameURLMappingTest, RemoveObserverOnPendingMappedUUID) {
|
||||
FencedFrameURLMapping fenced_frame_url_mapping;
|
||||
const GURL urn_uuid = fenced_frame_url_mapping.GeneratePendingMappedURN();
|
||||
|
||||
TestFencedFrameURLMappingResultObserver observer;
|
||||
fenced_frame_url_mapping.ConvertFencedFrameURNToURL(urn_uuid, &observer);
|
||||
EXPECT_FALSE(observer.mapping_complete_observed());
|
||||
|
||||
fenced_frame_url_mapping.RemoveObserverForURN(urn_uuid, &observer);
|
||||
fenced_frame_url_mapping.OnURNMappingResultDetermined(
|
||||
urn_uuid, GURL("https://foo.com"));
|
||||
|
||||
EXPECT_FALSE(observer.mapping_complete_observed());
|
||||
}
|
||||
|
||||
TEST(FencedFrameURLMappingTest, RegisterTwoObservers) {
|
||||
FencedFrameURLMapping fenced_frame_url_mapping;
|
||||
const GURL urn_uuid = fenced_frame_url_mapping.GeneratePendingMappedURN();
|
||||
|
||||
TestFencedFrameURLMappingResultObserver observer1;
|
||||
fenced_frame_url_mapping.ConvertFencedFrameURNToURL(urn_uuid, &observer1);
|
||||
EXPECT_FALSE(observer1.mapping_complete_observed());
|
||||
|
||||
TestFencedFrameURLMappingResultObserver observer2;
|
||||
fenced_frame_url_mapping.ConvertFencedFrameURNToURL(urn_uuid, &observer2);
|
||||
EXPECT_FALSE(observer2.mapping_complete_observed());
|
||||
|
||||
fenced_frame_url_mapping.OnURNMappingResultDetermined(
|
||||
urn_uuid, GURL("https://foo.com"));
|
||||
|
||||
EXPECT_TRUE(observer1.mapping_complete_observed());
|
||||
EXPECT_EQ(GURL("https://foo.com"), observer1.mapped_url());
|
||||
EXPECT_EQ(absl::nullopt, observer1.pending_ad_components_map());
|
||||
EXPECT_TRUE(observer2.mapping_complete_observed());
|
||||
EXPECT_EQ(GURL("https://foo.com"), observer2.mapped_url());
|
||||
EXPECT_EQ(absl::nullopt, observer2.pending_ad_components_map());
|
||||
}
|
||||
|
||||
// Test the case `ad_component_urls` is empty. In this case, it should be filled
|
||||
@ -136,21 +209,22 @@ TEST(FencedFrameURLMappingTest,
|
||||
GURL urn_uuid = fenced_frame_url_mapping
|
||||
.AddFencedFrameURLWithInterestGroupAdComponentUrls(
|
||||
top_level_url, ad_component_urls);
|
||||
absl::optional<FencedFrameURLMapping::PendingAdComponentsMap>
|
||||
pending_ad_components;
|
||||
EXPECT_EQ(top_level_url, fenced_frame_url_mapping.ConvertFencedFrameURNToURL(
|
||||
urn_uuid, pending_ad_components));
|
||||
ASSERT_TRUE(pending_ad_components);
|
||||
|
||||
TestFencedFrameURLMappingResultObserver observer;
|
||||
fenced_frame_url_mapping.ConvertFencedFrameURNToURL(urn_uuid, &observer);
|
||||
EXPECT_TRUE(observer.mapping_complete_observed());
|
||||
EXPECT_EQ(top_level_url, observer.mapped_url());
|
||||
EXPECT_TRUE(observer.pending_ad_components_map());
|
||||
|
||||
// Call with `add_to_new_map` set to false and true, to simulate ShadowDOM
|
||||
// and MPArch behavior, respectively.
|
||||
ValidatePendingAdComponentsMap(&fenced_frame_url_mapping,
|
||||
/*add_to_new_map=*/true,
|
||||
*pending_ad_components,
|
||||
*observer.pending_ad_components_map(),
|
||||
/*expected_mapped_urls=*/{});
|
||||
ValidatePendingAdComponentsMap(&fenced_frame_url_mapping,
|
||||
/*add_to_new_map=*/false,
|
||||
*pending_ad_components,
|
||||
*observer.pending_ad_components_map(),
|
||||
/*expected_mapped_urls=*/{});
|
||||
}
|
||||
|
||||
@ -164,20 +238,23 @@ TEST(FencedFrameURLMappingTest,
|
||||
GURL urn_uuid = fenced_frame_url_mapping
|
||||
.AddFencedFrameURLWithInterestGroupAdComponentUrls(
|
||||
top_level_url, ad_component_urls);
|
||||
absl::optional<FencedFrameURLMapping::PendingAdComponentsMap>
|
||||
pending_ad_components;
|
||||
EXPECT_EQ(top_level_url, fenced_frame_url_mapping.ConvertFencedFrameURNToURL(
|
||||
urn_uuid, pending_ad_components));
|
||||
ASSERT_TRUE(pending_ad_components);
|
||||
|
||||
TestFencedFrameURLMappingResultObserver observer;
|
||||
fenced_frame_url_mapping.ConvertFencedFrameURNToURL(urn_uuid, &observer);
|
||||
EXPECT_TRUE(observer.mapping_complete_observed());
|
||||
EXPECT_EQ(top_level_url, observer.mapped_url());
|
||||
EXPECT_TRUE(observer.pending_ad_components_map());
|
||||
|
||||
// Call with `add_to_new_map` set to false and true, to simulate ShadowDOM
|
||||
// and MPArch behavior, respectively.
|
||||
ValidatePendingAdComponentsMap(&fenced_frame_url_mapping,
|
||||
/*add_to_new_map=*/true,
|
||||
*pending_ad_components, ad_component_urls);
|
||||
*observer.pending_ad_components_map(),
|
||||
ad_component_urls);
|
||||
ValidatePendingAdComponentsMap(&fenced_frame_url_mapping,
|
||||
/*add_to_new_map=*/false,
|
||||
*pending_ad_components, ad_component_urls);
|
||||
*observer.pending_ad_components_map(),
|
||||
ad_component_urls);
|
||||
}
|
||||
|
||||
// Test the case `ad_component_urls` has the maximum number of allowed ad
|
||||
@ -195,20 +272,23 @@ TEST(FencedFrameURLMappingTest,
|
||||
GURL urn_uuid = fenced_frame_url_mapping
|
||||
.AddFencedFrameURLWithInterestGroupAdComponentUrls(
|
||||
top_level_url, ad_component_urls);
|
||||
absl::optional<FencedFrameURLMapping::PendingAdComponentsMap>
|
||||
pending_ad_components;
|
||||
EXPECT_EQ(top_level_url, fenced_frame_url_mapping.ConvertFencedFrameURNToURL(
|
||||
urn_uuid, pending_ad_components));
|
||||
ASSERT_TRUE(pending_ad_components);
|
||||
|
||||
TestFencedFrameURLMappingResultObserver observer;
|
||||
fenced_frame_url_mapping.ConvertFencedFrameURNToURL(urn_uuid, &observer);
|
||||
EXPECT_TRUE(observer.mapping_complete_observed());
|
||||
EXPECT_EQ(top_level_url, observer.mapped_url());
|
||||
EXPECT_TRUE(observer.pending_ad_components_map());
|
||||
|
||||
// Call with `add_to_new_map` set to false and true, to simulate ShadowDOM
|
||||
// and MPArch behavior, respectively.
|
||||
ValidatePendingAdComponentsMap(&fenced_frame_url_mapping,
|
||||
/*add_to_new_map=*/true,
|
||||
*pending_ad_components, ad_component_urls);
|
||||
*observer.pending_ad_components_map(),
|
||||
ad_component_urls);
|
||||
ValidatePendingAdComponentsMap(&fenced_frame_url_mapping,
|
||||
/*add_to_new_map=*/false,
|
||||
*pending_ad_components, ad_component_urls);
|
||||
*observer.pending_ad_components_map(),
|
||||
ad_component_urls);
|
||||
}
|
||||
|
||||
// Test the case `ad_component_urls` has the maximum number of allowed ad
|
||||
@ -225,21 +305,22 @@ TEST(
|
||||
GURL urn_uuid = fenced_frame_url_mapping
|
||||
.AddFencedFrameURLWithInterestGroupAdComponentUrls(
|
||||
top_level_url, ad_component_urls);
|
||||
absl::optional<FencedFrameURLMapping::PendingAdComponentsMap>
|
||||
pending_ad_components;
|
||||
EXPECT_EQ(top_level_url, fenced_frame_url_mapping.ConvertFencedFrameURNToURL(
|
||||
urn_uuid, pending_ad_components));
|
||||
ASSERT_TRUE(pending_ad_components);
|
||||
|
||||
TestFencedFrameURLMappingResultObserver observer;
|
||||
fenced_frame_url_mapping.ConvertFencedFrameURNToURL(urn_uuid, &observer);
|
||||
EXPECT_TRUE(observer.mapping_complete_observed());
|
||||
EXPECT_EQ(top_level_url, observer.mapped_url());
|
||||
EXPECT_TRUE(observer.pending_ad_components_map());
|
||||
|
||||
// Call with `add_to_new_map` set to false and true, to simulate ShadowDOM
|
||||
// and MPArch behavior, respectively.
|
||||
ValidatePendingAdComponentsMap(&fenced_frame_url_mapping,
|
||||
/*add_to_new_map=*/true,
|
||||
*pending_ad_components,
|
||||
*observer.pending_ad_components_map(),
|
||||
/*expected_mapped_urls=*/ad_component_urls);
|
||||
ValidatePendingAdComponentsMap(&fenced_frame_url_mapping,
|
||||
/*add_to_new_map=*/false,
|
||||
*pending_ad_components,
|
||||
*observer.pending_ad_components_map(),
|
||||
/*expected_mapped_urls=*/ad_component_urls);
|
||||
}
|
||||
|
||||
|
@ -38,6 +38,7 @@
|
||||
#include "content/services/auction_worklet/public/mojom/auction_worklet_service.mojom.h"
|
||||
#include "content/services/auction_worklet/public/mojom/bidder_worklet.mojom.h"
|
||||
#include "content/test/test_content_browser_client.h"
|
||||
#include "content/test/test_fenced_frame_url_mapping_result_observer.h"
|
||||
#include "mojo/public/cpp/bindings/remote.h"
|
||||
#include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
@ -410,13 +411,14 @@ class AdAuctionServiceImplTest : public RenderViewHostTestHarness {
|
||||
}
|
||||
|
||||
absl::optional<GURL> ConvertFencedFrameURNToURL(const GURL& urn_url) {
|
||||
const FencedFrameURLMapping& fenced_frame_urls_map =
|
||||
TestFencedFrameURLMappingResultObserver observer;
|
||||
FencedFrameURLMapping& fenced_frame_urls_map =
|
||||
static_cast<RenderFrameHostImpl*>(main_rfh())
|
||||
->GetPage()
|
||||
.fenced_frame_urls_map();
|
||||
absl::optional<FencedFrameURLMapping::PendingAdComponentsMap> ignored;
|
||||
return fenced_frame_urls_map.ConvertFencedFrameURNToURL(
|
||||
const_cast<GURL&>(urn_url), ignored);
|
||||
fenced_frame_urls_map.ConvertFencedFrameURNToURL(urn_url, &observer);
|
||||
return observer.mapped_url();
|
||||
}
|
||||
|
||||
// Create a new AdAuctionServiceImpl and use it to try and join
|
||||
|
@ -43,6 +43,7 @@
|
||||
#include "content/public/test/url_loader_monitor.h"
|
||||
#include "content/shell/browser/shell.h"
|
||||
#include "content/test/test_content_browser_client.h"
|
||||
#include "content/test/test_fenced_frame_url_mapping_result_observer.h"
|
||||
#include "net/base/isolation_info.h"
|
||||
#include "net/base/network_isolation_key.h"
|
||||
#include "net/dns/mock_host_resolver.h"
|
||||
@ -462,9 +463,11 @@ class InterestGroupBrowserTest : public ContentBrowserTest {
|
||||
GURL urn_url = GURL(result.ExtractString());
|
||||
EXPECT_TRUE(urn_url.is_valid());
|
||||
EXPECT_EQ(url::kUrnScheme, urn_url.scheme_piece());
|
||||
absl::optional<GURL> maybe_url = ConvertFencedFrameURNToURL(urn_url);
|
||||
EXPECT_TRUE(maybe_url) << urn_url;
|
||||
return maybe_url->spec();
|
||||
|
||||
TestFencedFrameURLMappingResultObserver observer;
|
||||
ConvertFencedFrameURNToURL(urn_url, &observer);
|
||||
EXPECT_TRUE(observer.mapped_url()) << urn_url;
|
||||
return observer.mapped_url()->spec();
|
||||
}
|
||||
|
||||
// Navigates an iframe with the id="test_iframe" to the provided URL and
|
||||
@ -494,10 +497,13 @@ class InterestGroupBrowserTest : public ContentBrowserTest {
|
||||
GURL urn_url = GURL(result.ExtractString());
|
||||
EXPECT_TRUE(urn_url.is_valid());
|
||||
EXPECT_EQ(url::kUrnScheme, urn_url.scheme_piece());
|
||||
absl::optional<GURL> maybe_url = ConvertFencedFrameURNToURL(urn_url);
|
||||
EXPECT_TRUE(maybe_url) << urn_url;
|
||||
|
||||
TestFencedFrameURLMappingResultObserver observer;
|
||||
ConvertFencedFrameURNToURL(urn_url, &observer);
|
||||
EXPECT_TRUE(observer.mapped_url()) << urn_url;
|
||||
|
||||
NavigateIframeAndCheckURL(web_contents(), urn_url, expected_url);
|
||||
EXPECT_EQ(expected_url, maybe_url);
|
||||
EXPECT_EQ(expected_url, observer.mapped_url());
|
||||
}
|
||||
|
||||
// If `execution_target` is non-null, uses it as the target. Otherwise, uses
|
||||
@ -691,18 +697,18 @@ class InterestGroupBrowserTest : public ContentBrowserTest {
|
||||
feature.c_str(), api.c_str());
|
||||
}
|
||||
|
||||
absl::optional<GURL> ConvertFencedFrameURNToURL(
|
||||
void ConvertFencedFrameURNToURL(
|
||||
const GURL& urn_url,
|
||||
TestFencedFrameURLMappingResultObserver* observer,
|
||||
const absl::optional<ToRenderFrameHost> execution_target =
|
||||
absl::nullopt) {
|
||||
ToRenderFrameHost adapter(execution_target ? *execution_target : shell());
|
||||
const FencedFrameURLMapping& fenced_frame_urls_map =
|
||||
FencedFrameURLMapping& fenced_frame_urls_map =
|
||||
static_cast<RenderFrameHostImpl*>(adapter.render_frame_host())
|
||||
->GetPage()
|
||||
.fenced_frame_urls_map();
|
||||
absl::optional<FencedFrameURLMapping::PendingAdComponentsMap> ignored;
|
||||
return fenced_frame_urls_map.ConvertFencedFrameURNToURL(
|
||||
const_cast<GURL&>(urn_url), ignored);
|
||||
fenced_frame_urls_map.ConvertFencedFrameURNToURL(urn_url, observer);
|
||||
}
|
||||
|
||||
WebContentsImpl* web_contents() const {
|
||||
@ -975,9 +981,11 @@ interestGroupBuyers: [$1]
|
||||
EXPECT_NE((*all_component_urls)[i], (*all_component_urls)[j]);
|
||||
|
||||
// Check URNs are mapped to the values in `expected_ad_component_urls`.
|
||||
absl::optional<GURL> mapped_url = ConvertFencedFrameURNToURL(
|
||||
(*all_component_urls)[i], render_frame_host);
|
||||
EXPECT_EQ(expected_ad_component_urls[i], mapped_url);
|
||||
TestFencedFrameURLMappingResultObserver observer;
|
||||
ConvertFencedFrameURNToURL((*all_component_urls)[i], &observer,
|
||||
render_frame_host);
|
||||
EXPECT_TRUE(observer.mapped_url());
|
||||
EXPECT_EQ(expected_ad_component_urls[i], observer.mapped_url());
|
||||
}
|
||||
|
||||
// Make sure smaller values passed to GetAdAuctionComponentsInJS() return
|
||||
@ -3137,12 +3145,12 @@ IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest, ValidateWorkletParameters) {
|
||||
NavigateToURL(shell(), https_server_->GetURL(kTopFrameHost, "/echo")));
|
||||
GURL seller_script_url = https_server_->GetURL(
|
||||
kSellerHost, "/interest_group/decision_argument_validator.js");
|
||||
EXPECT_EQ(
|
||||
"https://example.com/render",
|
||||
ConvertFencedFrameURNToURL(
|
||||
GURL(EvalJs(shell(),
|
||||
JsReplace(
|
||||
R"(
|
||||
|
||||
TestFencedFrameURLMappingResultObserver observer;
|
||||
ConvertFencedFrameURNToURL(
|
||||
GURL(EvalJs(shell(),
|
||||
JsReplace(
|
||||
R"(
|
||||
(async function() {
|
||||
return await navigator.runAdAuction({
|
||||
seller: $1,
|
||||
@ -3154,14 +3162,14 @@ IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest, ValidateWorkletParameters) {
|
||||
perBuyerSignals: {$4: {signalsForBuyer: 1}, $5: {signalsForBuyer: 2}}
|
||||
});
|
||||
})())",
|
||||
url::Origin::Create(seller_script_url),
|
||||
seller_script_url,
|
||||
https_server_->GetURL(
|
||||
kSellerHost,
|
||||
"/interest_group/trusted_scoring_signals.json"),
|
||||
bidder_origin, second_bidder_origin))
|
||||
.ExtractString()))
|
||||
->spec());
|
||||
url::Origin::Create(seller_script_url), seller_script_url,
|
||||
https_server_->GetURL(
|
||||
kSellerHost,
|
||||
"/interest_group/trusted_scoring_signals.json"),
|
||||
bidder_origin, second_bidder_origin))
|
||||
.ExtractString()),
|
||||
&observer);
|
||||
EXPECT_EQ(GURL("https://example.com/render"), observer.mapped_url());
|
||||
}
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
|
||||
@ -3333,10 +3341,11 @@ function validateAuctionConfig(auctionConfig) {
|
||||
https_server_->GetURL("a.test", kTrustedBiddingSignalsPath),
|
||||
https_server_->GetURL("a.test", kBiddingLogicPath))));
|
||||
|
||||
EXPECT_EQ("https://example.com/render",
|
||||
ConvertFencedFrameURNToURL(
|
||||
GURL(EvalJs(shell(), JsReplace(
|
||||
R"(
|
||||
TestFencedFrameURLMappingResultObserver observer;
|
||||
ConvertFencedFrameURNToURL(
|
||||
GURL(EvalJs(shell(),
|
||||
JsReplace(
|
||||
R"(
|
||||
(async function() {
|
||||
return await navigator.runAdAuction({
|
||||
seller: $1,
|
||||
@ -3347,11 +3356,12 @@ function validateAuctionConfig(auctionConfig) {
|
||||
perBuyerSignals: {$1: 5}
|
||||
});
|
||||
})())",
|
||||
test_origin,
|
||||
https_server_->GetURL(
|
||||
"a.test", kDecisionLogicPath)))
|
||||
.ExtractString()))
|
||||
->spec());
|
||||
test_origin,
|
||||
https_server_->GetURL("a.test", kDecisionLogicPath)))
|
||||
.ExtractString()),
|
||||
&observer);
|
||||
|
||||
EXPECT_EQ(GURL("https://example.com/render"), observer.mapped_url());
|
||||
}
|
||||
|
||||
// Make sure that qutting with a live auction doesn't crash.
|
||||
@ -3616,10 +3626,12 @@ class InterestGroupBrowserTestRunAdAuctionBypassBlink
|
||||
}));
|
||||
run_loop.Run();
|
||||
if (maybe_url) {
|
||||
absl::optional<GURL> decoded_URL = ConvertFencedFrameURNToURL(*maybe_url);
|
||||
TestFencedFrameURLMappingResultObserver observer;
|
||||
ConvertFencedFrameURNToURL(*maybe_url, &observer);
|
||||
EXPECT_TRUE(observer.mapped_url());
|
||||
NavigateIframeAndCheckURL(web_contents(), *maybe_url,
|
||||
decoded_URL.value_or(GURL()));
|
||||
return decoded_URL;
|
||||
*observer.mapped_url());
|
||||
return *observer.mapped_url();
|
||||
}
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "content/browser/fenced_frame/fenced_frame_url_mapping.h"
|
||||
#include "content/browser/renderer_host/frame_tree.h"
|
||||
#include "content/browser/renderer_host/frame_tree_node.h"
|
||||
#include "content/browser/renderer_host/navigation_request.h"
|
||||
#include "content/browser/renderer_host/render_frame_host_impl.h"
|
||||
#include "content/browser/renderer_host/render_view_host_impl.h"
|
||||
#include "content/browser/web_contents/web_contents_impl.h"
|
||||
@ -52,6 +53,84 @@ EvalJsResult GetOriginFromRenderer(FrameTreeNode* node) {
|
||||
return EvalJs(node, "self.origin");
|
||||
}
|
||||
|
||||
// This method takes in a RenderFrameHostImpl that must be inside a fenced frame
|
||||
// FrameTree, and returns the FencedFrame* object that represents this inner
|
||||
// FrameTree from the outer FrameTree.
|
||||
FencedFrame* GetMatchingFencedFrameInOuterFrameTree(RenderFrameHostImpl* rfh) {
|
||||
EXPECT_EQ(blink::features::kFencedFramesImplementationTypeParam.Get(),
|
||||
blink::features::FencedFramesImplementationType::kMPArch);
|
||||
// `rfh` doesn't always have to be a root frame, since this needs to work
|
||||
// for arbitrary frames within a fenced frame.
|
||||
EXPECT_TRUE(rfh->frame_tree_node()->IsInFencedFrameTree());
|
||||
|
||||
RenderFrameHostImpl* outer_delegate_frame =
|
||||
rfh->GetMainFrame()->GetParentOrOuterDocument();
|
||||
|
||||
std::vector<FencedFrame*> fenced_frames =
|
||||
outer_delegate_frame->GetFencedFrames();
|
||||
EXPECT_FALSE(fenced_frames.empty());
|
||||
|
||||
for (FencedFrame* fenced_frame : fenced_frames) {
|
||||
if (fenced_frame->GetInnerRoot() == rfh->GetMainFrame()) {
|
||||
return fenced_frame;
|
||||
}
|
||||
}
|
||||
|
||||
NOTREACHED();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
class FencedFrameNavigationObserver {
|
||||
public:
|
||||
explicit FencedFrameNavigationObserver(RenderFrameHostImpl* fenced_frame_rfh)
|
||||
: frame_tree_node_(fenced_frame_rfh->frame_tree_node()) {
|
||||
EXPECT_TRUE(frame_tree_node_->IsInFencedFrameTree());
|
||||
|
||||
if (blink::features::kFencedFramesImplementationTypeParam.Get() ==
|
||||
blink::features::FencedFramesImplementationType::kShadowDOM) {
|
||||
observer_for_shadow_dom_ =
|
||||
std::make_unique<TestFrameNavigationObserver>(fenced_frame_rfh);
|
||||
return;
|
||||
}
|
||||
|
||||
fenced_frame_for_mparch_ =
|
||||
GetMatchingFencedFrameInOuterFrameTree(fenced_frame_rfh);
|
||||
}
|
||||
|
||||
void Wait(net::Error expected_net_error_code) {
|
||||
if (blink::features::kFencedFramesImplementationTypeParam.Get() ==
|
||||
blink::features::FencedFramesImplementationType::kShadowDOM) {
|
||||
DCHECK(observer_for_shadow_dom_);
|
||||
observer_for_shadow_dom_->Wait();
|
||||
EXPECT_EQ(observer_for_shadow_dom_->last_net_error_code(),
|
||||
expected_net_error_code);
|
||||
return;
|
||||
}
|
||||
|
||||
DCHECK(fenced_frame_for_mparch_);
|
||||
fenced_frame_for_mparch_->WaitForDidStopLoadingForTesting();
|
||||
|
||||
EXPECT_EQ(frame_tree_node_->current_frame_host()->IsErrorDocument(),
|
||||
expected_net_error_code != net::OK);
|
||||
}
|
||||
|
||||
private:
|
||||
FrameTreeNode* frame_tree_node_ = nullptr;
|
||||
|
||||
// For the ShadowDOM version of fenced frames, we can just use a
|
||||
// `TestFrameNavigationObserver` as normal directly on the frame that is
|
||||
// navigating.
|
||||
std::unique_ptr<TestFrameNavigationObserver> observer_for_shadow_dom_;
|
||||
|
||||
// For the MPArch version of fenced frames, rely on
|
||||
// FencedFrame::WaitForDidStopLoadingForTesting. `TestFrameNavigationObserver`
|
||||
// does not fully work inside of a fenced frame FrameTree: `WaitForCommit()`
|
||||
// works, but `Wait()` always times out because it expects to hear the
|
||||
// DidFinishedLoad event from the outer WebContents, which is not communicated
|
||||
// by nested FrameTrees.
|
||||
FencedFrame* fenced_frame_for_mparch_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
class FrameTreeBrowserTest : public ContentBrowserTest {
|
||||
@ -886,33 +965,9 @@ class FencedFrameTreeBrowserTest
|
||||
RenderFrameHostImpl* fenced_frame_rfh,
|
||||
const std::string& script,
|
||||
net::Error expected_net_error_code = net::OK) {
|
||||
// For the ShadowDOM version of fenced frames, we can just use a
|
||||
// `TestFrameNavigationObserver` as normal directly on the frame that is
|
||||
// navigating.
|
||||
if (GetParam() ==
|
||||
blink::features::FencedFramesImplementationType::kShadowDOM) {
|
||||
TestFrameNavigationObserver observer(fenced_frame_rfh);
|
||||
EXPECT_TRUE(ExecJs(target_rfh, script));
|
||||
observer.Wait();
|
||||
EXPECT_EQ(observer.last_net_error_code(), expected_net_error_code);
|
||||
return;
|
||||
}
|
||||
|
||||
FrameTreeNode* frame_tree_node = fenced_frame_rfh->frame_tree_node();
|
||||
EXPECT_TRUE(frame_tree_node->IsInFencedFrameTree());
|
||||
|
||||
// For the MPArch version of fenced frames, `TestFrameNavigationObserver`
|
||||
// does not fully work inside of a fenced frame FrameTree. `WaitForCommit()`
|
||||
// works, but `Wait()` always times out because it expects to hear the
|
||||
// DidFinishedLoad event from the outer WebContents, which is not
|
||||
// communicated by nested FrameTrees.
|
||||
FencedFrame* fenced_frame =
|
||||
GetMatchingFencedFrameInOuterFrameTree(fenced_frame_rfh);
|
||||
FencedFrameNavigationObserver observer(fenced_frame_rfh);
|
||||
EXPECT_TRUE(ExecJs(target_rfh, script));
|
||||
fenced_frame->WaitForDidStopLoadingForTesting();
|
||||
// `rfh` might be destroyed and invalid at this point.
|
||||
EXPECT_EQ(frame_tree_node->current_frame_host()->IsErrorDocument(),
|
||||
expected_net_error_code != net::OK);
|
||||
observer.Wait(expected_net_error_code);
|
||||
}
|
||||
|
||||
void WaitForDidStopLoadingForTesting(RenderFrameHostImpl* fenced_frame_rfh) {
|
||||
@ -1047,34 +1102,6 @@ class FencedFrameTreeBrowserTest
|
||||
}
|
||||
|
||||
private:
|
||||
// This method takes in a RenderFrameHostImpl that must be inside a fenced
|
||||
// frame FrameTree, and returns the FencedFrame* object that represents this
|
||||
// inner FrameTree from the outer FrameTree.
|
||||
FencedFrame* GetMatchingFencedFrameInOuterFrameTree(
|
||||
RenderFrameHostImpl* rfh) {
|
||||
EXPECT_EQ(GetParam(),
|
||||
blink::features::FencedFramesImplementationType::kMPArch);
|
||||
// `rfh` doesn't always have to be a root frame, since this needs to work
|
||||
// for arbitrary frames within a fenced frame.
|
||||
EXPECT_TRUE(rfh->frame_tree_node()->IsInFencedFrameTree());
|
||||
|
||||
RenderFrameHostImpl* outer_delegate_frame =
|
||||
rfh->GetMainFrame()->GetParentOrOuterDocument();
|
||||
|
||||
std::vector<FencedFrame*> fenced_frames =
|
||||
outer_delegate_frame->GetFencedFrames();
|
||||
EXPECT_FALSE(fenced_frames.empty());
|
||||
|
||||
for (FencedFrame* fenced_frame : fenced_frames) {
|
||||
if (fenced_frame->GetInnerRoot() == rfh->GetMainFrame()) {
|
||||
return fenced_frame;
|
||||
}
|
||||
}
|
||||
|
||||
NOTREACHED();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
base::test::ScopedFeatureList scoped_feature_list_;
|
||||
base::Lock requests_lock_;
|
||||
std::map<std::string, std::string> cookie_headers_map_
|
||||
@ -1137,6 +1164,190 @@ IN_PROC_BROWSER_TEST_P(FencedFrameTreeBrowserTest,
|
||||
EXPECT_EQ(0, EvalJs(root, "window.frames.length"));
|
||||
}
|
||||
|
||||
IN_PROC_BROWSER_TEST_P(
|
||||
FencedFrameTreeBrowserTest,
|
||||
FencedFrameNavigationWithPendingMappedUUID_MappingSuccess) {
|
||||
GURL main_url = https_server()->GetURL("b.test", "/hello.html");
|
||||
EXPECT_TRUE(NavigateToURL(shell(), main_url));
|
||||
// It is safe to obtain the root frame tree node here, as it doesn't change.
|
||||
FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
|
||||
->GetPrimaryFrameTree()
|
||||
.root();
|
||||
|
||||
{
|
||||
EXPECT_TRUE(ExecJs(root,
|
||||
"var f = document.createElement('fencedframe');"
|
||||
"document.body.appendChild(f);"));
|
||||
}
|
||||
EXPECT_EQ(1U, root->child_count());
|
||||
FrameTreeNode* fenced_frame_root_node =
|
||||
GetFencedFrameRootNode(root->child_at(0));
|
||||
|
||||
EXPECT_TRUE(fenced_frame_root_node->IsFencedFrameRoot());
|
||||
EXPECT_TRUE(fenced_frame_root_node->IsInFencedFrameTree());
|
||||
|
||||
FencedFrameURLMapping& url_mapping =
|
||||
root->current_frame_host()->GetPage().fenced_frame_urls_map();
|
||||
|
||||
const GURL urn_uuid = url_mapping.GeneratePendingMappedURN();
|
||||
const GURL mapped_url =
|
||||
https_server()->GetURL("a.test", "/fenced_frames/title1.html");
|
||||
std::string navigate_urn_script = JsReplace("f.src = $1;", urn_uuid.spec());
|
||||
|
||||
FencedFrameNavigationObserver observer(
|
||||
fenced_frame_root_node->current_frame_host());
|
||||
|
||||
EXPECT_EQ(urn_uuid.spec(), EvalJs(root, navigate_urn_script));
|
||||
|
||||
// After the previous EvalJs, the NavigationRequest should have been created,
|
||||
// but may not have begun. Wait for BeginNavigation() and expect it to be
|
||||
// deferred on fenced frame url mapping.
|
||||
NavigationRequest* request = fenced_frame_root_node->navigation_request();
|
||||
if (!request->is_deferred_on_fenced_frame_url_mapping_for_testing()) {
|
||||
base::RunLoop run_loop;
|
||||
request->set_begin_navigation_callback_for_testing(
|
||||
run_loop.QuitWhenIdleClosure());
|
||||
run_loop.Run();
|
||||
|
||||
EXPECT_TRUE(request->is_deferred_on_fenced_frame_url_mapping_for_testing());
|
||||
}
|
||||
|
||||
EXPECT_TRUE(url_mapping.HasObserverForTesting(urn_uuid, request));
|
||||
|
||||
// Trigger the mapping to resume the deferred navigation.
|
||||
url_mapping.OnURNMappingResultDetermined(urn_uuid, mapped_url);
|
||||
|
||||
EXPECT_FALSE(url_mapping.HasObserverForTesting(urn_uuid, request));
|
||||
|
||||
observer.Wait(net::OK);
|
||||
|
||||
EXPECT_EQ(
|
||||
mapped_url,
|
||||
fenced_frame_root_node->current_frame_host()->GetLastCommittedURL());
|
||||
}
|
||||
|
||||
IN_PROC_BROWSER_TEST_P(
|
||||
FencedFrameTreeBrowserTest,
|
||||
FencedFrameNavigationWithPendingMappedUUID_MappingFailure) {
|
||||
GURL main_url = https_server()->GetURL("b.test", "/hello.html");
|
||||
EXPECT_TRUE(NavigateToURL(shell(), main_url));
|
||||
// It is safe to obtain the root frame tree node here, as it doesn't change.
|
||||
FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
|
||||
->GetPrimaryFrameTree()
|
||||
.root();
|
||||
|
||||
{
|
||||
EXPECT_TRUE(ExecJs(root,
|
||||
"var f = document.createElement('fencedframe');"
|
||||
"document.body.appendChild(f);"));
|
||||
}
|
||||
EXPECT_EQ(1U, root->child_count());
|
||||
FrameTreeNode* fenced_frame_root_node =
|
||||
GetFencedFrameRootNode(root->child_at(0));
|
||||
|
||||
EXPECT_TRUE(fenced_frame_root_node->IsFencedFrameRoot());
|
||||
EXPECT_TRUE(fenced_frame_root_node->IsInFencedFrameTree());
|
||||
|
||||
FencedFrameURLMapping& url_mapping =
|
||||
root->current_frame_host()->GetPage().fenced_frame_urls_map();
|
||||
|
||||
const GURL urn_uuid = url_mapping.GeneratePendingMappedURN();
|
||||
std::string navigate_urn_script = JsReplace("f.src = $1;", urn_uuid.spec());
|
||||
|
||||
FencedFrameNavigationObserver observer(
|
||||
fenced_frame_root_node->current_frame_host());
|
||||
|
||||
EXPECT_EQ(urn_uuid.spec(), EvalJs(root, navigate_urn_script));
|
||||
|
||||
// After the previous EvalJs, the NavigationRequest should have been created,
|
||||
// but may not have begun. Wait for BeginNavigation() and expect it to be
|
||||
// deferred on fenced frame url mapping.
|
||||
NavigationRequest* request = fenced_frame_root_node->navigation_request();
|
||||
if (!request->is_deferred_on_fenced_frame_url_mapping_for_testing()) {
|
||||
base::RunLoop run_loop;
|
||||
request->set_begin_navigation_callback_for_testing(
|
||||
run_loop.QuitWhenIdleClosure());
|
||||
run_loop.Run();
|
||||
|
||||
EXPECT_TRUE(request->is_deferred_on_fenced_frame_url_mapping_for_testing());
|
||||
}
|
||||
|
||||
EXPECT_TRUE(url_mapping.HasObserverForTesting(urn_uuid, request));
|
||||
|
||||
// Trigger the mapping to resume the deferred navigation.
|
||||
url_mapping.OnURNMappingResultDetermined(urn_uuid, absl::nullopt);
|
||||
|
||||
EXPECT_FALSE(url_mapping.HasObserverForTesting(urn_uuid, request));
|
||||
|
||||
observer.Wait(net::ERR_INVALID_URL);
|
||||
}
|
||||
|
||||
IN_PROC_BROWSER_TEST_P(
|
||||
FencedFrameTreeBrowserTest,
|
||||
FencedFrameNavigationWithPendingMappedUUID_NavigationCanceledDuringDeferring) {
|
||||
GURL main_url = https_server()->GetURL("b.test", "/hello.html");
|
||||
EXPECT_TRUE(NavigateToURL(shell(), main_url));
|
||||
// It is safe to obtain the root frame tree node here, as it doesn't change.
|
||||
FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
|
||||
->GetPrimaryFrameTree()
|
||||
.root();
|
||||
|
||||
{
|
||||
EXPECT_TRUE(ExecJs(root,
|
||||
"var f = document.createElement('fencedframe');"
|
||||
"document.body.appendChild(f);"));
|
||||
}
|
||||
EXPECT_EQ(1U, root->child_count());
|
||||
FrameTreeNode* fenced_frame_root_node =
|
||||
GetFencedFrameRootNode(root->child_at(0));
|
||||
|
||||
EXPECT_TRUE(fenced_frame_root_node->IsFencedFrameRoot());
|
||||
EXPECT_TRUE(fenced_frame_root_node->IsInFencedFrameTree());
|
||||
|
||||
FencedFrameURLMapping& url_mapping =
|
||||
root->current_frame_host()->GetPage().fenced_frame_urls_map();
|
||||
|
||||
const GURL urn_uuid = url_mapping.GeneratePendingMappedURN();
|
||||
const GURL mapped_url =
|
||||
https_server()->GetURL("a.test", "/fenced_frames/title1.html");
|
||||
std::string navigate_urn_script = JsReplace("f.src = $1;", urn_uuid.spec());
|
||||
|
||||
FencedFrameNavigationObserver observer(
|
||||
fenced_frame_root_node->current_frame_host());
|
||||
|
||||
EXPECT_EQ(urn_uuid.spec(), EvalJs(root, navigate_urn_script));
|
||||
|
||||
// After the previous EvalJs, the NavigationRequest should have been created,
|
||||
// but may not have begun. Wait for BeginNavigation() and expect it to be
|
||||
// deferred on fenced frame url mapping.
|
||||
NavigationRequest* request = fenced_frame_root_node->navigation_request();
|
||||
if (!request->is_deferred_on_fenced_frame_url_mapping_for_testing()) {
|
||||
base::RunLoop run_loop;
|
||||
request->set_begin_navigation_callback_for_testing(
|
||||
run_loop.QuitWhenIdleClosure());
|
||||
run_loop.Run();
|
||||
|
||||
EXPECT_TRUE(request->is_deferred_on_fenced_frame_url_mapping_for_testing());
|
||||
}
|
||||
|
||||
EXPECT_TRUE(url_mapping.HasObserverForTesting(urn_uuid, request));
|
||||
|
||||
// Navigate to a new URL. The previous navigation should have been canceled.
|
||||
// And `request` should have been removed from `url_mapping`.
|
||||
const GURL new_url =
|
||||
https_server()->GetURL("a.test", "/fenced_frames/empty.html");
|
||||
EXPECT_EQ(new_url.spec(),
|
||||
EvalJs(root, JsReplace("f.src = $1;", new_url.spec())));
|
||||
|
||||
EXPECT_FALSE(url_mapping.HasObserverForTesting(urn_uuid, request));
|
||||
|
||||
observer.Wait(net::OK);
|
||||
|
||||
EXPECT_EQ(
|
||||
new_url,
|
||||
fenced_frame_root_node->current_frame_host()->GetLastCommittedURL());
|
||||
}
|
||||
|
||||
IN_PROC_BROWSER_TEST_P(FencedFrameTreeBrowserTest,
|
||||
CheckFencedFrameCookiesNavigation) {
|
||||
// Create an a.test main page and set cookies. Then create a same-origin
|
||||
|
@ -1699,6 +1699,11 @@ NavigationRequest::~NavigationRequest() {
|
||||
navigation_handle_proxy_->DidFinish();
|
||||
#endif
|
||||
|
||||
if (is_deferred_on_fenced_frame_url_mapping_) {
|
||||
CHECK(NeedFencedFrameURLMapping());
|
||||
GetFencedFrameURLMap().RemoveObserverForURN(common_params_->url, this);
|
||||
}
|
||||
|
||||
if (IsNavigationStarted()) {
|
||||
GetDelegate()->DidFinishNavigation(this);
|
||||
ProcessOriginAgentClusterEndResult();
|
||||
@ -1752,11 +1757,37 @@ void NavigationRequest::BeginNavigation() {
|
||||
DCHECK(!render_frame_host_);
|
||||
ScopedCrashKeys crash_keys(*this);
|
||||
|
||||
if (begin_navigation_callback_for_testing_) {
|
||||
std::move(begin_navigation_callback_for_testing_).Run();
|
||||
}
|
||||
|
||||
if (MaybeStartPrerenderingActivationChecks()) {
|
||||
// BeginNavigationImpl() will be called after the checks.
|
||||
return;
|
||||
}
|
||||
|
||||
MaybeAssignInvalidPrerenderFrameTreeNodeId();
|
||||
|
||||
// If this is a fenced frame with a urn:uuid, then convert it to a url before
|
||||
// starting the navigation; otherwise, proceed directly with the navigation.
|
||||
if (NeedFencedFrameURLMapping()) {
|
||||
FencedFrameURLMapping& fenced_frame_urls_map = GetFencedFrameURLMap();
|
||||
|
||||
// If the mapping finishes synchronously, OnFencedFrameURLMappingComplete
|
||||
// will be synchronously called and will reset
|
||||
// `is_deferred_on_fenced_frame_url_mapping_` to false.
|
||||
is_deferred_on_fenced_frame_url_mapping_ = true;
|
||||
|
||||
// OnFencedFrameURLMappingComplete() and BeginNavigationImpl() will be
|
||||
// invoked after this.
|
||||
fenced_frame_urls_map.ConvertFencedFrameURNToURL(common_params_->url,
|
||||
/*observer=*/this);
|
||||
// DO NOT ADD CODE after this. The previous call to
|
||||
// ConvertFencedFrameURNToURL may cause the destruction of the
|
||||
// NavigationRequest.
|
||||
return;
|
||||
}
|
||||
|
||||
BeginNavigationImpl();
|
||||
}
|
||||
|
||||
@ -1831,46 +1862,53 @@ void NavigationRequest::OnPrerenderingActivationChecksComplete(
|
||||
is_potentially_prerendered_page_activation_for_testing_ = false;
|
||||
commit_deferrer_.reset();
|
||||
|
||||
// We can only activate top-level pages, which can never be at a fenced frame
|
||||
// URN that needs to be mapped.
|
||||
CHECK(!NeedFencedFrameURLMapping());
|
||||
|
||||
BeginNavigationImpl();
|
||||
// DO NOT ADD CODE after this. The previous call to BeginNavigationImpl may
|
||||
// cause the destruction of the NavigationRequest.
|
||||
// DO NOT ADD CODE after this. The previous call to
|
||||
// BeginNavigationImpl may cause the destruction of the NavigationRequest.
|
||||
}
|
||||
|
||||
void NavigationRequest::BeginNavigationImpl() {
|
||||
base::ElapsedTimer timer;
|
||||
SetState(WILL_START_NAVIGATION);
|
||||
FencedFrameURLMapping& NavigationRequest::GetFencedFrameURLMap() {
|
||||
// `inner_frame_tree` is true for navigations inside the main frame of a
|
||||
// nested fenced frame's `FrameTree`, and false otherwise. This is only the
|
||||
// case for the MPArch implementation of fenced frames.
|
||||
bool is_inner_frame_tree =
|
||||
frame_tree_node_->frame_tree()->type() == FrameTree::Type::kFencedFrame;
|
||||
FrameTreeNode* node_to_use =
|
||||
is_inner_frame_tree
|
||||
? frame_tree_node_->render_manager()->GetOuterDelegateNode()
|
||||
: frame_tree_node_;
|
||||
|
||||
bool convert_urn_uuid_urls = frame_tree_node_->IsFencedFrameRoot() ||
|
||||
(!frame_tree_node_->IsMainFrame() &&
|
||||
blink::features::IsAllowURNsInIframeEnabled());
|
||||
// If this is a fenced frame or iframe with a urn:uuid then convert it to a
|
||||
// url before starting the request.
|
||||
if (convert_urn_uuid_urls &&
|
||||
FencedFrameURLMapping::IsValidUrnUuidURL(common_params_->url)) {
|
||||
// `inner_frame_tree` is true for navigations inside the main frame of a
|
||||
// nested fenced frame's `FrameTree`, and false otherwise. This is only the
|
||||
// case for the MPArch implementation of fenced frames.
|
||||
bool is_inner_frame_tree =
|
||||
frame_tree_node_->frame_tree()->type() == FrameTree::Type::kFencedFrame;
|
||||
FrameTreeNode* node_to_use =
|
||||
is_inner_frame_tree
|
||||
? frame_tree_node_->render_manager()->GetOuterDelegateNode()
|
||||
: frame_tree_node_;
|
||||
return node_to_use->current_frame_host()->GetPage().fenced_frame_urls_map();
|
||||
}
|
||||
|
||||
bool NavigationRequest::NeedFencedFrameURLMapping() {
|
||||
bool need_convert_urn_uuid_urls =
|
||||
frame_tree_node_->IsFencedFrameRoot() ||
|
||||
(!frame_tree_node_->IsMainFrame() &&
|
||||
blink::features::IsAllowURNsInIframeEnabled());
|
||||
|
||||
return need_convert_urn_uuid_urls &&
|
||||
FencedFrameURLMapping::IsValidUrnUuidURL(common_params_->url);
|
||||
}
|
||||
|
||||
void NavigationRequest::OnFencedFrameURLMappingComplete(
|
||||
absl::optional<GURL> mapped_url,
|
||||
absl::optional<FencedFrameURLMapping::PendingAdComponentsMap>
|
||||
pending_ad_components_map;
|
||||
absl::optional<GURL> mapped_url =
|
||||
node_to_use->current_frame_host()
|
||||
->GetPage()
|
||||
.fenced_frame_urls_map()
|
||||
.ConvertFencedFrameURNToURL(common_params_->url,
|
||||
pending_ad_components_map);
|
||||
pending_ad_components_map) {
|
||||
is_deferred_on_fenced_frame_url_mapping_ = false;
|
||||
|
||||
if (mapped_url) {
|
||||
common_params_->url = *mapped_url;
|
||||
commit_params_->original_url = *mapped_url;
|
||||
pending_ad_components_map_ = std::move(pending_ad_components_map);
|
||||
} else if (frame_tree_node_->IsFencedFrameRoot()) {
|
||||
if (mapped_url) {
|
||||
common_params_->url = mapped_url.value();
|
||||
commit_params_->original_url = mapped_url.value();
|
||||
// TODO(crbug/1281643): move into commit_params_->ad_auction_components
|
||||
// directly.
|
||||
pending_ad_components_map_ = std::move(pending_ad_components_map);
|
||||
} else {
|
||||
if (frame_tree_node_->IsFencedFrameRoot()) {
|
||||
StartNavigation();
|
||||
OnRequestFailedInternal(
|
||||
network::URLLoaderCompletionStatus(net::ERR_INVALID_URL),
|
||||
@ -1880,6 +1918,14 @@ void NavigationRequest::BeginNavigationImpl() {
|
||||
} // else (for iframes) try the urn as-is to maintain existing behavior.
|
||||
}
|
||||
|
||||
BeginNavigationImpl();
|
||||
// DO NOT ADD CODE after this. The previous call to BeginNavigationImpl may
|
||||
// cause the destruction of the NavigationRequest.
|
||||
}
|
||||
|
||||
void NavigationRequest::BeginNavigationImpl() {
|
||||
base::ElapsedTimer timer;
|
||||
SetState(WILL_START_NAVIGATION);
|
||||
#if defined(OS_ANDROID)
|
||||
base::WeakPtr<NavigationRequest> this_ptr(weak_factory_.GetWeakPtr());
|
||||
bool should_override_url_loading = false;
|
||||
@ -2065,13 +2111,7 @@ void NavigationRequest::StartNavigation() {
|
||||
is_synchronous_renderer_commit_);
|
||||
FrameTreeNode* frame_tree_node = frame_tree_node_;
|
||||
|
||||
if (blink::features::IsPrerender2Enabled() &&
|
||||
!prerender_frame_tree_node_id_.has_value()) {
|
||||
// This navigation won't activate a prerendered page. Otherwise,
|
||||
// `prerender_frame_tree_node_id_` should have already been set before this
|
||||
// in OnPrerenderingActivationChecksComplete().
|
||||
prerender_frame_tree_node_id_ = RenderFrameHost::kNoFrameTreeNodeId;
|
||||
}
|
||||
MaybeAssignInvalidPrerenderFrameTreeNodeId();
|
||||
|
||||
// This is needed to get site URLs and assign the expected RenderProcessHost.
|
||||
// This is not always the same as |source_site_instance_|, as it only depends
|
||||
@ -6989,6 +7029,7 @@ void NavigationRequest::CheckStateTransition(NavigationState state) const {
|
||||
}},
|
||||
{WAITING_FOR_RENDERER_RESPONSE, {
|
||||
WILL_START_NAVIGATION,
|
||||
WILL_START_REQUEST,
|
||||
}},
|
||||
{WILL_START_NAVIGATION, {
|
||||
WILL_START_REQUEST,
|
||||
@ -7279,6 +7320,16 @@ NavigationRequest::ComputeWebExposedIsolationInfo() {
|
||||
: WebExposedIsolationInfo::CreateIsolated(origin);
|
||||
}
|
||||
|
||||
void NavigationRequest::MaybeAssignInvalidPrerenderFrameTreeNodeId() {
|
||||
if (blink::features::IsPrerender2Enabled() &&
|
||||
!prerender_frame_tree_node_id_.has_value()) {
|
||||
// This navigation won't activate a prerendered page. Otherwise,
|
||||
// `prerender_frame_tree_node_id_` should have already been set before this
|
||||
// in OnPrerenderingActivationChecksComplete().
|
||||
prerender_frame_tree_node_id_ = RenderFrameHost::kNoFrameTreeNodeId;
|
||||
}
|
||||
}
|
||||
|
||||
NavigationRequest::ScopedCrashKeys::ScopedCrashKeys(
|
||||
NavigationRequest& navigation_request)
|
||||
: initiator_origin_(
|
||||
|
@ -112,6 +112,7 @@ class CONTENT_EXPORT NavigationRequest
|
||||
public NavigationURLLoaderDelegate,
|
||||
public NavigationThrottleRunner::Delegate,
|
||||
public CommitDeferringConditionRunner::Delegate,
|
||||
public FencedFrameURLMapping::MappingResultObserver,
|
||||
private RenderProcessHostObserver,
|
||||
private network::mojom::CookieAccessObserver {
|
||||
public:
|
||||
@ -612,6 +613,10 @@ class CONTENT_EXPORT NavigationRequest
|
||||
// navigation being committed (e.g. canceled navigations).
|
||||
virtual bool DidEncounterError() const;
|
||||
|
||||
void set_begin_navigation_callback_for_testing(base::OnceClosure callback) {
|
||||
begin_navigation_callback_for_testing_ = std::move(callback);
|
||||
}
|
||||
|
||||
void set_complete_callback_for_testing(
|
||||
ThrottleChecksFinishedCallback callback) {
|
||||
complete_callback_for_testing_ = std::move(callback);
|
||||
@ -874,6 +879,10 @@ class CONTENT_EXPORT NavigationRequest
|
||||
void AddDeferredConsoleMessage(blink::mojom::ConsoleMessageLevel level,
|
||||
std::string message);
|
||||
|
||||
bool is_deferred_on_fenced_frame_url_mapping_for_testing() const {
|
||||
return is_deferred_on_fenced_frame_url_mapping_;
|
||||
}
|
||||
|
||||
base::WeakPtr<NavigationRequest> GetWeakPtr();
|
||||
|
||||
bool is_potentially_prerendered_page_activation_for_testing() const {
|
||||
@ -966,7 +975,22 @@ class CONTENT_EXPORT NavigationRequest
|
||||
CommitDeferringCondition::NavigationType navigation_type,
|
||||
absl::optional<int> candidate_prerender_frame_tree_node_id);
|
||||
|
||||
// Called from BeginNavigation() or OnPrerenderingActivationChecksComplete().
|
||||
// Get the `FencedFrameURLMapping` associated with the current page.
|
||||
FencedFrameURLMapping& GetFencedFrameURLMap();
|
||||
|
||||
// True if this is a fenced frame navigation to an urn:uuid.
|
||||
bool NeedFencedFrameURLMapping();
|
||||
|
||||
// FencedFrameURLMapping::MappingResultObserver implementation.
|
||||
// Called from `FencedFrameURLMapping` when the mapping decision is made, and
|
||||
// resume the deferred navigation.
|
||||
void OnFencedFrameURLMappingComplete(
|
||||
absl::optional<GURL> mapped_url,
|
||||
absl::optional<FencedFrameURLMapping::PendingAdComponentsMap>
|
||||
pending_ad_components_map) override;
|
||||
|
||||
// Called from BeginNavigation(), OnPrerenderingActivationChecksComplete(),
|
||||
// or OnFencedFrameURLMappingComplete().
|
||||
void BeginNavigationImpl();
|
||||
|
||||
// Checks if the response requests an isolated origin via the
|
||||
@ -1450,6 +1474,13 @@ class CONTENT_EXPORT NavigationRequest
|
||||
// a network response yet, or when going to an "about:blank" page.
|
||||
absl::optional<WebExposedIsolationInfo> ComputeWebExposedIsolationInfo();
|
||||
|
||||
// Assign an invalid frame tree node id to `prerender_frame_tree_node_id_`.
|
||||
// Called as soon as when we are certain that this navigation won't activate a
|
||||
// prerendered page. This is needed because `IsPrerenderedPageActivation()`,
|
||||
// which may be called at any point after BeginNavigation(), will assume that
|
||||
// 'prerender_frame_tree_node_id_' has an value assigned.
|
||||
void MaybeAssignInvalidPrerenderFrameTreeNodeId();
|
||||
|
||||
// Never null. The pointee node owns this navigation request instance.
|
||||
FrameTreeNode* const frame_tree_node_;
|
||||
|
||||
@ -1682,6 +1713,9 @@ class CONTENT_EXPORT NavigationRequest
|
||||
// with this html as content and |net_error| as the network error.
|
||||
std::string post_commit_error_page_html_;
|
||||
|
||||
// This test-only callback will be run when BeginNavigation() is called.
|
||||
base::OnceClosure begin_navigation_callback_for_testing_;
|
||||
|
||||
// This test-only callback will be run when all throttle checks have been
|
||||
// performed. If the callback returns true, On*ChecksComplete functions are
|
||||
// skipped, and only the test callback is being performed.
|
||||
@ -1869,6 +1903,12 @@ class CONTENT_EXPORT NavigationRequest
|
||||
// running.
|
||||
bool is_potentially_prerendered_page_activation_for_testing_ = false;
|
||||
|
||||
// Set to true before the fenced frame url mapping. Reset to false when the
|
||||
// mapping finishes. If the initial mapping state of the urn:uuid is pending,
|
||||
// the mapping will finish asynchronously; otherwise, the mapping will finish
|
||||
// synchronously.
|
||||
bool is_deferred_on_fenced_frame_url_mapping_ = false;
|
||||
|
||||
// The root frame tree node id of the prerendered page. This will be a valid
|
||||
// FrameTreeNode id when this navigation will activate a prerendered page.
|
||||
// For all other navigations this will be
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/i18n/number_formatting.h"
|
||||
#include "base/test/scoped_feature_list.h"
|
||||
#include "build/build_config.h"
|
||||
#include "content/public/browser/navigation_throttle.h"
|
||||
#include "content/public/browser/ssl_status.h"
|
||||
@ -214,6 +215,23 @@ class NavigationRequestTest : public RenderViewHostImplTestHarness {
|
||||
GetNavigationRequest()->StartNavigation();
|
||||
}
|
||||
|
||||
FrameTreeNode* AddFrame(FrameTree& frame_tree,
|
||||
RenderFrameHostImpl* parent,
|
||||
int process_id,
|
||||
int new_routing_id,
|
||||
const blink::FramePolicy& frame_policy,
|
||||
blink::FrameOwnerElementType owner_type) {
|
||||
return frame_tree.AddFrame(
|
||||
parent, process_id, new_routing_id,
|
||||
TestRenderFrameHost::CreateStubFrameRemote(),
|
||||
TestRenderFrameHost::CreateStubBrowserInterfaceBrokerReceiver(),
|
||||
TestRenderFrameHost::CreateStubPolicyContainerBindParams(),
|
||||
blink::mojom::TreeScopeType::kDocument, std::string(), "uniqueName0",
|
||||
false, blink::LocalFrameToken(), base::UnguessableToken::Create(),
|
||||
frame_policy, blink::mojom::FrameOwnerProperties(), false, owner_type,
|
||||
/*is_dummy_frame_for_inner_tree=*/false);
|
||||
}
|
||||
|
||||
private:
|
||||
// The callback provided to NavigationRequest::WillStartRequest,
|
||||
// NavigationRequest::WillRedirectRequest, and
|
||||
@ -301,6 +319,65 @@ TEST_F(NavigationRequestTest, SimpleDataChecksFailure) {
|
||||
navigation->GetNavigationHandle()->GetNetErrorCode());
|
||||
}
|
||||
|
||||
TEST_F(NavigationRequestTest, FencedFrameNavigationToPendingMappedURN) {
|
||||
// Note that we only run this test for the ShadowDOM implementation of fenced
|
||||
// frames, due to how they add subframes in a way that is very specific to the
|
||||
// ShadowDOM implementation, and not suitable for the MPArch implementation.
|
||||
base::test::ScopedFeatureList scoped_feature_list;
|
||||
scoped_feature_list.InitAndEnableFeatureWithParameters(
|
||||
blink::features::kFencedFrames, {{"implementation_type", "shadow_dom"}});
|
||||
|
||||
FrameTree& frame_tree = contents()->GetPrimaryFrameTree();
|
||||
FrameTreeNode* root = frame_tree.root();
|
||||
int process_id = root->current_frame_host()->GetProcess()->GetID();
|
||||
|
||||
// Add a fenced frame.
|
||||
constexpr auto kFencedframeOwnerType =
|
||||
blink::FrameOwnerElementType::kFencedframe;
|
||||
blink::FramePolicy policy;
|
||||
policy.is_fenced = true;
|
||||
AddFrame(frame_tree, root->current_frame_host(), process_id, 15, policy,
|
||||
kFencedframeOwnerType);
|
||||
|
||||
FrameTreeNode* fenced_frame_tree_node = root->child_at(0);
|
||||
EXPECT_TRUE(fenced_frame_tree_node->IsFencedFrameRoot());
|
||||
EXPECT_TRUE(fenced_frame_tree_node->IsInFencedFrameTree());
|
||||
|
||||
FencedFrameURLMapping& fenced_frame_urls_map =
|
||||
main_test_rfh()->GetPage().fenced_frame_urls_map();
|
||||
|
||||
const GURL urn_uuid = fenced_frame_urls_map.GeneratePendingMappedURN();
|
||||
const GURL mapped_url = GURL("http://chromium.org");
|
||||
|
||||
auto navigation_simulator = NavigationSimulatorImpl::CreateRendererInitiated(
|
||||
urn_uuid, fenced_frame_tree_node->current_frame_host());
|
||||
|
||||
auto response_headers =
|
||||
base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/1.1 200 OK");
|
||||
response_headers->SetHeader("Supports-Loading-Mode", "fenced-frame");
|
||||
|
||||
navigation_simulator->SetAutoAdvance(false);
|
||||
navigation_simulator->SetResponseHeaders(response_headers);
|
||||
navigation_simulator->SetTransition(ui::PAGE_TRANSITION_AUTO_SUBFRAME);
|
||||
|
||||
navigation_simulator->Start();
|
||||
|
||||
EXPECT_EQ(navigation_simulator->GetNavigationHandle()->GetURL(), urn_uuid);
|
||||
|
||||
fenced_frame_urls_map.OnURNMappingResultDetermined(urn_uuid, mapped_url);
|
||||
|
||||
// Expect that the url in the NavigationRequest is already mapped.
|
||||
EXPECT_EQ(navigation_simulator->GetNavigationHandle()->GetURL(), mapped_url);
|
||||
|
||||
navigation_simulator->Wait();
|
||||
|
||||
navigation_simulator->SetAutoAdvance(true);
|
||||
navigation_simulator->ReadyToCommit();
|
||||
navigation_simulator->Commit();
|
||||
|
||||
EXPECT_EQ(fenced_frame_tree_node->current_url(), mapped_url);
|
||||
}
|
||||
|
||||
// Checks that a navigation deferred during WillStartRequest can be properly
|
||||
// cancelled.
|
||||
TEST_F(NavigationRequestTest, CancelDeferredWillStart) {
|
||||
|
@ -368,6 +368,8 @@ static_library("test_support") {
|
||||
"test_content_browser_client.h",
|
||||
"test_content_client.cc",
|
||||
"test_content_client.h",
|
||||
"test_fenced_frame_url_mapping_result_observer.cc",
|
||||
"test_fenced_frame_url_mapping_result_observer.h",
|
||||
"test_navigation_url_loader.cc",
|
||||
"test_navigation_url_loader.h",
|
||||
"test_navigation_url_loader_delegate.cc",
|
||||
|
@ -699,6 +699,11 @@ void NavigationSimulatorImpl::Commit() {
|
||||
browser_interface_broker_receiver_.reset();
|
||||
}
|
||||
|
||||
// The initial `navigation_url_` may have been mapped to a new URL at this
|
||||
// point. Overwrite it here with the desired value to correctly mock the
|
||||
// DidCommitProvisionalLoadParams.
|
||||
navigation_url_ = request_->GetURL();
|
||||
|
||||
auto params = BuildDidCommitProvisionalLoadParams(
|
||||
same_document_ /* same_document */, false /* failed_navigation */,
|
||||
render_frame_host_->last_http_status_code());
|
||||
@ -1331,10 +1336,15 @@ bool NavigationSimulatorImpl::SimulateRendererInitiatedStart() {
|
||||
if (!request)
|
||||
return false;
|
||||
|
||||
// Prerendered page activation can be deferred by CommitDeferringConditions in
|
||||
// BeginNavigation(), and `request_` hasn't been set by DidStartNavigation()
|
||||
// yet. In that case, we set it here.
|
||||
if (request->is_potentially_prerendered_page_activation_for_testing()) {
|
||||
// `request_` may not have been set by DidStartNavigation() yet, due to
|
||||
// either:
|
||||
// 1) Prerendered page activation can be deferred by CommitDeferringConditions
|
||||
// in BeginNavigation().
|
||||
// 2) Fenced frame navigation can be deferred on pending URL mapping.
|
||||
//
|
||||
// In these cases, we set the `request_` here.
|
||||
if (request->is_potentially_prerendered_page_activation_for_testing() ||
|
||||
request->is_deferred_on_fenced_frame_url_mapping_for_testing()) {
|
||||
DCHECK(!request_);
|
||||
request_ = request;
|
||||
}
|
||||
|
@ -0,0 +1,24 @@
|
||||
// Copyright 2022 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/test/test_fenced_frame_url_mapping_result_observer.h"
|
||||
|
||||
namespace content {
|
||||
|
||||
TestFencedFrameURLMappingResultObserver::
|
||||
TestFencedFrameURLMappingResultObserver() = default;
|
||||
|
||||
TestFencedFrameURLMappingResultObserver::
|
||||
~TestFencedFrameURLMappingResultObserver() = default;
|
||||
|
||||
void TestFencedFrameURLMappingResultObserver::OnFencedFrameURLMappingComplete(
|
||||
absl::optional<GURL> mapped_url,
|
||||
absl::optional<FencedFrameURLMapping::PendingAdComponentsMap>
|
||||
pending_ad_components_map) {
|
||||
mapping_complete_observed_ = true;
|
||||
mapped_url_ = std::move(mapped_url);
|
||||
pending_ad_components_map_ = std::move(pending_ad_components_map);
|
||||
}
|
||||
|
||||
} // namespace content
|
42
content/test/test_fenced_frame_url_mapping_result_observer.h
Normal file
42
content/test/test_fenced_frame_url_mapping_result_observer.h
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright 2022 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_TEST_TEST_FENCED_FRAME_URL_MAPPING_RESULT_OBSERVER_H_
|
||||
#define CONTENT_TEST_TEST_FENCED_FRAME_URL_MAPPING_RESULT_OBSERVER_H_
|
||||
|
||||
#include "content/browser/fenced_frame/fenced_frame_url_mapping.h"
|
||||
|
||||
namespace content {
|
||||
|
||||
// Tests can use this class to observe and check the URL mapping result.
|
||||
class TestFencedFrameURLMappingResultObserver
|
||||
: public FencedFrameURLMapping::MappingResultObserver {
|
||||
public:
|
||||
TestFencedFrameURLMappingResultObserver();
|
||||
~TestFencedFrameURLMappingResultObserver() override;
|
||||
|
||||
void OnFencedFrameURLMappingComplete(
|
||||
absl::optional<GURL> mapped_url,
|
||||
absl::optional<FencedFrameURLMapping::PendingAdComponentsMap>
|
||||
pending_ad_components_map) override;
|
||||
|
||||
bool mapping_complete_observed() const { return mapping_complete_observed_; }
|
||||
|
||||
const absl::optional<GURL>& mapped_url() const { return mapped_url_; }
|
||||
|
||||
const absl::optional<FencedFrameURLMapping::PendingAdComponentsMap>&
|
||||
pending_ad_components_map() const {
|
||||
return pending_ad_components_map_;
|
||||
}
|
||||
|
||||
private:
|
||||
bool mapping_complete_observed_ = false;
|
||||
absl::optional<GURL> mapped_url_;
|
||||
absl::optional<FencedFrameURLMapping::PendingAdComponentsMap>
|
||||
pending_ad_components_map_;
|
||||
};
|
||||
|
||||
} // namespace content
|
||||
|
||||
#endif // CONTENT_TEST_TEST_FENCED_FRAME_URL_MAPPING_RESULT_OBSERVER_H_
|
@ -4,7 +4,7 @@
|
||||
// See tools/state_transitions/README.md
|
||||
digraph createflow {
|
||||
NOT_STARTED -> {WAITING_FOR_RENDERER_RESPONSE, WILL_START_NAVIGATION, WILL_START_REQUEST};
|
||||
WAITING_FOR_RENDERER_RESPONSE -> {WILL_START_NAVIGATION};
|
||||
WAITING_FOR_RENDERER_RESPONSE -> {WILL_START_NAVIGATION, WILL_START_REQUEST};
|
||||
WILL_START_NAVIGATION -> {WILL_START_REQUEST, WILL_FAIL_REQUEST};
|
||||
WILL_START_REQUEST -> {WILL_REDIRECT_REQUEST, WILL_PROCESS_RESPONSE, READY_TO_COMMIT, DID_COMMIT, CANCELING, WILL_FAIL_REQUEST, DID_COMMIT_ERROR_PAGE};
|
||||
WILL_REDIRECT_REQUEST -> {WILL_REDIRECT_REQUEST, WILL_PROCESS_RESPONSE, CANCELING, WILL_FAIL_REQUEST};
|
||||
|
Binary file not shown.
Before ![]() (image error) Size: 156 KiB After ![]() (image error) Size: 143 KiB ![]() ![]() |
Reference in New Issue
Block a user