blob: 038d741a70cdb50df714b6036b2a1724ff7b53ea [file] [log] [blame]
// Copyright 2019 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 <memory>
#include <vector>
#include "base/callback.h"
#include "base/containers/flat_set.h"
#include "base/memory/scoped_refptr.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/post_task.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/devtools/devtools_window_testing.h"
#include "chrome/browser/history/history_service_factory.h"
#include "chrome/browser/history/history_test_utils.h"
#include "chrome/browser/interstitials/security_interstitial_page_test_utils.h"
#include "chrome/browser/pdf/pdf_extension_test_util.h"
#include "chrome/browser/safe_browsing/test_safe_browsing_service.h"
#include "chrome/browser/task_manager/providers/task.h"
#include "chrome/browser/task_manager/task_manager_browsertest_util.h"
#include "chrome/browser/task_manager/task_manager_tester.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_dialogs.h"
#include "chrome/browser/ui/login/login_handler.h"
#include "chrome/browser/ui/login/login_handler_test_utils.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/safe_browsing/core/db/test_database_manager.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/page_type.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_navigation_observer.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/features.h"
#include "ui/base/l10n/l10n_util.h"
#include "url/gurl.h"
using content::WebContents;
class PortalBrowserTest : public InProcessBrowserTest {
public:
PortalBrowserTest() = default;
void SetUp() override {
scoped_feature_list_.InitWithFeatures(
/*enabled_features=*/{blink::features::kPortals,
blink::features::kPortalsCrossOrigin},
/*disabled_features=*/{});
InProcessBrowserTest::SetUp();
}
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
InProcessBrowserTest::SetUpOnMainThread();
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
IN_PROC_BROWSER_TEST_F(PortalBrowserTest, PortalActivation) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL("/portal/activate.html"));
ui_test_utils::NavigateToURL(browser(), url);
TabStripModel* tab_strip_model = browser()->tab_strip_model();
WebContents* contents = tab_strip_model->GetActiveWebContents();
EXPECT_EQ(1, tab_strip_model->count());
EXPECT_EQ(true, content::EvalJs(contents, "loadPromise"));
std::vector<WebContents*> inner_web_contents =
contents->GetInnerWebContents();
EXPECT_EQ(1u, inner_web_contents.size());
WebContents* portal_contents = inner_web_contents[0];
EXPECT_EQ(true, content::EvalJs(contents, "activate()"));
EXPECT_EQ(1, tab_strip_model->count());
EXPECT_EQ(portal_contents, tab_strip_model->GetActiveWebContents());
}
IN_PROC_BROWSER_TEST_F(PortalBrowserTest,
DevToolsWindowStaysOpenAfterActivation) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL("/portal/activate.html"));
ui_test_utils::NavigateToURL(browser(), url);
WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents();
EXPECT_EQ(true, content::EvalJs(contents, "loadPromise"));
DevToolsWindow* dev_tools_window =
DevToolsWindowTesting::OpenDevToolsWindowSync(browser(), true);
WebContents* main_web_contents =
DevToolsWindowTesting::Get(dev_tools_window)->main_web_contents();
EXPECT_EQ(main_web_contents,
DevToolsWindow::GetInTabWebContents(contents, nullptr));
EXPECT_EQ(true, content::EvalJs(contents, "activate()"));
EXPECT_EQ(main_web_contents,
DevToolsWindow::GetInTabWebContents(
browser()->tab_strip_model()->GetActiveWebContents(), nullptr));
}
IN_PROC_BROWSER_TEST_F(
PortalBrowserTest,
DevToolsWindowIsAttachedToOriginalWebContentsWhenActivationFails) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL("/portal/portal-no-src.html"));
ui_test_utils::NavigateToURL(browser(), url);
WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents();
DevToolsWindow* dev_tools_window =
DevToolsWindowTesting::OpenDevToolsWindowSync(browser(), true);
WebContents* main_web_contents =
DevToolsWindowTesting::Get(dev_tools_window)->main_web_contents();
EXPECT_EQ(main_web_contents,
DevToolsWindow::GetInTabWebContents(contents, nullptr));
EXPECT_EQ(true, content::EvalJs(contents, "activate()"));
EXPECT_EQ(main_web_contents,
DevToolsWindow::GetInTabWebContents(
browser()->tab_strip_model()->GetActiveWebContents(), nullptr));
}
IN_PROC_BROWSER_TEST_F(PortalBrowserTest, HttpBasicAuthenticationInPortal) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL("/title1.html"));
ui_test_utils::NavigateToURL(browser(), url);
WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents();
EXPECT_EQ(true,
content::EvalJs(contents,
"new Promise((resolve, reject) => {\n"
" let portal = document.createElement('portal');\n"
" portal.src = '/title2.html';\n"
" portal.onload = () => resolve(true);\n"
" document.body.appendChild(portal);\n"
"})"));
const auto& inner_contents = contents->GetInnerWebContents();
ASSERT_EQ(inner_contents.size(), 1u);
WebContents* portal_contents = inner_contents[0];
content::NavigationController& portal_controller =
portal_contents->GetController();
LoginPromptBrowserTestObserver login_observer;
login_observer.Register(
content::Source<content::NavigationController>(&portal_controller));
WindowedAuthNeededObserver auth_needed(&portal_controller);
ASSERT_TRUE(content::ExecJs(portal_contents,
"location.href = '/auth-basic?realm=Aperture'"));
auth_needed.Wait();
WindowedAuthSuppliedObserver auth_supplied(&portal_controller);
LoginHandler* login_handler = login_observer.handlers().front();
EXPECT_EQ(login_handler->auth_info().realm, "Aperture");
login_handler->SetAuth(base::ASCIIToUTF16("basicuser"),
base::ASCIIToUTF16("secret"));
auth_supplied.Wait();
base::string16 expected_title = base::ASCIIToUTF16("basicuser/secret");
content::TitleWatcher title_watcher(portal_contents, expected_title);
EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
}
namespace {
std::vector<base::string16> GetRendererTaskTitles(
task_manager::TaskManagerTester* tester) {
std::vector<base::string16> renderer_titles;
renderer_titles.reserve(tester->GetRowCount());
for (int row = 0; row < tester->GetRowCount(); row++) {
if (tester->GetTabId(row) != SessionID::InvalidValue())
renderer_titles.push_back(tester->GetRowTitle(row));
}
return renderer_titles;
}
} // namespace
// The task manager should show the portal tasks, and update the tasks after
// activation as tab contents become portals and vice versa.
IN_PROC_BROWSER_TEST_F(PortalBrowserTest, TaskManagerUpdatesAfterActivation) {
ASSERT_TRUE(embedded_test_server()->Start());
const base::string16 expected_tab_title_before_activation =
l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_TAB_PREFIX,
base::ASCIIToUTF16("activate.html"));
const base::string16 expected_tab_title_after_activation =
l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_TAB_PREFIX,
base::ASCIIToUTF16("activate-portal.html"));
const base::string16 expected_portal_title = l10n_util::GetStringFUTF16(
IDS_TASK_MANAGER_PORTAL_PREFIX, base::ASCIIToUTF16("http://127.0.0.1/"));
ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL("/portal/activate.html"));
WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
EXPECT_EQ(true, content::EvalJs(tab, "loadPromise"));
// Check that both tasks appear.
chrome::ShowTaskManager(browser());
auto tester = task_manager::TaskManagerTester::Create(base::Closure());
task_manager::browsertest_util::WaitForTaskManagerRows(
1, expected_tab_title_before_activation);
task_manager::browsertest_util::WaitForTaskManagerRows(1,
expected_portal_title);
EXPECT_THAT(GetRendererTaskTitles(tester.get()),
::testing::ElementsAre(expected_tab_title_before_activation,
expected_portal_title));
// Activate and check that this updates as expected.
EXPECT_EQ(true, content::EvalJs(tab, "activate()"));
task_manager::browsertest_util::WaitForTaskManagerRows(
1, expected_tab_title_after_activation);
task_manager::browsertest_util::WaitForTaskManagerRows(1,
expected_portal_title);
EXPECT_THAT(GetRendererTaskTitles(tester.get()),
::testing::ElementsAre(expected_tab_title_after_activation,
expected_portal_title));
}
// The task manager should show the portal tasks, and by default they should be
// grouped with their respective tabs. This is similar to
// TaskManagerOOPIFBrowserTest.OrderingOfDependentRows, but less exhaustive.
IN_PROC_BROWSER_TEST_F(PortalBrowserTest, TaskManagerOrderingOfDependentRows) {
ASSERT_TRUE(embedded_test_server()->Start());
const unsigned kNumTabs = 3;
const unsigned kPortalsPerTab = 2;
const base::string16 expected_tab_title = l10n_util::GetStringFUTF16(
IDS_TASK_MANAGER_TAB_PREFIX, base::ASCIIToUTF16("Title Of Awesomeness"));
const base::string16 expected_portal_title = l10n_util::GetStringFUTF16(
IDS_TASK_MANAGER_PORTAL_PREFIX, base::ASCIIToUTF16("http://127.0.0.1/"));
std::vector<base::string16> expected_titles;
for (unsigned i = 0; i < kNumTabs; i++) {
expected_titles.push_back(expected_tab_title);
for (unsigned j = 0; j < kPortalsPerTab; j++)
expected_titles.push_back(expected_portal_title);
}
// Open a number of new tabs.
std::vector<WebContents*> tab_contents;
for (unsigned i = 0; i < kNumTabs; i++) {
ui_test_utils::NavigateToURLWithDispositionBlockUntilNavigationsComplete(
browser(), embedded_test_server()->GetURL("/title2.html"), 1,
WindowOpenDisposition::NEW_FOREGROUND_TAB,
ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
tab_contents.push_back(tab);
}
// There's an initial tab that's implicitly created.
browser()->tab_strip_model()->CloseWebContentsAt(0,
TabStripModel::CLOSE_NONE);
EXPECT_EQ(static_cast<int>(kNumTabs), browser()->tab_strip_model()->count());
// Create portals in each tab.
for (WebContents* tab : tab_contents) {
EXPECT_EQ(static_cast<int>(kPortalsPerTab),
content::EvalJs(
tab, content::JsReplace(
"Promise.all([...Array($1)].map(() =>"
" new Promise((resolve, reject) => {"
" let portal = document.createElement('portal');"
" portal.src = '/title3.html';"
" portal.onload = () => resolve();"
" document.body.appendChild(portal);"
" }))).then(arr => arr.length)",
static_cast<int>(kPortalsPerTab))));
}
// Check that the tasks are grouped in the UI as expected.
chrome::ShowTaskManager(browser());
auto tester = task_manager::TaskManagerTester::Create(base::Closure());
task_manager::browsertest_util::WaitForTaskManagerRows(kNumTabs,
expected_tab_title);
task_manager::browsertest_util::WaitForTaskManagerRows(
kNumTabs * kPortalsPerTab, expected_portal_title);
EXPECT_THAT(GetRendererTaskTitles(tester.get()), expected_titles);
}
IN_PROC_BROWSER_TEST_F(PortalBrowserTest, PdfViewerLoadsInPortal) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL("/title1.html"));
ui_test_utils::NavigateToURL(browser(), url);
WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_EQ(true,
content::EvalJs(contents,
"new Promise((resolve) => {\n"
" let portal = document.createElement('portal');\n"
" portal.src = '/pdf/test.pdf';\n"
" portal.onload = () => { resolve(true); }\n"
" document.body.appendChild(portal);\n"
"});"));
std::vector<WebContents*> inner_web_contents =
contents->GetInnerWebContents();
ASSERT_EQ(1u, inner_web_contents.size());
WebContents* portal_contents = inner_web_contents[0];
EXPECT_TRUE(pdf_extension_test_util::EnsurePDFHasLoaded(portal_contents));
}
// Test that we do not show main frame interstitials in portal contents. We
// should treat portals like subframes in terms of how to display the error to
// the user.
IN_PROC_BROWSER_TEST_F(PortalBrowserTest, ShowSubFrameErrorPage) {
net::EmbeddedTestServer bad_https_server(net::EmbeddedTestServer::TYPE_HTTPS);
bad_https_server.AddDefaultHandlers(GetChromeTestDataDir());
bad_https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_EXPIRED);
ASSERT_TRUE(bad_https_server.Start());
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL("/title1.html"));
ui_test_utils::NavigateToURL(browser(), url);
WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents();
GURL bad_cert_url(bad_https_server.GetURL("/title1.html"));
content::TestNavigationObserver portal_navigation_observer(nullptr, 1);
portal_navigation_observer.StartWatchingNewWebContents();
ASSERT_EQ(true,
content::EvalJs(
contents, content::JsReplace(
"new Promise((resolve) => {"
" let portal = document.createElement('portal');"
" portal.src = $1;"
" portal.onload = () => { resolve(true); };"
" document.body.appendChild(portal);"
"});",
bad_cert_url)));
portal_navigation_observer.StopWatchingNewWebContents();
portal_navigation_observer.Wait();
EXPECT_FALSE(portal_navigation_observer.last_navigation_succeeded());
EXPECT_EQ(net::ERR_CERT_DATE_INVALID,
portal_navigation_observer.last_net_error_code());
std::vector<WebContents*> inner_web_contents =
contents->GetInnerWebContents();
ASSERT_EQ(1u, inner_web_contents.size());
WebContents* portal_contents = inner_web_contents[0];
content::NavigationController& controller = portal_contents->GetController();
content::NavigationEntry* entry = controller.GetLastCommittedEntry();
ASSERT_TRUE(entry);
EXPECT_EQ(content::PAGE_TYPE_ERROR, entry->GetPageType());
// Ensure that this is the net error page and not an interstitial.
ASSERT_FALSE(
chrome_browser_interstitials::IsShowingInterstitial(portal_contents));
// Also ensure that the error page is using its subframe layout.
ASSERT_EQ(true, content::EvalJs(
portal_contents,
"document.documentElement.hasAttribute('subframe');"));
}
IN_PROC_BROWSER_TEST_F(PortalBrowserTest, BrowserHistoryUpdatesOnActivation) {
ASSERT_TRUE(embedded_test_server()->Start());
Profile* profile = browser()->profile();
ASSERT_TRUE(profile);
ui_test_utils::WaitForHistoryToLoad(HistoryServiceFactory::GetForProfile(
profile, ServiceAccessType::EXPLICIT_ACCESS));
GURL url1(embedded_test_server()->GetURL("/title1.html"));
ui_test_utils::NavigateToURL(browser(), url1);
WaitForHistoryBackendToRun(profile);
EXPECT_TRUE(
base::Contains(ui_test_utils::HistoryEnumerator(profile).urls(), url1));
WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents();
GURL url2(embedded_test_server()->GetURL("/title2.html"));
ASSERT_EQ(true,
content::EvalJs(
contents, content::JsReplace(
"new Promise((resolve) => {"
" let portal = document.createElement('portal');"
" portal.src = $1;"
" portal.onload = () => { resolve(true); };"
" document.body.appendChild(portal);"
"});",
url2)));
WaitForHistoryBackendToRun(profile);
// Content loaded in a portal should not be considered a page visit by the
// user.
EXPECT_FALSE(
base::Contains(ui_test_utils::HistoryEnumerator(profile).urls(), url2));
ASSERT_EQ(true,
content::EvalJs(contents,
"let portal = document.querySelector('portal');"
"portal.activate().then(() => { return true; });"));
WaitForHistoryBackendToRun(profile);
// Now that the portal has activated, its contents are presented to the user
// as a navigation in the tab, so this should be considered a page visit.
EXPECT_TRUE(
base::Contains(ui_test_utils::HistoryEnumerator(profile).urls(), url2));
}
namespace {
// Allows us to treat certain URLs as dangerous with Safe Browsing.
class FakeSafeBrowsingDatabaseManager
: public safe_browsing::TestSafeBrowsingDatabaseManager {
public:
FakeSafeBrowsingDatabaseManager() = default;
void AddDangerousUrl(const GURL& dangerous_url) {
dangerous_urls_.insert(dangerous_url);
}
// safe_browsing::TestSafeBrowsingDatabaseManager:
bool CheckBrowseUrl(const GURL& url,
const safe_browsing::SBThreatTypeSet& threat_types,
Client* client) override {
if (!dangerous_urls_.contains(url))
return true;
base::PostTask(
FROM_HERE, {content::BrowserThread::IO},
base::BindOnce(&FakeSafeBrowsingDatabaseManager::CheckBrowseURLAsync,
this, url, client));
return false;
}
bool IsSupported() const override { return true; }
bool ChecksAreAlwaysAsync() const override { return false; }
bool CheckExtensionIDs(const std::set<std::string>& extension_ids,
Client* client) override {
return true;
}
bool CheckUrlForSubresourceFilter(const GURL& url, Client* client) override {
return true;
}
bool CanCheckResourceType(
blink::mojom::ResourceType resource_type) const override {
return true;
}
safe_browsing::ThreatSource GetThreatSource() const override {
// This choice is arbitrary. The blocking page expects this to not be
// |UNKNOWN|.
return safe_browsing::ThreatSource::LOCAL_PVER4;
}
private:
~FakeSafeBrowsingDatabaseManager() override = default;
void CheckBrowseURLAsync(const GURL& url, Client* client) {
client->OnCheckBrowseUrlResult(url,
safe_browsing::SB_THREAT_TYPE_URL_PHISHING,
safe_browsing::ThreatMetadata());
}
base::flat_set<GURL> dangerous_urls_;
};
} // namespace
class PortalSafeBrowsingBrowserTest : public PortalBrowserTest {
public:
PortalSafeBrowsingBrowserTest()
: safe_browsing_factory_(
std::make_unique<safe_browsing::TestSafeBrowsingServiceFactory>()) {
}
protected:
void CreatedBrowserMainParts(
content::BrowserMainParts* browser_main_parts) override {
fake_safe_browsing_database_manager_ =
base::MakeRefCounted<FakeSafeBrowsingDatabaseManager>();
safe_browsing_factory_->SetTestDatabaseManager(
fake_safe_browsing_database_manager_.get());
safe_browsing::SafeBrowsingService::RegisterFactory(
safe_browsing_factory_.get());
PortalBrowserTest::CreatedBrowserMainParts(browser_main_parts);
}
void TearDown() override {
PortalBrowserTest::TearDown();
safe_browsing::SafeBrowsingService::RegisterFactory(nullptr);
}
void AddDangerousUrl(const GURL& dangerous_url) {
fake_safe_browsing_database_manager_->AddDangerousUrl(dangerous_url);
}
private:
scoped_refptr<FakeSafeBrowsingDatabaseManager>
fake_safe_browsing_database_manager_;
std::unique_ptr<safe_browsing::TestSafeBrowsingServiceFactory>
safe_browsing_factory_;
};
// Tests that if a page embeds a portal whose contents are considered dangerous
// by Safe Browsing, the embedder is also treated as dangerous in terms of how
// we display the Safe Browsing interstitial.
IN_PROC_BROWSER_TEST_F(PortalSafeBrowsingBrowserTest,
EmbedderOfDangerousPortalConsideredDangerous) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL main_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
GURL dangerous_url(
embedded_test_server()->GetURL("evil.com", "/title2.html"));
AddDangerousUrl(dangerous_url);
ui_test_utils::NavigateToURL(browser(), main_url);
WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents();
content::TestNavigationObserver error_observer(contents,
net::ERR_BLOCKED_BY_CLIENT);
ASSERT_TRUE(content::ExecJs(
contents,
content::JsReplace("let portal = document.createElement('portal');"
"portal.src = $1;"
"document.body.appendChild(portal);",
dangerous_url)));
error_observer.WaitForNavigationFinished();
EXPECT_TRUE(chrome_browser_interstitials::IsShowingInterstitial(contents));
}
// Test that if a page embeds a portal which contains a dangerous subresource,
// the embedder is also treated as dangerous in terms of how we display the Safe
// Browsing interstitial.
IN_PROC_BROWSER_TEST_F(PortalSafeBrowsingBrowserTest,
PortalDangerousSubresource) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL main_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
GURL portal_url(embedded_test_server()->GetURL("b.com", "/title2.html"));
GURL dangerous_url(
embedded_test_server()->GetURL("evil.com", "/title3.html"));
AddDangerousUrl(dangerous_url);
ui_test_utils::NavigateToURL(browser(), main_url);
WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_EQ(true,
content::EvalJs(
contents, content::JsReplace(
"new Promise((resolve) => {"
" let portal = document.createElement('portal');"
" portal.src = $1;"
" portal.onload = () => { resolve(true); };"
" document.body.appendChild(portal);"
"});",
portal_url)));
std::vector<WebContents*> inner_web_contents =
contents->GetInnerWebContents();
ASSERT_EQ(1u, inner_web_contents.size());
WebContents* portal_contents = inner_web_contents[0];
content::TestNavigationObserver error_observer(contents,
net::ERR_BLOCKED_BY_CLIENT);
ASSERT_TRUE(content::ExecJs(
portal_contents,
content::JsReplace("let iframe = document.createElement('iframe');"
"iframe.src = $1;"
"document.body.appendChild(iframe);",
dangerous_url)));
error_observer.WaitForNavigationFinished();
EXPECT_TRUE(chrome_browser_interstitials::IsShowingInterstitial(contents));
}
IN_PROC_BROWSER_TEST_F(PortalSafeBrowsingBrowserTest, DangerousOrphanedPortal) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL main_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
GURL portal_url(embedded_test_server()->GetURL("b.com", "/title2.html"));
GURL dangerous_url(
embedded_test_server()->GetURL("evil.com", "/title3.html"));
AddDangerousUrl(dangerous_url);
ui_test_utils::NavigateToURL(browser(), main_url);
WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_EQ(true,
content::EvalJs(
contents, content::JsReplace(
"new Promise((resolve) => {"
" let portal = document.createElement('portal');"
" portal.src = $1;"
" portal.onload = () => { resolve(true); };"
" document.body.appendChild(portal);"
"});",
portal_url)));
std::vector<WebContents*> inner_web_contents =
contents->GetInnerWebContents();
ASSERT_EQ(1u, inner_web_contents.size());
WebContents* portal_contents = inner_web_contents[0];
// Block the activate callback so that the predecessor portal stays orphaned
// while it navigates to a dangerous URL.
ASSERT_TRUE(content::ExecJs(
portal_contents, "window.onportalactivate = e => { while(true) {} };"));
// Since the portal contents becomes the top level contents from the following
// activation, it's the contents where we show the interstitial.
content::TestNavigationObserver error_observer(portal_contents,
net::ERR_BLOCKED_BY_CLIENT);
ASSERT_TRUE(content::ExecJs(
contents,
content::JsReplace("document.querySelector('portal').activate();"
"window.location.href = $1;",
dangerous_url)));
error_observer.WaitForNavigationFinished();
EXPECT_TRUE(
chrome_browser_interstitials::IsShowingInterstitial(portal_contents));
}