#include "content/public/browser/document_user_data.h"
#include "base/command_line.h"
#include "base/memory/weak_ptr.h"
#include "base/test/bind.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.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/web_contents/web_contents_impl.h"
#include "content/common/content_navigation_policy.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/site_isolation_policy.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_features.h"
#include "content/public/test/back_forward_cache_util.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/navigation_handle_observer.h"
#include "content/public/test/render_frame_host_test_support.h"
#include "content/public/test/test_navigation_observer.h"
#include "content/public/test/test_navigation_throttle.h"
#include "content/public/test/test_navigation_throttle_inserter.h"
#include "content/public/test/test_utils.h"
#include "content/public/test/url_loader_interceptor.h"
#include "content/shell/browser/shell.h"
#include "content/test/content_browser_test_utils_internal.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/url_request/url_request_failed_job.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace content {
namespace {
int next_id = 0;
class Data : public DocumentUserData<Data> {
public:
~Data() override = default;
base::WeakPtr<Data> GetWeakPtr() { return weak_ptr_factory_.GetWeakPtr(); }
int unique_id() { return unique_id_; }
private:
explicit Data(RenderFrameHost* render_frame_host)
: DocumentUserData<Data>(render_frame_host) {
unique_id_ = ++next_id;
}
friend class content::DocumentUserData<Data>;
int unique_id_;
base::WeakPtrFactory<Data> weak_ptr_factory_{this};
DOCUMENT_USER_DATA_KEY_DECL();
};
DOCUMENT_USER_DATA_KEY_IMPL(Data);
class PopupCreatedObserver : public WebContentsDelegate {
public:
using WebContentsCreatedCallback =
base::RepeatingCallback<void(WebContents* web_contents)>;
explicit PopupCreatedObserver(WebContentsCreatedCallback callback)
: callback_(std::move(callback)) {}
void AddNewContents(WebContents* source_contents,
std::unique_ptr<WebContents> new_contents,
const GURL& target_url,
WindowOpenDisposition disposition,
const blink::mojom::WindowFeatures& window_features,
bool user_gesture,
bool* was_blocked) override {
callback_.Run(new_contents.get());
web_contents_.push_back(std::move(new_contents));
}
private:
WebContentsCreatedCallback callback_;
std::vector<std::unique_ptr<WebContents>> web_contents_;
};
}
class DocumentUserDataTest : public ContentBrowserTest {
public:
~DocumentUserDataTest() override = default;
protected:
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
ContentBrowserTest::SetUpOnMainThread();
}
WebContentsImpl* web_contents() const {
return static_cast<WebContentsImpl*>(shell()->web_contents());
}
RenderFrameHostImpl* top_frame_host() {
return web_contents()->GetPrimaryFrameTree().root()->current_frame_host();
}
};
IN_PROC_BROWSER_TEST_F(DocumentUserDataTest,
GetCreateAndDeleteForCurrentDocument) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a = top_frame_host();
Data* data = Data::GetForCurrentDocument(rfh_a);
EXPECT_FALSE(data);
Data::CreateForCurrentDocument(rfh_a);
base::WeakPtr<Data> created_data =
Data::GetForCurrentDocument(rfh_a)->GetWeakPtr();
EXPECT_TRUE(created_data);
Data::DeleteForCurrentDocument(rfh_a);
EXPECT_FALSE(created_data);
EXPECT_FALSE(Data::GetForCurrentDocument(rfh_a));
}
IN_PROC_BROWSER_TEST_F(DocumentUserDataTest, GetOrCreateForCurrentDocument) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a = top_frame_host();
Data* data = Data::GetForCurrentDocument(rfh_a);
EXPECT_FALSE(data);
base::WeakPtr<Data> created_data =
Data::GetOrCreateForCurrentDocument(rfh_a)->GetWeakPtr();
EXPECT_TRUE(created_data);
Data::GetOrCreateForCurrentDocument(rfh_a);
EXPECT_TRUE(created_data);
}
IN_PROC_BROWSER_TEST_F(DocumentUserDataTest, CheckForMultipleRFHsInFrameTree) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url_a(embedded_test_server()->GetURL(
"a.com", "/cross_site_iframe_factory.html?a(b)"));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a = top_frame_host();
RenderFrameHostImpl* rfh_b = rfh_a->child_at(0)->current_frame_host();
Data::CreateForCurrentDocument(rfh_a);
Data* data_a = Data::GetForCurrentDocument(rfh_a);
Data::CreateForCurrentDocument(rfh_b);
Data* data_b = Data::GetForCurrentDocument(rfh_b);
EXPECT_TRUE(data_a);
EXPECT_TRUE(data_b);
EXPECT_NE(data_a->unique_id(), data_b->unique_id());
}
IN_PROC_BROWSER_TEST_F(DocumentUserDataTest,
CrashedFrameUserDataIsPreservedAndDeletedOnReset) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a = top_frame_host();
Data::CreateForCurrentDocument(rfh_a);
base::WeakPtr<Data> data = Data::GetForCurrentDocument(rfh_a)->GetWeakPtr();
EXPECT_TRUE(data);
RenderProcessHost* renderer_process = rfh_a->GetProcess();
RenderProcessHostWatcher crash_observer(
renderer_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
renderer_process->Shutdown(0);
crash_observer.Wait();
EXPECT_FALSE(rfh_a->IsRenderFrameLive());
EXPECT_TRUE(data);
bool did_clear_user_data = false;
RenderFrameHostCreatedObserver observer(
web_contents(), base::BindRepeating(
[](bool* did_clear_user_data, RenderFrameHost* rfh) {
if (!Data::GetForCurrentDocument(rfh))
*did_clear_user_data = true;
},
&did_clear_user_data));
FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
root->render_manager()->InitializeMainRenderFrameForImmediateUse();
EXPECT_TRUE(did_clear_user_data);
RenderFrameHostImpl* new_rfh_a = top_frame_host();
EXPECT_TRUE(new_rfh_a->IsRenderFrameLive());
EXPECT_FALSE(data);
Data::CreateForCurrentDocument(new_rfh_a);
base::WeakPtr<Data> new_data =
Data::GetForCurrentDocument(new_rfh_a)->GetWeakPtr();
EXPECT_TRUE(new_data);
}
IN_PROC_BROWSER_TEST_F(DocumentUserDataTest,
CheckWithFrameCrashDuringNavigation) {
DisableBackForwardCacheForTesting(shell()->web_contents(),
BackForwardCache::TEST_REQUIRES_NO_CACHING);
ASSERT_TRUE(embedded_test_server()->Start());
GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
GURL url_b(embedded_test_server()->GetURL("b.com", "/title2.html"));
IsolateOriginsForTesting(embedded_test_server(), shell()->web_contents(),
{"b.com"});
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a = top_frame_host();
TestNavigationManager manager(shell()->web_contents(), url_b);
DisableProactiveBrowsingInstanceSwapFor(rfh_a);
shell()->LoadURLForFrame(url_b, std::string(),
ui::PageTransitionFromInt(ui::PAGE_TRANSITION_LINK));
EXPECT_TRUE(manager.WaitForRequestStart());
FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
RenderFrameHostImpl* pending_rfh =
root->render_manager()->speculative_frame_host();
NavigationRequest* navigation_request = root->navigation_request();
EXPECT_EQ(navigation_request->GetAssociatedRFHType(),
NavigationRequest::AssociatedRenderFrameHostType::SPECULATIVE);
EXPECT_TRUE(pending_rfh);
Data::CreateForCurrentDocument(pending_rfh);
base::WeakPtr<Data> data =
Data::GetForCurrentDocument(pending_rfh)->GetWeakPtr();
EXPECT_TRUE(data);
RenderProcessHost* renderer_process = rfh_a->GetProcess();
RenderProcessHostWatcher crash_observer(
renderer_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
renderer_process->Shutdown(0);
crash_observer.Wait();
EXPECT_EQ(top_frame_host(), rfh_a);
EXPECT_FALSE(pending_rfh->IsActive());
EXPECT_FALSE(rfh_a->IsRenderFrameLive());
EXPECT_TRUE(pending_rfh->IsRenderFrameLive());
EXPECT_TRUE(data);
ASSERT_TRUE(manager.WaitForNavigationFinished());
EXPECT_EQ(url_b,
web_contents()->GetPrimaryMainFrame()->GetLastCommittedURL());
EXPECT_TRUE(data);
}
IN_PROC_BROWSER_TEST_F(DocumentUserDataTest,
CheckWithFrameCrashBeforeNavigation) {
if (ShouldSkipEarlyCommitPendingForCrashedFrame())
return;
ASSERT_TRUE(embedded_test_server()->Start());
GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
GURL url_b(embedded_test_server()->GetURL("b.com", "/title2.html"));
IsolateOriginsForTesting(embedded_test_server(), shell()->web_contents(),
{"b.com"});
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a = top_frame_host();
RenderProcessHost* renderer_process = rfh_a->GetProcess();
RenderProcessHostWatcher crash_observer(
renderer_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
renderer_process->Shutdown(0);
crash_observer.Wait();
TestNavigationManager manager(shell()->web_contents(), url_b);
shell()->LoadURL(url_b);
EXPECT_TRUE(manager.WaitForRequestStart());
FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
RenderFrameHostImpl* current_rfh =
root->render_manager()->current_frame_host();
NavigationRequest* navigation_request = root->navigation_request();
EXPECT_EQ(navigation_request->GetAssociatedRFHType(),
NavigationRequest::AssociatedRenderFrameHostType::CURRENT);
EXPECT_TRUE(current_rfh);
EXPECT_TRUE(current_rfh->IsActive());
Data::CreateForCurrentDocument(current_rfh);
base::WeakPtr<Data> data =
Data::GetForCurrentDocument(current_rfh)->GetWeakPtr();
EXPECT_TRUE(data);
ASSERT_TRUE(manager.WaitForNavigationFinished());
EXPECT_EQ(url_b,
web_contents()->GetPrimaryMainFrame()->GetLastCommittedURL());
EXPECT_TRUE(data);
}
IN_PROC_BROWSER_TEST_F(DocumentUserDataTest,
CheckWithRenderFrameCreationAfterRendererDebugURLCrash) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL renderer_debug_url("javascript:'hello'");
GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
shell()->LoadURL(renderer_debug_url);
ASSERT_EQ("hello", EvalJs(shell(), "document.body.innerText"));
FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
RenderFrameHostImpl* rfh = root->current_frame_host();
Data::CreateForCurrentDocument(rfh);
base::WeakPtr<Data> data = Data::GetForCurrentDocument(rfh)->GetWeakPtr();
EXPECT_TRUE(data);
RenderProcessHost* renderer_process = rfh->GetProcess();
RenderProcessHostWatcher crash_observer(
renderer_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
renderer_process->Shutdown(0);
crash_observer.Wait();
EXPECT_FALSE(rfh->IsRenderFrameLive());
EXPECT_TRUE(data);
shell()->LoadURL(renderer_debug_url);
ASSERT_EQ("hello", EvalJs(shell(), "document.body.innerText"));
EXPECT_FALSE(data);
}
IN_PROC_BROWSER_TEST_F(DocumentUserDataTest,
CheckIDsForSpeculativeRFHBeforeAndAfterCommit) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
GURL url_b(embedded_test_server()->GetURL("b.com", "/title2.html"));
IsolateOriginsForTesting(embedded_test_server(), shell()->web_contents(),
{"b.com"});
EXPECT_TRUE(NavigateToURL(shell(), url_a));
TestNavigationManager manager(shell()->web_contents(), url_b);
shell()->LoadURL(url_b);
EXPECT_TRUE(manager.WaitForRequestStart());
FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
RenderFrameHostImpl* pending_rfh =
root->render_manager()->speculative_frame_host();
NavigationRequest* navigation_request = root->navigation_request();
EXPECT_EQ(navigation_request->GetAssociatedRFHType(),
NavigationRequest::AssociatedRenderFrameHostType::SPECULATIVE);
EXPECT_TRUE(pending_rfh);
Data::CreateForCurrentDocument(pending_rfh);
Data* data_before_commit = Data::GetForCurrentDocument(pending_rfh);
EXPECT_TRUE(data_before_commit);
ASSERT_TRUE(manager.WaitForNavigationFinished());
EXPECT_EQ(url_b,
web_contents()->GetPrimaryMainFrame()->GetLastCommittedURL());
RenderFrameHostImpl* rfh_b = top_frame_host();
EXPECT_EQ(pending_rfh, rfh_b);
Data* data_after_commit = Data::GetForCurrentDocument(rfh_b);
EXPECT_TRUE(data_after_commit);
EXPECT_EQ(data_before_commit->unique_id(), data_after_commit->unique_id());
}
IN_PROC_BROWSER_TEST_F(DocumentUserDataTest, SpeculativeRFHDeleted) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url_a(embedded_test_server()->GetURL(
"a.com", "/cross_site_iframe_factory.html?a(b)"));
GURL url_c(embedded_test_server()->GetURL("c.com", "/hung"));
IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a = web_contents()->GetPrimaryMainFrame();
RenderFrameHostImpl* rfh_b = rfh_a->child_at(0)->current_frame_host();
LeaveInPendingDeletionState(rfh_b);
TestNavigationManager navigation_observer(web_contents(), url_c);
EXPECT_TRUE(ExecJs(rfh_b, JsReplace("location.href=$1;", url_c)));
EXPECT_TRUE(navigation_observer.WaitForRequestStart());
RenderFrameHostImpl* pending_rfh_c =
rfh_b->frame_tree_node()->render_manager()->speculative_frame_host();
Data::CreateForCurrentDocument(pending_rfh_c);
base::WeakPtr<Data> data =
Data::GetForCurrentDocument(pending_rfh_c)->GetWeakPtr();
EXPECT_TRUE(data);
RenderFrameDeletedObserver delete_speculative_c(pending_rfh_c);
EXPECT_TRUE(
ExecJs(rfh_a, JsReplace("document.querySelector('iframe').remove();")));
delete_speculative_c.WaitUntilDeleted();
EXPECT_TRUE(delete_speculative_c.deleted());
EXPECT_FALSE(data);
}
IN_PROC_BROWSER_TEST_F(DocumentUserDataTest, RenderFrameHostDeleted) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url_a(embedded_test_server()->GetURL(
"a.com", "/cross_site_iframe_factory.html?a(b)"));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
RenderFrameHostImpl* rfh_a = top_frame_host();
RenderFrameHostImpl* rfh_b = rfh_a->child_at(0)->current_frame_host();
RenderFrameDeletedObserver delete_observer_rfh_b(rfh_b);
Data::CreateForCurrentDocument(rfh_b);
base::WeakPtr<Data> data = Data::GetForCurrentDocument(rfh_b)->GetWeakPtr();
EXPECT_TRUE(data);
EXPECT_TRUE(ExecJs(root, "document.querySelector('iframe').remove()"));
delete_observer_rfh_b.WaitUntilDeleted();
EXPECT_FALSE(data);
}
IN_PROC_BROWSER_TEST_F(DocumentUserDataTest, CheckInPendingDeletionState) {
ASSERT_TRUE(embedded_test_server()->Start());
IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
GURL url_ab(embedded_test_server()->GetURL(
"a.com", "/cross_site_iframe_factory.html?a(b)"));
GURL url_c(embedded_test_server()->GetURL("c.com", "/title1.html"));
EXPECT_TRUE(NavigateToURL(shell(), url_ab));
RenderFrameHostImpl* rfh_a = top_frame_host();
RenderFrameHostImpl* rfh_b = rfh_a->child_at(0)->current_frame_host();
DisableBackForwardCacheForTesting(shell()->web_contents(),
BackForwardCache::TEST_REQUIRES_NO_CACHING);
LeaveInPendingDeletionState(rfh_a);
LeaveInPendingDeletionState(rfh_b);
Data::CreateForCurrentDocument(rfh_a);
Data::CreateForCurrentDocument(rfh_b);
base::WeakPtr<Data> data_a = Data::GetForCurrentDocument(rfh_a)->GetWeakPtr();
base::WeakPtr<Data> data_b = Data::GetForCurrentDocument(rfh_b)->GetWeakPtr();
EXPECT_TRUE(data_a);
EXPECT_TRUE(data_b);
EXPECT_TRUE(NavigateToURL(shell(), url_c));
EXPECT_EQ(rfh_a->lifecycle_state(),
RenderFrameHostImpl::LifecycleStateImpl::kRunningUnloadHandlers);
EXPECT_EQ(rfh_b->lifecycle_state(),
RenderFrameHostImpl::LifecycleStateImpl::kRunningUnloadHandlers);
EXPECT_TRUE(data_a);
EXPECT_TRUE(data_b);
EXPECT_FALSE(rfh_a->IsActive());
EXPECT_FALSE(rfh_b->IsActive());
}
IN_PROC_BROWSER_TEST_F(DocumentUserDataTest, CommitSameDocumentNavigation) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
GURL url_a2(embedded_test_server()->GetURL("a.com", "/title1.html#2"));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a = top_frame_host();
Data::CreateForCurrentDocument(rfh_a);
Data* data = Data::GetForCurrentDocument(rfh_a);
EXPECT_TRUE(data);
EXPECT_TRUE(ExecJs(shell(), JsReplace("location = $1", url_a2.spec())));
EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
EXPECT_EQ(url_a2,
web_contents()->GetPrimaryMainFrame()->GetLastCommittedURL());
Data* data2 = Data::GetForCurrentDocument(rfh_a);
EXPECT_TRUE(data2);
EXPECT_EQ(data->unique_id(), data2->unique_id());
}
IN_PROC_BROWSER_TEST_F(DocumentUserDataTest, CancelledNavigation) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a = top_frame_host();
Data::CreateForCurrentDocument(rfh_a);
Data* data = Data::GetForCurrentDocument(rfh_a);
EXPECT_TRUE(data);
TestNavigationThrottleInserter throttle_inserter(
shell()->web_contents(),
base::BindLambdaForTesting(
[&](NavigationHandle* handle) -> std::unique_ptr<NavigationThrottle> {
auto throttle = std::make_unique<TestNavigationThrottle>(handle);
throttle->SetResponse(TestNavigationThrottle::WILL_START_REQUEST,
TestNavigationThrottle::SYNCHRONOUS,
NavigationThrottle::CANCEL_AND_IGNORE);
return throttle;
}));
EXPECT_FALSE(NavigateToURL(shell(), url_b));
EXPECT_EQ(rfh_a, top_frame_host());
Data* data2 = Data::GetForCurrentDocument(rfh_a);
EXPECT_TRUE(data2);
EXPECT_EQ(data->unique_id(), data2->unique_id());
}
IN_PROC_BROWSER_TEST_F(DocumentUserDataTest, FailedNavigation) {
if (!SiteIsolationPolicy::IsErrorPageIsolationEnabled(true))
return;
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL("a.com", "/title1.html"));
GURL error_url(embedded_test_server()->GetURL("/close-socket"));
std::unique_ptr<URLLoaderInterceptor> url_interceptor =
URLLoaderInterceptor::SetupRequestFailForURL(error_url,
net::ERR_DNS_TIMED_OUT);
EXPECT_TRUE(NavigateToURL(shell(), url));
RenderFrameHostImpl* rfh_a = top_frame_host();
RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
Data::CreateForCurrentDocument(rfh_a);
base::WeakPtr<Data> data = Data::GetForCurrentDocument(rfh_a)->GetWeakPtr();
EXPECT_TRUE(data);
DisableBackForwardCacheForTesting(shell()->web_contents(),
BackForwardCache::TEST_REQUIRES_NO_CACHING);
NavigationHandleObserver observer(shell()->web_contents(), error_url);
EXPECT_FALSE(NavigateToURL(shell(), error_url));
EXPECT_TRUE(observer.is_error());
EXPECT_EQ(net::ERR_DNS_TIMED_OUT, observer.net_error_code());
delete_observer_rfh_a.WaitUntilDeleted();
EXPECT_FALSE(data);
}
IN_PROC_BROWSER_TEST_F(DocumentUserDataTest, CrossSiteNavigation) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a = top_frame_host();
RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
Data::CreateForCurrentDocument(rfh_a);
base::WeakPtr<Data> data = Data::GetForCurrentDocument(rfh_a)->GetWeakPtr();
EXPECT_TRUE(data);
DisableBackForwardCacheForTesting(shell()->web_contents(),
BackForwardCache::TEST_REQUIRES_NO_CACHING);
EXPECT_TRUE(NavigateToURL(shell(), url_b));
EXPECT_NE(rfh_a, top_frame_host());
delete_observer_rfh_a.WaitUntilDeleted();
EXPECT_FALSE(data);
}
IN_PROC_BROWSER_TEST_F(DocumentUserDataTest, SameSiteNavigation) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url_a1(embedded_test_server()->GetURL("a.com", "/title1.html"));
GURL url_a2(embedded_test_server()->GetURL("a.com", "/title2.html"));
DisableBackForwardCacheForTesting(shell()->web_contents(),
BackForwardCache::TEST_REQUIRES_NO_CACHING);
EXPECT_TRUE(NavigateToURL(shell(), url_a1));
RenderFrameHostImpl* rfh_a1 = top_frame_host();
Data::CreateForCurrentDocument(rfh_a1);
base::WeakPtr<Data> data = Data::GetForCurrentDocument(rfh_a1)->GetWeakPtr();
EXPECT_TRUE(data);
EXPECT_TRUE(NavigateToURL(shell(), url_a2));
EXPECT_FALSE(data);
}
IN_PROC_BROWSER_TEST_F(DocumentUserDataTest, WindowOpen) {
ASSERT_TRUE(embedded_test_server()->Start());
int popup_data_id = -1;
WebContents* new_tab = nullptr;
GURL url(embedded_test_server()->GetURL("a.com", "/title1.html"));
EXPECT_TRUE(NavigateToURL(shell(), url));
PopupCreatedObserver observer(base::BindRepeating(
[](int* popup_data_id, WebContents** new_tab, WebContents* web_contents) {
EXPECT_EQ(*popup_data_id, -1);
EXPECT_FALSE(*new_tab);
*new_tab = web_contents;
*popup_data_id = Data::GetOrCreateForCurrentDocument(
web_contents->GetPrimaryMainFrame())
->unique_id();
},
&popup_data_id, &new_tab));
web_contents()->SetDelegate(&observer);
EXPECT_TRUE(ExecJs(top_frame_host(), "window.open()"));
EXPECT_TRUE(new_tab);
WaitForLoadStopWithoutSuccessCheck(new_tab);
Data* new_tab_data =
Data::GetForCurrentDocument(new_tab->GetPrimaryMainFrame());
EXPECT_TRUE(new_tab_data);
EXPECT_EQ(new_tab_data->unique_id(), popup_data_id);
web_contents()->SetDelegate(nullptr);
}
IN_PROC_BROWSER_TEST_F(DocumentUserDataTest, BlankIframe) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
GURL url_b(
embedded_test_server()->GetURL("b.com", "/page_with_blank_iframe.html"));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
int starting_id = next_id + 1;
std::vector<RenderFrameHost*> created_rfhs;
RenderFrameHostCreatedObserver observer(
web_contents(), base::BindRepeating(
[](std::vector<RenderFrameHost*>* created_rfhs,
RenderFrameHost* rfh) {
created_rfhs->push_back(rfh);
Data::GetOrCreateForCurrentDocument(rfh);
},
&created_rfhs));
EXPECT_TRUE(NavigateToURL(shell(), url_b));
std::vector<int> current_ids;
for (RenderFrameHost* rfh : created_rfhs) {
if (auto* data = Data::GetForCurrentDocument(rfh)) {
current_ids.push_back(data->unique_id());
}
}
EXPECT_EQ(2u, created_rfhs.size());
EXPECT_THAT(current_ids, testing::ElementsAre(starting_id, starting_id + 1));
}
IN_PROC_BROWSER_TEST_F(DocumentUserDataTest, SrcDocIframe) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
GURL url_b(embedded_test_server()->GetURL(
"b.com", "/frame_tree/page_with_srcdoc_frame.html"));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
int starting_id = next_id + 1;
std::vector<RenderFrameHost*> created_rfhs;
RenderFrameHostCreatedObserver observer(
web_contents(), base::BindRepeating(
[](std::vector<RenderFrameHost*>* created_rfhs,
RenderFrameHost* rfh) {
created_rfhs->push_back(rfh);
Data::GetOrCreateForCurrentDocument(rfh);
},
&created_rfhs));
EXPECT_TRUE(NavigateToURL(shell(), url_b));
std::vector<int> current_ids;
for (RenderFrameHost* rfh : created_rfhs) {
if (auto* data = Data::GetForCurrentDocument(rfh)) {
current_ids.push_back(data->unique_id());
}
}
EXPECT_EQ(2u, created_rfhs.size());
EXPECT_THAT(current_ids, testing::ElementsAre(starting_id));
}
IN_PROC_BROWSER_TEST_F(DocumentUserDataTest, AttachOnCreatingInitialFrame) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL("a.com", "/title1.html"));
bool observed_frame_creation = false;
RenderFrameHostCreatedObserver observer(
web_contents(),
base::BindRepeating(
[](bool* observed_frame_creation, RenderFrameHost* rfh) {
Data::GetOrCreateForCurrentDocument(rfh);
*observed_frame_creation = true;
},
&observed_frame_creation));
EXPECT_FALSE(
shell()->web_contents()->GetPrimaryMainFrame()->IsRenderFrameLive());
EXPECT_TRUE(NavigateToURL(shell(), url));
EXPECT_TRUE(observed_frame_creation);
EXPECT_FALSE(Data::GetForCurrentDocument(
shell()->web_contents()->GetPrimaryMainFrame()));
}
class DocumentUserDataWithBackForwardCacheTest : public DocumentUserDataTest {
public:
DocumentUserDataWithBackForwardCacheTest() {
scoped_feature_list_.InitWithFeaturesAndParameters(
GetDefaultEnabledBackForwardCacheFeaturesForTesting(
false),
GetDefaultDisabledBackForwardCacheFeaturesForTesting());
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
IN_PROC_BROWSER_TEST_F(DocumentUserDataWithBackForwardCacheTest,
BackForwardCacheNavigation) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
EXPECT_TRUE(NavigateToURL(shell(), url_a));
RenderFrameHostImpl* rfh_a = top_frame_host();
Data::CreateForCurrentDocument(rfh_a);
Data* data = Data::GetForCurrentDocument(rfh_a);
EXPECT_TRUE(data);
EXPECT_TRUE(NavigateToURL(shell(), url_b));
EXPECT_TRUE(rfh_a->IsInBackForwardCache());
data = Data::GetForCurrentDocument(rfh_a);
EXPECT_TRUE(data);
web_contents()->GetController().GoBack();
EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
Data* data_after_restore =
Data::GetForCurrentDocument(web_contents()->GetPrimaryMainFrame());
EXPECT_TRUE(data);
EXPECT_EQ(data_after_restore->unique_id(), data->unique_id());
}
}