#include "base/command_line.h"
#include "base/test/scoped_feature_list.h"
#include "content/browser/devtools/devtools_instrumentation.h"
#include "content/browser/devtools/protocol/audits.h"
#include "content/browser/devtools/protocol/devtools_protocol_test_support.h"
#include "content/browser/devtools/render_frame_devtools_agent_host.h"
#include "content/browser/renderer_host/frame_tree_node.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/devtools_agent_host.h"
#include "content/public/browser/devtools_agent_host_client.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_features.h"
#include "content/public/common/url_constants.h"
#include "content/public/test/back_forward_cache_util.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_base.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/fenced_frame_test_util.h"
#include "content/public/test/prerender_test_util.h"
#include "content/public/test/test_utils.h"
#include "content/shell/browser/shell.h"
#include "content/test/content_browser_test_utils_internal.h"
#include "net/dns/mock_host_resolver.h"
namespace content {
class DevToolsIssueStorageBrowserTest : public DevToolsProtocolTest {
public:
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
SetupCrossSiteRedirector(embedded_test_server());
}
protected:
WebContentsImpl* web_contents() {
return static_cast<WebContentsImpl*>(shell()->web_contents());
}
RenderFrameHostImpl* main_frame_host() {
return web_contents()->GetPrimaryFrameTree().GetMainFrame();
}
void WaitForDummyIssueNotification() {
base::Value::Dict notification =
WaitForNotification("Audits.issueAdded", true);
EXPECT_EQ(*notification.FindStringByDottedPath("issue.code"),
protocol::Audits::InspectorIssueCodeEnum::CookieIssue);
}
};
namespace {
void ReportDummyIssue(RenderFrameHostImpl* rfh) {
auto issueDetails = protocol::Audits::InspectorIssueDetails::Create();
auto inspector_issue =
protocol::Audits::InspectorIssue::Create()
.SetCode(protocol::Audits::InspectorIssueCodeEnum::CookieIssue)
.SetDetails(issueDetails.Build())
.Build();
devtools_instrumentation::ReportBrowserInitiatedIssue(
rfh, std::move(inspector_issue));
}
}
IN_PROC_BROWSER_TEST_F(DevToolsIssueStorageBrowserTest,
DevToolsReceivesBrowserIssues) {
EXPECT_TRUE(NavigateToURL(shell(), GURL("about:blank")));
ReportDummyIssue(main_frame_host());
Attach();
SendCommandSync("Audits.enable");
WaitForDummyIssueNotification();
}
IN_PROC_BROWSER_TEST_F(DevToolsIssueStorageBrowserTest,
DevToolsReceivesBrowserIssuesWhileAttached) {
EXPECT_TRUE(NavigateToURL(shell(), GURL("about:blank")));
Attach();
SendCommandSync("Audits.enable");
ReportDummyIssue(main_frame_host());
WaitForDummyIssueNotification();
}
IN_PROC_BROWSER_TEST_F(DevToolsIssueStorageBrowserTest,
DeleteSubframeWithIssue) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL test_url =
embedded_test_server()->GetURL("/devtools/page-with-oopif.html");
EXPECT_TRUE(NavigateToURL(shell(), test_url));
RenderFrameHostImpl* main_frame = main_frame_host();
EXPECT_EQ(main_frame->child_count(), static_cast<unsigned>(1));
RenderFrameHostImpl* iframe = main_frame->child_at(0)->current_frame_host();
EXPECT_FALSE(iframe->is_main_frame());
ReportDummyIssue(iframe);
main_frame->RemoveChild(iframe->frame_tree_node());
Attach();
SendCommandSync("Audits.enable");
WaitForDummyIssueNotification();
}
IN_PROC_BROWSER_TEST_F(DevToolsIssueStorageBrowserTest,
MainFrameNavigationClearsIssues) {
EXPECT_TRUE(NavigateToURL(shell(), GURL("about:blank")));
ReportDummyIssue(main_frame_host());
ASSERT_TRUE(embedded_test_server()->Start());
GURL test_url = embedded_test_server()->GetURL("/devtools/navigation.html");
EXPECT_TRUE(NavigateToURL(shell(), test_url));
Attach();
SendCommandSync("Audits.enable");
ASSERT_FALSE(HasExistingNotification());
}
IN_PROC_BROWSER_TEST_F(DevToolsIssueStorageBrowserTest,
IssueForSkippableNavigationEntry_MainFrame) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL non_skippable_url(
embedded_test_server()->GetURL("a.com", "/title1.html"));
EXPECT_TRUE(NavigateToURL(shell(), non_skippable_url));
GURL skippable_url(embedded_test_server()->GetURL("b.com", "/title2.html"));
EXPECT_TRUE(NavigateToURL(shell(), skippable_url));
GURL redirected_url(embedded_test_server()->GetURL("c.com", "/title3.html"));
EXPECT_TRUE(
NavigateToURLFromRendererWithoutUserGesture(shell(), redirected_url));
Attach();
SendCommandSync("Audits.enable");
base::Value::Dict notification =
WaitForNotification("Audits.issueAdded", true);
EXPECT_EQ(*notification.FindStringByDottedPath("issue.code"),
protocol::Audits::InspectorIssueCodeEnum::GenericIssue);
EXPECT_EQ(*notification.FindStringByDottedPath(
"issue.details.genericIssueDetails.errorType"),
protocol::Audits::GenericIssueErrorTypeEnum::
NavigationEntryMarkedSkippable);
EXPECT_THAT(*notification.FindStringByDottedPath(
"issue.details.genericIssueDetails.request.url"),
testing::HasSubstr("/title2.html"));
}
IN_PROC_BROWSER_TEST_F(DevToolsIssueStorageBrowserTest,
IssueForSkippableNavigationEntry_SubFrame) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL page_with_iframe_url =
embedded_test_server()->GetURL("/devtools/page-with-oopif.html");
EXPECT_TRUE(NavigateToURL(shell(), page_with_iframe_url));
RenderFrameHostImpl* main_frame = main_frame_host();
GURL next_url(embedded_test_server()->GetURL("bar.com", "/title2.html"));
EXPECT_TRUE(NavigateToURLFromRendererWithoutUserGesture(
main_frame->child_at(0)->current_frame_host(), next_url));
EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
RenderFrameHost* child_frame = main_frame->child_at(0)->current_frame_host();
EXPECT_TRUE(child_frame);
EXPECT_EQ(child_frame->GetLastCommittedURL(), next_url);
EXPECT_EQ(main_frame->GetLastCommittedURL(), page_with_iframe_url);
AttachToFrameTreeHost(child_frame);
SendCommandSync("Audits.enable");
base::Value::Dict notification =
WaitForNotification("Audits.issueAdded", true);
EXPECT_EQ(*notification.FindStringByDottedPath("issue.code"),
protocol::Audits::InspectorIssueCodeEnum::GenericIssue);
EXPECT_EQ(*notification.FindStringByDottedPath(
"issue.details.genericIssueDetails.errorType"),
protocol::Audits::GenericIssueErrorTypeEnum::
NavigationEntryMarkedSkippable);
EXPECT_THAT(*notification.FindStringByDottedPath(
"issue.details.genericIssueDetails.request.url"),
testing::HasSubstr("/title1.html"));
}
class DevToolsIssueStorageWithBackForwardCacheBrowserTest
: public DevToolsIssueStorageBrowserTest {
public:
void SetUpCommandLine(base::CommandLine* command_line) override {
feature_list_.InitWithFeaturesAndParameters(
GetDefaultEnabledBackForwardCacheFeaturesForTesting(),
GetDefaultDisabledBackForwardCacheFeaturesForTesting());
}
protected:
base::test::ScopedFeatureList feature_list_;
};
IN_PROC_BROWSER_TEST_F(DevToolsIssueStorageWithBackForwardCacheBrowserTest,
BackForwardCacheGoBack) {
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 = main_frame_host();
RenderFrameDeletedObserver rfh_a_deleted(rfh_a);
ReportDummyIssue(rfh_a);
EXPECT_TRUE(NavigateToURL(shell(), url_b));
EXPECT_TRUE(rfh_a->IsInBackForwardCache());
web_contents()->GetController().GoBack();
EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
EXPECT_FALSE(rfh_a_deleted.deleted());
EXPECT_EQ(main_frame_host(), rfh_a);
Attach();
SendCommandSync("Audits.enable");
WaitForDummyIssueNotification();
}
class DevToolsIssueStorageWithPrerenderBrowserTest
: public DevToolsIssueStorageBrowserTest {
public:
DevToolsIssueStorageWithPrerenderBrowserTest()
: prerender_test_helper_(base::BindRepeating(
&DevToolsIssueStorageWithPrerenderBrowserTest::GetWebContents,
base::Unretained(this))) {}
void SetUp() override {
prerender_test_helper().RegisterServerRequestMonitor(
embedded_test_server());
DevToolsIssueStorageBrowserTest::SetUp();
}
test::PrerenderTestHelper& prerender_test_helper() {
return prerender_test_helper_;
}
private:
WebContents* GetWebContents() { return web_contents(); }
test::PrerenderTestHelper prerender_test_helper_;
};
IN_PROC_BROWSER_TEST_F(DevToolsIssueStorageWithPrerenderBrowserTest,
IssueWhilePrerendering) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL main_url(embedded_test_server()->GetURL("/empty.html"));
GURL prerender_url(embedded_test_server()->GetURL("/title1.html"));
EXPECT_TRUE(NavigateToURL(shell(), main_url));
FrameTreeNodeId host_id = prerender_test_helper().AddPrerender(prerender_url);
RenderFrameHostImpl* prerender_rfh = static_cast<RenderFrameHostImpl*>(
prerender_test_helper().GetPrerenderedMainFrameHost(host_id));
ReportDummyIssue(prerender_rfh);
prerender_test_helper().NavigatePrimaryPage(prerender_url);
Attach();
SendCommandSync("Audits.enable");
WaitForDummyIssueNotification();
}
class DevToolsIssueStorageFencedFrameTest
: public DevToolsIssueStorageBrowserTest {
public:
content::test::FencedFrameTestHelper& fenced_frame_test_helper() {
return fenced_frame_helper_;
}
protected:
content::test::FencedFrameTestHelper fenced_frame_helper_;
};
IN_PROC_BROWSER_TEST_F(DevToolsIssueStorageFencedFrameTest,
DeleteFencedFrameWithIssue) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL test_url = embedded_test_server()->GetURL("/title1.html");
EXPECT_TRUE(NavigateToURL(shell(), test_url));
GURL fenced_frame_url =
embedded_test_server()->GetURL("/fenced_frames/title1.html");
content::RenderFrameHostImpl* fenced_frame_rfh =
static_cast<RenderFrameHostImpl*>(
fenced_frame_test_helper().CreateFencedFrame(
web_contents()->GetPrimaryMainFrame(), fenced_frame_url));
EXPECT_NE(nullptr, fenced_frame_rfh);
ReportDummyIssue(fenced_frame_rfh);
EXPECT_TRUE(ExecJs(shell()->web_contents(),
"document.querySelector('fencedframe').remove()"));
Attach();
SendCommandSync("Audits.enable");
WaitForDummyIssueNotification();
}
}