blob: feb89425187c8742f95b17fe90b9eab2256aa049 [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/test/scoped_feature_list.h"
#include "base/test/test_timeouts.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/app_modal/javascript_app_modal_dialog.h"
#include "components/guest_view/browser/test_guest_view_manager.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_renderer_host.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_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/test/result_catcher.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/http_request.h"
#include "services/network/public/cpp/features.h"
#include "url/url_constants.h"
using extensions::ExtensionsAPIClient;
using extensions::MimeHandlerViewGuest;
using extensions::TestMimeHandlerViewGuest;
using guest_view::GuestViewManager;
using guest_view::GuestViewManagerDelegate;
using guest_view::TestGuestViewManager;
using guest_view::TestGuestViewManagerFactory;
// The test extension id is set by the key value in the manifest.
const char kExtensionId[] = "oickdpebdnfbgkcaoklfcdhjniefkcji";
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)));
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() {
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;
}
const extensions::Extension* LoadTestExtension() {
const extensions::Extension* extension =
LoadExtension(test_data_dir_.AppendASCII("mime_handler_view"));
if (!extension)
return nullptr;
CHECK_EQ(std::string(kExtensionId), extension->id());
return extension;
}
void RunTestWithUrl(const GURL& url) {
// Use the testing subclass of MimeHandlerViewGuest.
GetGuestViewManager()->RegisterTestGuestViewType<MimeHandlerViewGuest>(
base::Bind(&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;
};
// The parametric version of the test class which runs the test both on
// BrowserPlugin-based and cross-process-frame-based MimeHandlerView
// implementation. All current browser tests should eventually be moved to this
// and then eventually drop the BrowserPlugin dependency once
// https://crbug.com/659750 is fixed.
class MimeHandlerViewCrossProcessTest
: public MimeHandlerViewTest,
public ::testing::WithParamInterface<bool> {
public:
MimeHandlerViewCrossProcessTest() : MimeHandlerViewTest() {}
~MimeHandlerViewCrossProcessTest() override {}
void SetUpCommandLine(base::CommandLine* cl) override {
MimeHandlerViewTest::SetUpCommandLine(cl);
if (GetParam()) {
scoped_feature_list_.InitAndEnableFeature(
features::kMimeHandlerViewInCrossProcessFrame);
}
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
DISALLOW_COPY_AND_ASSIGN(MimeHandlerViewCrossProcessTest);
};
INSTANTIATE_TEST_CASE_P(/* no prefix */,
MimeHandlerViewCrossProcessTest,
::testing::Bool());
IN_PROC_BROWSER_TEST_P(MimeHandlerViewCrossProcessTest, 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());
}
// 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");
}
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, Iframe) {
RunTest("test_iframe.html");
}
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, Abort) {
if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
// With the network service, abortStream isn't needed since we pass a Mojo
// pipe to the renderer. If the plugin chooses to cancel the main request
// (e.g. to make range requests instead), we are always guaranteed that the
// Mojo pipe will be broken which will cancel the request. This is different
// than without the network service, since stream URLs need to be explicitly
// closed if they weren't yet opened to avoid leaks.
// TODO(jam): once the network service is the only path, delete the
// abortStream mimeHandlerPrivate method and supporting code.
return;
}
RunTest("testAbort.csv");
}
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");
}
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, ResizeBeforeAttach) {
// Delay the creation of the guest's WebContents in order to delay the guest's
// attachment to the embedder. This will allow us to resize the <object> tag
// after the guest is created, but before it is attached in
// "test_resize_before_attach.html".
TestMimeHandlerViewGuest::DelayNextCreateWebContents(500);
RunTest("test_resize_before_attach.html");
// Wait for the guest to attach.
content::WebContents* guest_web_contents =
GetGuestViewManager()->WaitForSingleGuestCreated();
TestMimeHandlerViewGuest* guest = static_cast<TestMimeHandlerViewGuest*>(
MimeHandlerViewGuest::FromWebContents(guest_web_contents));
guest->WaitForGuestAttached();
// Ensure that the guest has the correct size after it has attached.
auto guest_size = guest->size();
CHECK_EQ(guest_size.width(), 500);
CHECK_EQ(guest_size.height(), 400);
}
// 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());
content::WaitForLoadStop(browser()->tab_strip_model()->GetWebContentsAt(1));
EXPECT_EQ(
GURL("about:blank"),
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 = browser()->tab_strip_model()->GetWebContentsAt(0);
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("about:blank"));
}
IN_PROC_BROWSER_TEST_F(MimeHandlerViewTest, BeforeUnload_ShowDialog) {
ASSERT_NO_FATAL_FAILURE(RunTest("testBeforeUnloadShowDialog.csv"));
auto* web_contents = browser()->tab_strip_model()->GetWebContentsAt(0);
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("about:blank"), {},
ui::PAGE_TRANSITION_TYPED, "");
app_modal::JavaScriptAppModalDialog* 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(base::string16(), false);
}
// TODO(mcnee): These tests are BrowserPlugin specific. Once
// MimeHandlerViewGuest is no longer based on BrowserPlugin, remove these tests.
// (See https://crbug.com/533069 and https://crbug.com/659750). These category
// of tests are solely testing BrowserPlugin features.
class MimeHandlerViewBrowserPluginSpecificTest : public MimeHandlerViewTest {
public:
MimeHandlerViewBrowserPluginSpecificTest() {}
~MimeHandlerViewBrowserPluginSpecificTest() override {}
protected:
// None of these test create new tabs, so the embedder should be the first
// tab.
content::WebContents* GetEmbedderWebContents() {
return browser()->tab_strip_model()->GetWebContentsAt(0);
}
DISALLOW_COPY_AND_ASSIGN(MimeHandlerViewBrowserPluginSpecificTest);
};
// This test verifies that when BrowserPlugin-based guest has touch handlers,
// the embedder knows about it.
IN_PROC_BROWSER_TEST_F(MimeHandlerViewBrowserPluginSpecificTest,
AcceptTouchEvents) {
RunTest("testBasic.csv");
content::RenderViewHost* embedder_rvh =
GetEmbedderWebContents()->GetRenderViewHost();
bool embedder_has_touch_handler =
content::RenderViewHostTester::HasTouchEventHandler(embedder_rvh);
EXPECT_FALSE(embedder_has_touch_handler);
auto* guest_web_contents = GetGuestViewManager()->WaitForSingleGuestCreated();
ASSERT_TRUE(ExecuteScript(
guest_web_contents,
"document.addEventListener('touchstart', dummyTouchStartHandler);"));
// Wait until embedder has touch handlers.
while (!content::RenderViewHostTester::HasTouchEventHandler(embedder_rvh)) {
base::RunLoop run_loop;
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, run_loop.QuitClosure(), TestTimeouts::tiny_timeout());
run_loop.Run();
}
ASSERT_TRUE(ExecuteScript(
guest_web_contents,
"document.removeEventListener('touchstart', dummyTouchStartHandler);"));
// Wait until embedder not longer has any touch handlers.
while (content::RenderViewHostTester::HasTouchEventHandler(embedder_rvh)) {
base::RunLoop run_loop;
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, run_loop.QuitClosure(), TestTimeouts::tiny_timeout());
run_loop.Run();
}
}
// Verify that a BrowserPlugin captures mouse input on MouseDown.
IN_PROC_BROWSER_TEST_F(MimeHandlerViewBrowserPluginSpecificTest,
MouseCaptureOnMouseDown) {
RunTest("testBasic.csv");
auto* guest_web_contents = GetGuestViewManager()->WaitForSingleGuestCreated();
auto* guest_widget = MimeHandlerViewGuest::FromWebContents(guest_web_contents)
->GetOwnerRenderWidgetHost();
auto* embedder_web_contents = GetEmbedderWebContents();
SendMouseDownToWidget(guest_widget, 0, blink::WebMouseEvent::Button::kLeft);
while (!GetMouseCaptureWidget(embedder_web_contents)) {
base::RunLoop run_loop;
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, run_loop.QuitClosure(), TestTimeouts::tiny_timeout());
run_loop.Run();
}
EXPECT_EQ(GetMouseCaptureWidget(embedder_web_contents), guest_widget);
}