[go: nahoru, domu]

blob: 9c2961326829d84536623cff3c789c1035e5003b [file] [log] [blame]
// Copyright 2014 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 "base/base_paths.h"
#include "base/bind.h"
#include "base/files/file_util.h"
#include "base/location.h"
#include "base/macros.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_timeouts.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/guest_view/browser/test_guest_view_manager.h"
#include "components/javascript_dialogs/app_modal_dialog_controller.h"
#include "components/javascript_dialogs/app_modal_dialog_view.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_renderer_host.h"
#include "content/public/test/test_utils.h"
#include "extensions/browser/api/extensions_api_client.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/guest_view/extensions_guest_view_manager_delegate.h"
#include "extensions/browser/guest_view/mime_handler_view/mime_handler_stream_manager.h"
#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h"
#include "extensions/browser/guest_view/mime_handler_view/test_mime_handler_view_guest.h"
#include "extensions/browser/process_manager.h"
#include "extensions/common/constants.h"
#include "extensions/common/guest_view/extensions_guest_view_messages.h"
#include "extensions/common/mojom/guest_view.mojom.h"
#include "extensions/test/result_catcher.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/http_request.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
#include "ui/base/ui_base_features.h"
#include "url/url_constants.h"
using extensions::ExtensionsAPIClient;
using extensions::MimeHandlerStreamManager;
using extensions::MimeHandlerViewGuest;
using extensions::TestMimeHandlerViewGuest;
using guest_view::GuestViewManager;
using guest_view::GuestViewManagerDelegate;
using guest_view::TestGuestViewManager;
using guest_view::TestGuestViewManagerFactory;
class MimeHandlerViewTest : public extensions::ExtensionApiTest {
public:
MimeHandlerViewTest() {
GuestViewManager::set_factory_for_testing(&factory_);
}
~MimeHandlerViewTest() override {}
void SetUpOnMainThread() override {
extensions::ExtensionApiTest::SetUpOnMainThread();
embedded_test_server()->ServeFilesFromDirectory(
test_data_dir_.AppendASCII("mime_handler_view"));
embedded_test_server()->RegisterRequestMonitor(base::BindRepeating(
&MimeHandlerViewTest::MonitorRequest, base::Unretained(this)));
host_resolver()->AddRule("*", "127.0.0.1");
ASSERT_TRUE(StartEmbeddedTestServer());
}
// TODO(paulmeyer): This function is implemented over and over by the
// different GuestView test classes. It really needs to be refactored out to
// some kind of GuestViewTest base class.
TestGuestViewManager* GetGuestViewManager() const {
TestGuestViewManager* manager = static_cast<TestGuestViewManager*>(
TestGuestViewManager::FromBrowserContext(browser()->profile()));
// TestGuestViewManager::WaitForSingleGuestCreated can and will get called
// before a guest is created. Since GuestViewManager is usually not created
// until the first guest is created, this means that |manager| will be
// nullptr if trying to use the manager to wait for the first guest. Because
// of this, the manager must be created here if it does not already exist.
if (!manager) {
manager = static_cast<TestGuestViewManager*>(
GuestViewManager::CreateWithDelegate(
browser()->profile(),
ExtensionsAPIClient::Get()->CreateGuestViewManagerDelegate(
browser()->profile())));
}
return manager;
}
content::WebContents* GetEmbedderWebContents() {
return browser()->tab_strip_model()->GetWebContentsAt(0);
}
MimeHandlerViewGuest* GetLastGuestView() const {
return MimeHandlerViewGuest::FromWebContents(
GetGuestViewManager()->GetLastGuestCreated());
}
const extensions::Extension* LoadTestExtension() {
const extensions::Extension* extension =
LoadExtension(test_data_dir_.AppendASCII("mime_handler_view"));
if (!extension)
return nullptr;
EXPECT_EQ(std::string(extension_misc::kMimeHandlerPrivateTestExtensionId),
extension->id());
return extension;
}
void RunTestWithUrl(const GURL& url) {
// Use the testing subclass of MimeHandlerViewGuest.
GetGuestViewManager()->RegisterTestGuestViewType<MimeHandlerViewGuest>(
base::BindRepeating(&TestMimeHandlerViewGuest::Create));
const extensions::Extension* extension = LoadTestExtension();
ASSERT_TRUE(extension);
extensions::ResultCatcher catcher;
ui_test_utils::NavigateToURL(browser(), url);
if (!catcher.GetNextResult())
FAIL() << catcher.message();
}
void RunTest(const std::string& path) {
RunTestWithUrl(embedded_test_server()->GetURL("/" + path));
}
int basic_count() const { return basic_count_; }
private:
void MonitorRequest(const net::test_server::HttpRequest& request) {
if (request.relative_url == "/testBasic.csv")
basic_count_++;
}
TestGuestViewManagerFactory factory_;
base::test::ScopedFeatureList scoped_feature_list_;
int basic_count_ = 0;
};
class UserActivationUpdateWaiter {
public:
explicit UserActivationUpdateWaiter(content::WebContents* web_contents) {
user_activation_interceptor_.Init(web_contents->GetMainFrame());
}
~UserActivationUpdateWaiter() = default;
void Wait() {
if (user_activation_interceptor_.update_user_activation_state())
return;
base::RunLoop run_loop;
user_activation_interceptor_.set_quit_handler(run_loop.QuitClosure());
run_loop.Run();
}
private:
content::UpdateUserActivationStateInterceptor user_activation_interceptor_;
};
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, Embedded) {
RunTest("test_embedded.html");
// Sanity check. Navigate the page and verify the guest goes away.
ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
auto* gv_manager = GetGuestViewManager();
gv_manager->WaitForAllGuestsDeleted();
EXPECT_EQ(1U, gv_manager->num_guests_created());
}
// This test start with an <object> that has a content frame. Then the content
// frame (plugin frame) is navigated to a cross-origin target page. After the
// navigation is completed, the <object> is set to render MimeHandlerView by
// setting its |data| and |type| attributes accordingly.
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, EmbedWithInitialCrossOriginFrame) {
const std::string kTestName = "test_cross_origin_frame";
std::string cross_origin_url =
embedded_test_server()->GetURL("b.com", "/test_page.html").spec();
auto test_url = embedded_test_server()->GetURL(
"a.com",
base::StringPrintf("/test_object_with_frame.html?test_data=%s,%s,%s",
kTestName.c_str(), cross_origin_url.c_str(),
"testEmbedded.csv"));
RunTestWithUrl(test_url);
}
// This test verifies that navigations on the plugin frame before setting it to
// load MimeHandlerView do not race with the creation of the guest. The test
// loads a page with an <object> which is first navigated to some cross-origin
// domain and then immediately after load, the page triggers a navigation of its
// own to another cross-origin domain. Meanwhile the embedder sets the <object>
// to load a MimeHandlerView. The test passes if MHV loads. This is to catch the
// potential race between the cross-origin renderer initiated navigation and
// the navigation to "about:blank" started from the browser.
//
// Disabled due to flakiness: https://crbug.com/1002788.
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest,
DISABLED_NavigationRaceFromEmbedder) {
const std::string kTestName = "test_navigation_race_embedder";
auto cross_origin_url =
embedded_test_server()->GetURL("b.com", "/test_page.html").spec();
auto test_url = embedded_test_server()->GetURL(
"a.com",
base::StringPrintf("/test_object_with_frame.html?test_data=%s,%s,%s",
kTestName.c_str(), cross_origin_url.c_str(),
"testEmbedded.csv"));
RunTestWithUrl(test_url);
}
// TODO(ekaramad): Without proper handling of navigation to 'about:blank', this
// test would be flaky. Use TestNavigationManager class and possibly break the
// test into more sub-tests for various scenarios (https://crbug.com/659750).
// This test verifies that (almost) concurrent navigations in a cross-process
// frame inside an <embed> which is transitioning to a MimeHandlerView will
// not block creation of MimeHandlerView. The test will load some cross-origin
// content in <object> which right after loading will navigate it self to some
// other cross-origin content. On the embedder side, when the first page loads,
// the <object> loads some text/csv content to create a MimeHandlerViewGuest.
// The test passes if MHV loads.
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest,
NavigationRaceFromCrossProcessRenderer) {
const std::string kTestName = "test_navigation_race_cross_origin";
auto cross_origin_url =
embedded_test_server()->GetURL("b.com", "/test_page.html").spec();
auto other_cross_origin_url =
embedded_test_server()->GetURL("c.com", "/test_page.html").spec();
auto test_url = embedded_test_server()->GetURL(
"a.com",
base::StringPrintf("/test_object_with_frame.html?test_data=%s,%s,%s,%s",
kTestName.c_str(), cross_origin_url.c_str(),
other_cross_origin_url.c_str(), "testEmbedded.csv"));
RunTestWithUrl(test_url);
}
// This test verifies that removing embedder RenderFrame will not crash the
// renderer (for context see https://crbug.com/930803).
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, EmbedderFrameRemovedNoCrash) {
RunTest("test_iframe_basic.html");
auto* guest_view = GuestViewBase::FromWebContents(
GetGuestViewManager()->WaitForSingleGuestCreated());
ASSERT_TRUE(guest_view);
int32_t element_instance_id = guest_view->element_instance_id();
auto* embedder_web_contents = GetEmbedderWebContents();
auto* child_frame =
content::ChildFrameAt(embedder_web_contents->GetMainFrame(), 0);
content::RenderFrameDeletedObserver render_frame_observer(child_frame);
ASSERT_TRUE(
content::ExecJs(embedder_web_contents,
"document.querySelector('iframe').outerHTML = ''"));
render_frame_observer.WaitUntilDeleted();
// Send the IPC. During destruction MHVFC would cause a UaF since it was not
// removed from the global map.
mojo::AssociatedRemote<extensions::mojom::MimeHandlerViewContainerManager>
container_manager;
embedder_web_contents->GetMainFrame()
->GetRemoteAssociatedInterfaces()
->GetInterface(&container_manager);
container_manager->DestroyFrameContainer(element_instance_id);
// Running the following JS code fails if the renderer has crashed.
ASSERT_TRUE(content::ExecJs(embedder_web_contents, "window.name = 'foo'"));
}
// TODO(ekaramad): Somehow canceling a first dialog in a setup similar to the
// test below pops up another dialog. This is likely due to the navigation to
// about:blank from both the browser side and the embedder side in the method
// HTMLPlugInElement::RequestObjectInternal. Find out the issue and add another
// test here where the dialog is dismissed and the guest not created.
// (https://crbug.com/659750).
// This test verifies that transitioning a plugin element from text/html to
// application/pdf respects 'beforeunload'. The test specifically checks that
// 'beforeunload' dialog is shown to the user and if the user decides to
// proceed with the transition, MimeHandlerViewGuest is created.
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest,
EmbedWithInitialFrameAcceptBeforeUnloadDialog) {
// Use the testing subclass of MimeHandlerViewGuest.
GetGuestViewManager()->RegisterTestGuestViewType<MimeHandlerViewGuest>(
base::BindRepeating(&TestMimeHandlerViewGuest::Create));
const extensions::Extension* extension = LoadTestExtension();
ASSERT_TRUE(extension);
ui_test_utils::NavigateToURL(
browser(),
embedded_test_server()->GetURL("a.com", "/test_object_with_frame.html"));
auto* main_frame =
browser()->tab_strip_model()->GetWebContentsAt(0)->GetMainFrame();
auto url_with_beforeunload =
embedded_test_server()->GetURL("b.com", "/test_page.html?beforeunload");
bool result = false;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
main_frame,
base::StringPrintf(
"object.data = '%s';"
" object. => window.domAutomationController.send(true);",
url_with_beforeunload.spec().c_str()),
&result));
ASSERT_TRUE(result);
// Give user gesture to the frame, set the <object> to text/csv resource and
// handle the "beforeunload" dialog.
content::PrepContentsForBeforeUnloadTest(
browser()->tab_strip_model()->GetWebContentsAt(0));
ASSERT_TRUE(content::ExecuteScript(main_frame,
"object.data = './testEmbedded.csv';"
"object.type = 'text/csv';"));
javascript_dialogs::AppModalDialogController* alert =
ui_test_utils::WaitForAppModalDialog();
ASSERT_TRUE(alert->is_before_unload_dialog());
alert->view()->AcceptAppModalDialog();
EXPECT_TRUE(GetGuestViewManager()->WaitForSingleGuestCreated());
}
// The following tests will eventually converted into a parametric version which
// will run on both BrowserPlugin-based and cross-process-frame-based
// MimeHandlerView (https://crbug.com/659750).
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, PostMessage) {
RunTest("test_postmessage.html");
}
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, Basic) {
RunTest("testBasic.csv");
// Verify that for a navigation to a MimeHandlerView MIME type, exactly one
// stream is intercepted. This means we do not create a PluginDocument. If a
// PluginDocument was created here, the |view_id| associated with the
// stream intercepted from navigation response would be lost (
// PluginDocument does not talk to a MimeHandlerViewFrameContainer). Then,
// the newly added <embed> by the PluginDocument would send its own request
// leading to a total of 2 intercepted streams. The first one (from
// navigation) would never be released.
EXPECT_EQ(0U, MimeHandlerStreamManager::Get(
GetEmbedderWebContents()->GetBrowserContext())
->streams_.size());
}
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, Iframe) {
RunTest("test_iframe.html");
}
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, NonAsciiHeaders) {
RunTest("testNonAsciiHeaders.csv");
}
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, DataUrl) {
const char* kDataUrlCsv = "data:text/csv;base64,Y29udGVudCB0byByZWFkCg==";
RunTestWithUrl(GURL(kDataUrlCsv));
}
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, EmbeddedDataUrlObject) {
RunTest("test_embedded_data_url_object.html");
}
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, EmbeddedDataUrlEmbed) {
RunTest("test_embedded_data_url_embed.html");
}
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, EmbeddedDataUrlLong) {
RunTest("test_embedded_data_url_long.html");
}
// Regression test for crbug.com/587709.
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, SingleRequest) {
GURL url(embedded_test_server()->GetURL("/testBasic.csv"));
RunTest("testBasic.csv");
EXPECT_EQ(1, basic_count());
}
// Test that a mime handler view can keep a background page alive.
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, BackgroundPage) {
extensions::ProcessManager::SetEventPageIdleTimeForTesting(1);
extensions::ProcessManager::SetEventPageSuspendingTimeForTesting(1);
RunTest("testBackgroundPage.csv");
}
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, TargetBlankAnchor) {
RunTest("testTargetBlankAnchor.csv");
ASSERT_EQ(2, browser()->tab_strip_model()->count());
EXPECT_TRUE(content::WaitForLoadStop(
browser()->tab_strip_model()->GetWebContentsAt(1)));
EXPECT_EQ(
GURL(url::kAboutBlankURL),
browser()->tab_strip_model()->GetWebContentsAt(1)->GetLastCommittedURL());
}
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, BeforeUnload_NoDialog) {
ASSERT_NO_FATAL_FAILURE(RunTest("testBeforeUnloadNoDialog.csv"));
auto* web_contents = GetEmbedderWebContents();
content::PrepContentsForBeforeUnloadTest(web_contents);
// Wait for a round trip to the outer renderer to ensure any beforeunload
// toggle IPC has had time to reach the browser.
ExecuteScriptAndGetValue(web_contents->GetMainFrame(), "");
// Try to navigate away from the page. If the beforeunload listener is
// triggered and a dialog is shown, this navigation will never complete,
// causing the test to timeout and fail.
ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
}
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, BeforeUnload_ShowDialog) {
ASSERT_NO_FATAL_FAILURE(RunTest("testBeforeUnloadShowDialog.csv"));
auto* web_contents = GetEmbedderWebContents();
content::PrepContentsForBeforeUnloadTest(web_contents);
// Wait for a round trip to the outer renderer to ensure the beforeunload
// toggle IPC has had time to reach the browser.
ExecuteScriptAndGetValue(web_contents->GetMainFrame(), "");
web_contents->GetController().LoadURL(GURL(url::kAboutBlankURL), {},
ui::PAGE_TRANSITION_TYPED, "");
javascript_dialogs::AppModalDialogController* before_unload_dialog =
ui_test_utils::WaitForAppModalDialog();
EXPECT_TRUE(before_unload_dialog->is_before_unload_dialog());
EXPECT_FALSE(before_unload_dialog->is_reload());
before_unload_dialog->OnAccept(std::u16string(), false);
}
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest,
BeforeUnloadEnabled_WithoutUserActivation) {
ASSERT_NO_FATAL_FAILURE(RunTest("testBeforeUnloadWithUserActivation.csv"));
auto* web_contents = GetEmbedderWebContents();
// Prepare frames but don't trigger user activation.
content::PrepContentsForBeforeUnloadTest(web_contents, false);
// Even though this test's JS setup enables BeforeUnload dialogs, the dialog
// is still suppressed here because of lack of user activation. As a result,
// the following navigation away from the page works fine. If a beforeunload
// dialog were shown, this navigation would fail, causing the test to timeout.
ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
}
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest,
BeforeUnloadEnabled_WithUserActivation) {
ASSERT_NO_FATAL_FAILURE(RunTest("testBeforeUnloadWithUserActivation.csv"));
auto* web_contents = GetEmbedderWebContents();
// Prepare frames but don't trigger user activation across all frames.
content::PrepContentsForBeforeUnloadTest(web_contents, false);
// Make sure we have a guestviewmanager.
auto* guest_contents = GetGuestViewManager()->WaitForSingleGuestCreated();
UserActivationUpdateWaiter activation_waiter(guest_contents);
// Activate |guest_contents| through a click, then wait until the activation
// IPC reaches the browser process.
SimulateMouseClick(guest_contents, 0, blink::WebMouseEvent::Button::kLeft);
activation_waiter.Wait();
// Wait for a round trip to the outer renderer to ensure any beforeunload
// toggle IPC has had time to reach the browser.
ExecuteScriptAndGetValue(web_contents->GetMainFrame(), "");
// Try to navigate away, this should invoke a beforeunload dialog.
web_contents->GetController().LoadURL(GURL(url::kAboutBlankURL), {},
ui::PAGE_TRANSITION_TYPED, "");
javascript_dialogs::AppModalDialogController* before_unload_dialog =
ui_test_utils::WaitForAppModalDialog();
EXPECT_TRUE(before_unload_dialog->is_before_unload_dialog());
EXPECT_FALSE(before_unload_dialog->is_reload());
before_unload_dialog->OnAccept(std::u16string(), false);
}
// Helper class to wait for document load event in the main frame.
class DocumentLoadComplete : public content::WebContentsObserver {
public:
explicit DocumentLoadComplete(content::WebContents* web_contents)
: content::WebContentsObserver(web_contents) {}
~DocumentLoadComplete() override {}
void DocumentOnLoadCompletedInMainFrame(
content::RenderFrameHost* render_frame_host) override {
did_load_ = true;
run_loop_.Quit();
}
void Wait() {
if (!did_load_)
run_loop_.Run();
}
private:
bool did_load_ = false;
base::RunLoop run_loop_;
};
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, ActivatePostMessageSupportOnce) {
RunTest("test_embedded.html");
// Attach a second <embed>.
ASSERT_TRUE(content::ExecJs(GetEmbedderWebContents(),
"const e = document.createElement('embed');"
"e.src = './testEmbedded.csv'; e.type='text/csv';"
"document.body.appendChild(e);"));
DocumentLoadComplete(GetGuestViewManager()->WaitForNextGuestCreated()).Wait();
// After load, an IPC has been sent to the renderer to update routing IDs for
// the guest frame and the content frame (and activate the
// PostMessageSupport). Run some JS to Ensure no DCHECKs have fired in the
// embedder process.
ASSERT_TRUE(content::ExecJs(GetEmbedderWebContents(), "foo = 0;"));
}
// This is a minimized repro for a clusterfuzz crasher and is not really related
// to MimeHandlerView. The test verifies that when
// HTMLPlugInElement::PluginWrapper is called for a plugin with no node document
// frame, the renderer does not crash (see https://966371).
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, AdoptNodeInOnLoadDoesNotCrash) {
ui_test_utils::NavigateToURL(
browser(),
embedded_test_server()->GetURL("/adopt_node_in_onload_no_crash.html"));
// Run some JavaScript in embedder and make sure it is not crashed.
ASSERT_TRUE(content::ExecJs(GetEmbedderWebContents(), "true"));
}
// Verifies that sandboxed frames do not create GuestViews (plugins are
// blocked in sandboxed frames).
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, DoNotLoadInSandboxedFrame) {
// Use the testing subclass of MimeHandlerViewGuest.
GetGuestViewManager()->RegisterTestGuestViewType<MimeHandlerViewGuest>(
base::BindRepeating(&TestMimeHandlerViewGuest::Create));
const extensions::Extension* extension = LoadTestExtension();
ASSERT_TRUE(extension);
ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL("/test_sandboxed_frame.html"));
auto* guest_view_manager = GetGuestViewManager();
// The page contains three <iframes> where two are sandboxed. The expectation
// is that the sandboxed frames do not end up creating a MimeHandlerView.
// Therefore, it suffices to wait for one GuestView to be created, then remove
// the non-sandboxed frame, and ensue there are no GuestViews left.
if (guest_view_manager->num_guests_created() == 0)
ASSERT_TRUE(guest_view_manager->WaitForNextGuestCreated());
ASSERT_EQ(1U, guest_view_manager->num_guests_created());
// Remove the non-sandboxed frame.
content::RenderFrameHost* main_rfh = GetEmbedderWebContents()->GetMainFrame();
ASSERT_TRUE(content::ExecJs(main_rfh, "remove_frame('notsandboxed');"));
// The page is expected to embed only '1' GuestView. If there is GuestViews
// embedded inside other frames we should be timing out here.
guest_view_manager->WaitForAllGuestsDeleted();
// Since 'sandbox1' has no fallback content, we would render an error page in
// the iframe. Note that we can't access the contentDocument because error
// pages have opaque origins (so it's using a different origin than the main
// frame).
EXPECT_EQ(false, content::EvalJs(main_rfh, "!!(sandbox1.contentDocument)"));
// The error page will not be blank.
EXPECT_EQ(true,
content::EvalJs(ChildFrameAt(main_rfh, 0),
"!!(document.body && document.body.firstChild)"));
// The document inside 'sandbox2' contains an <object> with fallback content.
// The expectation is that the <object> fails to load the MimeHandlerView and
// should show the fallback content instead.
EXPECT_EQ(true, content::EvalJs(main_rfh, "!!(sandbox2.contentDocument)"));
EXPECT_EQ(
"Fallback",
content::EvalJs(
main_rfh,
"sandbox2.contentDocument.getElementById('fallback').innerText"));
}
// Tests that a MimeHandlerViewGuest auto-rejects pointer lock requests.
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, RejectPointLock) {
GetGuestViewManager()->RegisterTestGuestViewType<MimeHandlerViewGuest>(
base::BindRepeating(&TestMimeHandlerViewGuest::Create));
auto* extension = LoadTestExtension();
ASSERT_TRUE(extension);
ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL("/test_embedded.html"));
auto* guest_contents = GetGuestViewManager()->WaitForSingleGuestCreated();
// Make sure the load has started, before waiting for it to stop.
// This is a little hacky, but will unjank the test for now.
while (!guest_contents->IsLoading() &&
!guest_contents->GetController().GetLastCommittedEntry()) {
base::RunLoop run_loop;
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, run_loop.QuitClosure(), TestTimeouts::tiny_timeout());
run_loop.Run();
}
EXPECT_TRUE(WaitForLoadStop(guest_contents));
content::RenderFrameHost* guest_rfh = guest_contents->GetMainFrame();
EXPECT_EQ(false, content::EvalJs(guest_rfh, R"code(
var promise = new Promise((resolve, reject) => {
document.addEventListener('pointerlockchange', () => resolve(true));
document.addEventListener('pointerlockerror', () => resolve(false));
});
document.body.requestPointerLock();
(async ()=> { return await promise; })();
)code",
content::EXECUTE_SCRIPT_DEFAULT_OPTIONS,
1 /* world_id */));
}