Commit ae8ee33c authored by Alexander Timin's avatar Alexander Timin Committed by Commit Bot

[bfcache] Track sync-scriptable pages

Add tracking for pages which can be scripted by other pages (via
window.open) to the back-forward cache metrics.

BUG=933147,957632
R=creis@chromium.org
CC=​arthursonzogni@chromium.org

Change-Id: I33712203b3c996f09a70ebb4fe221d712c7a919d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1569006
Commit-Queue: Alexander Timin <altimin@chromium.org>
Reviewed-by: default avatarCharlie Reis <creis@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#657745}
parent 5a142bea
......@@ -25,6 +25,10 @@ namespace {
constexpr int kPageShowFeature = static_cast<int>(
blink::scheduler::WebSchedulerTrackedFeature::kPageShowEventListener);
constexpr int kHasScriptableFramesInMultipleTabsFeature =
static_cast<int>(blink::scheduler::WebSchedulerTrackedFeature::
kHasScriptableFramesInMultipleTabs);
ukm::SourceId ToSourceId(int64_t navigation_id) {
return ukm::ConvertToSourceId(navigation_id,
ukm::SourceIdType::NAVIGATION_ID);
......@@ -482,4 +486,116 @@ IN_PROC_BROWSER_TEST_F(BackForwardCacheMetricsBrowserTest, DedicatedWorker) {
kDedicatedWorkerOrWorklet));
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheMetricsBrowserTest,
WindowOpen_SameOrigin) {
ukm::TestAutoSetUkmRecorder recorder;
const GURL url1(embedded_test_server()->GetURL("/title1.html"));
const GURL url2(embedded_test_server()->GetURL("/title2.html"));
EXPECT_TRUE(NavigateToURL(shell(), url1));
ShellAddedObserver observer;
EXPECT_TRUE(ExecJs(shell(), JsReplace("window.open($1, '_blank');", url2)));
Shell* shell2 = observer.GetShell();
EXPECT_TRUE(WaitForLoadStop(shell2->web_contents()));
EXPECT_TRUE(NavigateToURL(shell(), url2));
{
// We emit metrics when we navigate back to the previously visited page,
// so navigate back to trigger the metrics.
TestNavigationObserver navigation_observer(shell()->web_contents());
shell()->GoBackOrForward(-1);
navigation_observer.WaitForNavigationFinished();
}
ASSERT_EQ(navigation_ids_.size(), static_cast<size_t>(3));
// ukm::SourceId id1 = ToSourceId(navigation_ids_[0]);
// ukm::SourceId id2 = ToSourceId(navigation_ids_[1]);
ukm::SourceId id3 = ToSourceId(navigation_ids_[2]);
EXPECT_THAT(GetFeatureUsageMetrics(&recorder),
testing::ElementsAre(FeatureUsage{
id3, 1 << kHasScriptableFramesInMultipleTabsFeature, 0, 0}));
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheMetricsBrowserTest,
WindowOpen_CrossOrigin) {
ukm::TestAutoSetUkmRecorder recorder;
const GURL url1(embedded_test_server()->GetURL("/title1.html"));
const GURL url2(embedded_test_server()->GetURL("/title2.html"));
const GURL url3(
embedded_test_server()->GetURL("/cross-site/bar.com/title3.html"));
EXPECT_TRUE(NavigateToURL(shell(), url1));
ShellAddedObserver observer;
EXPECT_TRUE(ExecJs(shell(), JsReplace("window.open($1, '_blank');", url3)));
Shell* shell2 = observer.GetShell();
EXPECT_TRUE(WaitForLoadStop(shell2->web_contents()));
EXPECT_TRUE(NavigateToURL(shell(), url2));
{
// We emit metrics when we navigate back to the previously visited page,
// so navigate back to trigger the metrics.
TestNavigationObserver navigation_observer(shell()->web_contents());
shell()->GoBackOrForward(-1);
navigation_observer.WaitForNavigationFinished();
}
ASSERT_EQ(navigation_ids_.size(), static_cast<size_t>(3));
// ukm::SourceId id1 = ToSourceId(navigation_ids_[0]);
// ukm::SourceId id2 = ToSourceId(navigation_ids_[1]);
ukm::SourceId id3 = ToSourceId(navigation_ids_[2]);
// TODO(altimin): For now we don't distinguish between same-origin and
// cross-origin window.opens. We might want to revisit this in the future.
EXPECT_THAT(GetFeatureUsageMetrics(&recorder),
testing::ElementsAre(FeatureUsage{
id3, 1 << kHasScriptableFramesInMultipleTabsFeature, 0, 0}));
}
IN_PROC_BROWSER_TEST_F(BackForwardCacheMetricsBrowserTest,
WindowOpen_SameOrigin_Openee) {
ukm::TestAutoSetUkmRecorder recorder;
const GURL url1(embedded_test_server()->GetURL("/title1.html"));
const GURL url2(embedded_test_server()->GetURL("/title2.html"));
const GURL url3(embedded_test_server()->GetURL("/title3.html"));
EXPECT_TRUE(NavigateToURL(shell(), url1));
ShellAddedObserver observer;
EXPECT_TRUE(ExecJs(shell(), JsReplace("window.open($1, '_blank');", url2)));
Shell* shell2 = observer.GetShell();
EXPECT_TRUE(WaitForLoadStop(shell2->web_contents()));
Observe(shell2->web_contents());
// Navigate the second shell and ensure that the openee doesn't get cached
// too.
EXPECT_TRUE(NavigateToURL(shell2, url3));
{
// We emit metrics when we navigate back to the previously visited page,
// so navigate back to trigger the metrics.
TestNavigationObserver navigation_observer(shell2->web_contents());
shell2->GoBackOrForward(-1);
navigation_observer.WaitForNavigationFinished();
}
ASSERT_EQ(navigation_ids_.size(), static_cast<size_t>(3));
// ukm::SourceId id1 = ToSourceId(navigation_ids_[0]);
// ukm::SourceId id2 = ToSourceId(navigation_ids_[1]);
ukm::SourceId id3 = ToSourceId(navigation_ids_[2]);
EXPECT_THAT(GetFeatureUsageMetrics(&recorder),
testing::ElementsAre(FeatureUsage{
id3, 1 << kHasScriptableFramesInMultipleTabsFeature, 0, 0}));
}
} // namespace content
......@@ -37,7 +37,14 @@ enum class WebSchedulerTrackedFeature {
kOutstandingIndexedDBTransaction = 17,
kMaxValue = kOutstandingIndexedDBTransaction
// Whether there are other pages which can potentially synchronously script
// the current one (e.g. due to window.open being used).
// This is a conservative estimation which doesn't take into account the
// origin, so it may be true if the related page is cross-origin.
// Recorded only for the main frame.
kHasScriptableFramesInMultipleTabs = 18,
kMaxValue = kHasScriptableFramesInMultipleTabs
};
} // namespace scheduler
......
......@@ -148,6 +148,11 @@ Page* Page::CreateOrdinary(PageClients& page_clients, Page* opener) {
page->prev_related_page_ = opener;
page->next_related_page_ = next;
next->prev_related_page_ = page;
// No need to update |prev| here as if |next| != |prev|, |prev| was already
// marked as having related pages.
next->UpdateHasRelatedPages();
page->UpdateHasRelatedPages();
}
OrdinaryPages().insert(page);
......@@ -287,6 +292,9 @@ void Page::SetMainFrame(Frame* main_frame) {
main_frame_ = main_frame;
page_scheduler_->SetIsMainFrameLocal(main_frame->IsLocalFrame());
// |has_related_pages_| is only reported when the main frame is local, so make
// sure it's updated after the main frame changes.
UpdateHasRelatedPages();
}
LocalFrame* Page::DeprecatedLocalMainFrame() const {
......@@ -753,6 +761,8 @@ void Page::DidCommitLoad(LocalFrame* frame) {
kScrollBehaviorInstant,
ScrollableArea::ScrollCallback());
hosts_using_features_.UpdateMeasurementsAndClear();
// Update |has_related_pages_| as features are reset after navigation.
UpdateHasRelatedPages();
}
GetLinkHighlights().ResetForPageNavigation();
}
......@@ -836,6 +846,10 @@ void Page::WillBeDestroyed() {
prev->next_related_page_ = next;
this->prev_related_page_ = nullptr;
this->next_related_page_ = nullptr;
if (prev != this)
prev->UpdateHasRelatedPages();
if (next != this)
next->UpdateHasRelatedPages();
}
if (scrolling_coordinator_)
......@@ -933,6 +947,22 @@ bool Page::InsidePortal() const {
return inside_portal_;
}
void Page::UpdateHasRelatedPages() {
bool has_related_pages = next_related_page_ != this;
if (!has_related_pages) {
has_related_pages_.reset();
} else {
LocalFrame* local_main_frame = DynamicTo<LocalFrame>(main_frame_.Get());
// We want to record this only for the pages which have local main frame,
// which is fine as we are aggregating results across all processes.
if (!local_main_frame)
return;
has_related_pages_ = local_main_frame->GetFrameScheduler()->RegisterFeature(
SchedulingPolicy::Feature::kHasScriptableFramesInMultipleTabs,
{SchedulingPolicy::RecordMetricsForBackForwardCache()});
}
}
Page::PageClients::PageClients() : chrome_client(nullptr) {}
Page::PageClients::~PageClients() = default;
......
......@@ -331,6 +331,8 @@ class CORE_EXPORT Page final : public GarbageCollectedFinalized<Page>,
void SetPageScheduler(std::unique_ptr<PageScheduler>);
void UpdateHasRelatedPages();
// Typically, the main frame and Page should both be owned by the embedder,
// which must call Page::willBeDestroyed() prior to destroying Page. This
// call detaches the main frame and clears this pointer, thus ensuring that
......@@ -406,6 +408,10 @@ class CORE_EXPORT Page final : public GarbageCollectedFinalized<Page>,
Member<Page> next_related_page_;
Member<Page> prev_related_page_;
// A handle to notify the scheduler whether this page has other related
// pages or not.
FrameScheduler::SchedulingAffectingFeatureHandle has_related_pages_;
std::unique_ptr<PageScheduler> page_scheduler_;
int32_t autoplay_flags_;
......
......@@ -15,6 +15,7 @@ bool SchedulingPolicy::IsFeatureSticky(SchedulingPolicy::Feature feature) {
case Feature::kDedicatedWorkerOrWorklet:
case Feature::kOutstandingNetworkRequest:
case Feature::kOutstandingIndexedDBTransaction:
case Feature::kHasScriptableFramesInMultipleTabs:
return false;
case Feature::kMainResourceHasCacheControlNoStore:
case Feature::kMainResourceHasCacheControlNoCache:
......
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