blob: 99e241281e72522364e029eed51f9cab391e8c3c [file] [log] [blame]
// Copyright 2018 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/bind.h"
#include "base/containers/circular_deque.h"
#include "base/guid.h"
#include "base/memory/scoped_refptr.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/histogram_tester.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/metrics/subprocess_metrics_provider.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/find_bar/find_tab_helper.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/pdf_util.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/guest_view/browser/guest_view_manager.h"
#include "components/guest_view/browser/guest_view_manager_delegate.h"
#include "components/guest_view/browser/test_guest_view_manager.h"
#include "content/public/browser/devtools_agent_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/web_contents.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/find_test_utils.h"
#include "content/public/test/hit_test_region_observer.h"
#include "content/public/test/test_navigation_observer.h"
#include "content/public/test/test_renderer_host.h"
#include "extensions/browser/api/extensions_api_client.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_constants.h"
#include "extensions/browser/guest_view/mime_handler_view/test_mime_handler_view_guest.h"
#include "extensions/common/constants.h"
#include "extensions/common/guest_view/mime_handler_view_uma_types.h"
#include "extensions/test/extension_test_message_listener.h"
#include "extensions/test/result_catcher.h"
#include "net/dns/mock_host_resolver.h"
#include "net/http/http_response_headers.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/controls/webview/webview.h"
#include "ui/views/view.h"
#include "ui/views/widget/widget.h"
#include "url/url_constants.h"
#if defined(USE_AURA)
#include "ui/aura/window.h"
#endif
using extensions::ExtensionsAPIClient;
using extensions::MimeHandlerViewGuest;
using extensions::TestMimeHandlerViewGuest;
using guest_view::GuestViewManager;
using guest_view::TestGuestViewManager;
using guest_view::TestGuestViewManagerFactory;
using UMAType = extensions::MimeHandlerViewUMATypes::Type;
namespace {
constexpr char kTestExtensionId[] = "oickdpebdnfbgkcaoklfcdhjniefkcji";
}
// Note: This file contains several old WebViewGuest tests which were for
// certain BrowserPlugin features and no longer made sense for the new
// WebViewGuest which is based on cross-process frames. Since
// MimeHandlerViewGuest is the only guest which still uses BrowserPlugin, the
// test were moved, with adaptation, to this file. Eventually this file might
// contain new tests for MimeHandlerViewGuest but ideally they should all be
// tests which are a) based on cross-process frame version of MHVG, and b) tests
// that need chrome layer API. Anything else should go to the extension layer
// version of the tests. Most of the legacy tests will probably be removed when
// MimeHandlerViewGuest starts using cross-process frames (see
// https://crbug.com/659750).
// Base class for tests below.
class ChromeMimeHandlerViewTestBase : public extensions::ExtensionApiTest {
public:
ChromeMimeHandlerViewTestBase() {
GuestViewManager::set_factory_for_testing(&factory_);
}
~ChromeMimeHandlerViewTestBase() override {}
void SetUpOnMainThread() override {
extensions::ExtensionApiTest::SetUpOnMainThread();
host_resolver()->AddRule("*", "127.0.0.1");
embedded_test_server()->ServeFilesFromDirectory(
test_data_dir_.AppendASCII("mime_handler_view"));
ASSERT_TRUE(StartEmbeddedTestServer());
}
protected:
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;
}
void InitializeTestPage(const GURL& url) {
// Use the testing subclass of MimeHandlerViewGuest.
GetGuestViewManager()->RegisterTestGuestViewType<MimeHandlerViewGuest>(
base::BindRepeating(&TestMimeHandlerViewGuest::Create));
const extensions::Extension* extension =
LoadExtension(test_data_dir_.AppendASCII("mime_handler_view"));
ASSERT_TRUE(extension);
CHECK_EQ(kTestExtensionId, extension->id());
extensions::ResultCatcher catcher;
ui_test_utils::NavigateToURL(browser(), url);
if (!catcher.GetNextResult())
FAIL() << catcher.message();
guest_web_contents_ = GetGuestViewManager()->WaitForSingleGuestCreated();
embedder_web_contents_ = browser()->tab_strip_model()->GetWebContentsAt(0);
ASSERT_TRUE(guest_web_contents_);
ASSERT_TRUE(embedder_web_contents_);
}
content::WebContents* guest_web_contents() const {
return guest_web_contents_;
}
content::WebContents* embedder_web_contents() const {
return embedder_web_contents_;
}
// Creates a bogus StreamContainer for the first tab. This is not intended to
// be really consumed by MimeHandler API.
std::unique_ptr<extensions::StreamContainer> CreateFakeStreamContainer(
const GURL& url,
std::string* view_id) {
*view_id = base::GenerateGUID();
auto transferrable_loader = content::mojom::TransferrableURLLoader::New();
transferrable_loader->url = url;
transferrable_loader->head = network::mojom::URLResponseHead::New();
transferrable_loader->head->mime_type = "application/pdf";
transferrable_loader->head->headers =
base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/2 200 OK");
return std::make_unique<extensions::StreamContainer>(
0 /* tab_id */, false /* embedded */,
GURL(std::string(extensions::kExtensionScheme) +
kTestExtensionId) /* handler_url */,
kTestExtensionId, std::move(transferrable_loader), url);
}
private:
TestGuestViewManagerFactory factory_;
content::WebContents* guest_web_contents_;
content::WebContents* embedder_web_contents_;
ChromeMimeHandlerViewTestBase(const ChromeMimeHandlerViewTestBase&) = delete;
ChromeMimeHandlerViewTestBase& operator=(
const ChromeMimeHandlerViewTestBase&) = delete;
};
// 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 ChromeMimeHandlerViewCrossProcessTest
: public ChromeMimeHandlerViewTestBase,
public ::testing::WithParamInterface<bool> {
public:
ChromeMimeHandlerViewCrossProcessTest() : ChromeMimeHandlerViewTestBase() {}
~ChromeMimeHandlerViewCrossProcessTest() override {}
void SetUpCommandLine(base::CommandLine* cl) override {
ChromeMimeHandlerViewTestBase::SetUpCommandLine(cl);
is_cross_process_mode_ = GetParam();
if (is_cross_process_mode_) {
scoped_feature_list_.InitAndEnableFeature(
features::kMimeHandlerViewInCrossProcessFrame);
} else {
scoped_feature_list_.InitAndDisableFeature(
features::kMimeHandlerViewInCrossProcessFrame);
}
}
bool is_cross_process_mode() const { return is_cross_process_mode_; }
private:
bool is_cross_process_mode_ = false;
base::test::ScopedFeatureList scoped_feature_list_;
DISALLOW_COPY_AND_ASSIGN(ChromeMimeHandlerViewCrossProcessTest);
};
// A class of tests which were originally designed as WebViewGuest tests which
// were testing some aspects of BrowserPlugin. Since all GuestViews except for
// MimeHandlerViewGuest have now moved on to using cross-process frames these
// tests were modified to using MimeHandlerViewGuest instead. They also could
// not be moved to extensions/browser/guest_view/mime_handler_view due to chrome
// layer dependencies.
class ChromeMimeHandlerViewBrowserPluginTest
: public ChromeMimeHandlerViewTestBase {
public:
void SetUpCommandLine(base::CommandLine* cl) override {
ChromeMimeHandlerViewTestBase::SetUpCommandLine(cl);
scoped_feature_list_.InitAndDisableFeature(
features::kMimeHandlerViewInCrossProcessFrame);
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
// Helper class to monitor focus on a WebContents with BrowserPlugin (guest).
class FocusChangeWaiter {
public:
explicit FocusChangeWaiter(content::WebContents* web_contents,
bool expected_focus)
: web_contents_(web_contents), expected_focus_(expected_focus) {}
~FocusChangeWaiter() {}
void WaitForFocusChange() {
while (expected_focus_ !=
IsWebContentsBrowserPluginFocused(web_contents_)) {
base::RunLoop().RunUntilIdle();
}
}
private:
content::WebContents* web_contents_;
bool expected_focus_;
};
// This test creates two guest views in a tab: a normal attached
// MimeHandlerViewGuest, and then another MHVG which is unattached. Right after
// the second GuestView's WebContents is created a find request is send to the
// tab's WebContents. The test then verifies that the set of outstanding
// RenderFrameHosts with find request in flight includes all frames but the one
// from the unattached guest. For more context see https://crbug.com/897465.
IN_PROC_BROWSER_TEST_F(ChromeMimeHandlerViewBrowserPluginTest,
NoFindInPageForUnattachedGuest) {
InitializeTestPage(embedded_test_server()->GetURL("/testBasic.csv"));
auto* main_frame = embedder_web_contents()->GetMainFrame();
auto* attached_guest_main_frame = guest_web_contents()->GetMainFrame();
std::string view_id;
auto stream_container = CreateFakeStreamContainer(GURL("foo.com"), &view_id);
extensions::MimeHandlerStreamManager::Get(
embedder_web_contents()->GetBrowserContext())
->AddStream(view_id, std::move(stream_container),
main_frame->GetFrameTreeNodeId(),
main_frame->GetProcess()->GetID(),
main_frame->GetRoutingID());
base::DictionaryValue create_params;
create_params.SetString(mime_handler_view::kViewId, view_id);
// The actual test logic is inside the callback.
GuestViewManager::WebContentsCreatedCallback callback = base::BindOnce(
[](content::WebContents* embedder_contents,
content::RenderFrameHost* attached_guest_rfh,
content::WebContents* guest_contents) {
auto* guest_main_frame = guest_contents->GetMainFrame();
auto* find_helper = FindTabHelper::FromWebContents(embedder_contents);
find_helper->StartFinding(base::ASCIIToUTF16("doesn't matter"), true,
true, false);
auto pending = content::GetRenderFrameHostsWithPendingFindResults(
embedder_contents);
// Request for main frame of the tab.
EXPECT_EQ(1U, pending.count(embedder_contents->GetMainFrame()));
// Request for main frame of the attached guest.
EXPECT_EQ(1U, pending.count(attached_guest_rfh));
// No request for the unattached guest.
EXPECT_EQ(0U, pending.count(guest_main_frame));
// Sanity-check: try the set returned for guest.
pending =
content::GetRenderFrameHostsWithPendingFindResults(guest_contents);
EXPECT_TRUE(pending.empty());
},
embedder_web_contents(), attached_guest_main_frame);
GetGuestViewManager()->CreateGuest(MimeHandlerViewGuest::Type,
embedder_web_contents(), create_params,
std::move(callback));
}
IN_PROC_BROWSER_TEST_F(ChromeMimeHandlerViewBrowserPluginTest,
UnderChildFrame) {
// Create this frame tree structure.
// main_frame_node
// |
// middle_node -> is child of |main_frame_node|
// |
// mime_node -> is inner web contents of |middle_node|
InitializeTestPage(
embedded_test_server()->GetURL("/find_in_page_one_frame.html"));
int ordinal;
EXPECT_EQ(2, ui_test_utils::FindInPage(embedder_web_contents(),
base::ASCIIToUTF16("two"), true, true,
&ordinal, nullptr));
EXPECT_EQ(1, ordinal);
// Go to next result.
EXPECT_EQ(2, ui_test_utils::FindInPage(embedder_web_contents(),
base::ASCIIToUTF16("two"), true, true,
&ordinal, nullptr));
EXPECT_EQ(2, ordinal);
// Go to next result, should wrap back to #1.
EXPECT_EQ(2, ui_test_utils::FindInPage(embedder_web_contents(),
base::ASCIIToUTF16("two"), true, true,
&ordinal, nullptr));
EXPECT_EQ(1, ordinal);
}
// Flaky under MSan: https://crbug.com/837757
#if defined(MEMORY_SANITIZER)
#define MAYBE_BP_AutoResizeMessages DISABLED_AutoResizeMessages
#else
#define MAYBE_BP_AutoResizeMessages AutoResizeMessages
#endif
IN_PROC_BROWSER_TEST_F(ChromeMimeHandlerViewBrowserPluginTest,
MAYBE_BP_AutoResizeMessages) {
InitializeTestPage(embedded_test_server()->GetURL("/testBasic.csv"));
// Helper function as this test requires inspecting a number of content::
// internal objects.
EXPECT_TRUE(content::TestChildOrGuestAutoresize(
true,
embedder_web_contents()
->GetRenderWidgetHostView()
->GetRenderWidgetHost()
->GetProcess(),
guest_web_contents()->GetRenderWidgetHostView()->GetRenderWidgetHost()));
}
#if defined(USE_AURA)
// Flaky on Linux. See: https://crbug.com/870604.
#if defined(OS_LINUX)
#define MAYBE_TouchFocusesEmbedder DISABLED_TouchFocusesEmbedder
#else
#define MAYBE_TouchFocusesEmbedder TouchFocusesEmbedder
#endif
IN_PROC_BROWSER_TEST_F(ChromeMimeHandlerViewBrowserPluginTest,
MAYBE_TouchFocusesEmbedder) {
InitializeTestPage(embedded_test_server()->GetURL("/testBasic.csv"));
content::RenderViewHost* embedder_rvh =
embedder_web_contents()->GetRenderViewHost();
content::RenderFrameSubmissionObserver frame_observer(
embedder_web_contents());
bool embedder_has_touch_handler =
content::RenderViewHostTester::HasTouchEventHandler(embedder_rvh);
ASSERT_FALSE(embedder_has_touch_handler);
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();
}
auto* top_level_window =
embedder_web_contents()->GetNativeView()->GetToplevelWindow();
ASSERT_TRUE(top_level_window);
auto* widget = views::Widget::GetWidgetForNativeWindow(top_level_window);
ASSERT_TRUE(widget);
ASSERT_TRUE(widget->GetRootView());
// Find WebView corresponding to embedder_web_contents().
const std::string kWebViewClassName = views::WebView::kViewClassName;
views::View* aura_webview = nullptr;
for (base::circular_deque<views::View*> deque = {widget->GetRootView()};
!deque.empty(); deque.pop_front()) {
views::View* current = deque.front();
if (current->GetClassName() == kWebViewClassName &&
static_cast<views::WebView*>(current)->GetWebContents() ==
embedder_web_contents()) {
aura_webview = current;
break;
}
const auto& children = current->children();
deque.insert(deque.end(), children.cbegin(), children.cend());
}
ASSERT_TRUE(aura_webview);
gfx::Rect bounds(aura_webview->bounds());
EXPECT_TRUE(aura_webview->IsFocusable());
views::View* other_focusable_view = new views::View();
other_focusable_view->SetBounds(bounds.x() + bounds.width(), bounds.y(), 100,
100);
other_focusable_view->SetFocusBehavior(views::View::FocusBehavior::ALWAYS);
// Focusable views require an accessible name to pass accessibility checks.
other_focusable_view->GetViewAccessibility().OverrideName("Any name");
aura_webview->parent()->AddChildView(other_focusable_view);
other_focusable_view->SetPosition(gfx::Point(bounds.x() + bounds.width(), 0));
// Sync changes to compositor.
while (!RequestFrame(embedder_web_contents())) {
// RequestFrame failed because we were waiting on an ack ... wait a short
// time and retry.
base::RunLoop run_loop;
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, run_loop.QuitClosure(),
base::TimeDelta::FromMilliseconds(10));
run_loop.Run();
}
frame_observer.WaitForAnyFrameSubmission();
aura_webview->RequestFocus();
// Verify that other_focusable_view can steal focus from aura_webview.
EXPECT_TRUE(aura_webview->HasFocus());
other_focusable_view->RequestFocus();
EXPECT_TRUE(other_focusable_view->HasFocus());
EXPECT_FALSE(aura_webview->HasFocus());
// Compute location of guest within embedder so we can more accurately
// target our touch event.
gfx::Rect guest_rect = guest_web_contents()->GetContainerBounds();
gfx::Point embedder_origin =
embedder_web_contents()->GetContainerBounds().origin();
guest_rect.Offset(-embedder_origin.x(), -embedder_origin.y());
// Generate and send synthetic touch event.
content::InputEventAckWaiter waiter(
guest_web_contents()->GetRenderWidgetHostView()->GetRenderWidgetHost(),
blink::WebInputEvent::kTouchStart);
content::SimulateTouchPressAt(embedder_web_contents(),
guest_rect.CenterPoint());
waiter.Wait();
EXPECT_TRUE(aura_webview->HasFocus());
}
IN_PROC_BROWSER_TEST_F(ChromeMimeHandlerViewBrowserPluginTest,
TouchFocusesBrowserPluginInEmbedder) {
InitializeTestPage(embedded_test_server()->GetURL("/test_embedded.html"));
auto embedder_rect = embedder_web_contents()->GetContainerBounds();
auto guest_rect = guest_web_contents()->GetContainerBounds();
guest_rect.set_x(guest_rect.x() - embedder_rect.x());
guest_rect.set_y(guest_rect.y() - embedder_rect.y());
embedder_rect.set_x(0);
embedder_rect.set_y(0);
// Don't send events that need to be routed until we know the child's surface
// is ready for hit testing.
content::WaitForHitTestData(guest_web_contents());
// 1) BrowserPlugin should not be focused at start.
ASSERT_FALSE(IsWebContentsBrowserPluginFocused(guest_web_contents()));
// 2) Send touch event to guest, now BrowserPlugin should get focus.
{
gfx::Point point = guest_rect.CenterPoint();
FocusChangeWaiter focus_waiter(guest_web_contents(), true);
SendRoutedTouchTapSequence(embedder_web_contents(), point);
SendRoutedGestureTapSequence(embedder_web_contents(), point);
focus_waiter.WaitForFocusChange();
ASSERT_TRUE(IsWebContentsBrowserPluginFocused(guest_web_contents()));
}
// 3) Send touch start to embedder, now BrowserPlugin should lose focus.
{
// Choose a point outside of guest (but inside the embedder).
gfx::Point point = guest_rect.bottom_right();
point += gfx::Vector2d(10, 10);
EXPECT_TRUE(embedder_rect.Contains(point));
FocusChangeWaiter focus_waiter(guest_web_contents(), false);
SendRoutedTouchTapSequence(embedder_web_contents(), point);
SendRoutedGestureTapSequence(embedder_web_contents(), point);
focus_waiter.WaitForFocusChange();
ASSERT_FALSE(IsWebContentsBrowserPluginFocused(guest_web_contents()));
}
}
#endif // USE_AURA
class ChromeMimeHandlerViewBrowserPluginScrollTest
: public ChromeMimeHandlerViewBrowserPluginTest {
public:
void SetUpCommandLine(base::CommandLine* command_line) override {
ChromeMimeHandlerViewBrowserPluginTest::SetUpCommandLine(command_line);
command_line->AppendSwitchASCII(
switches::kTouchEventFeatureDetection,
switches::kTouchEventFeatureDetectionEnabled);
}
};
#if defined(OS_WIN) || defined(OS_LINUX) || defined(OS_MACOSX)
#define MAYBE_ScrollGuestContent DISABLED_ScrollGuestContent
#else
#define MAYBE_ScrollGuestContent ScrollGuestContent
#endif
IN_PROC_BROWSER_TEST_F(ChromeMimeHandlerViewBrowserPluginScrollTest,
MAYBE_ScrollGuestContent) {
InitializeTestPage(embedded_test_server()->GetURL("/test_embedded.html"));
ASSERT_TRUE(ExecuteScript(guest_web_contents(), "ensurePageIsScrollable();"));
content::RenderFrameSubmissionObserver embedder_frame_observer(
embedder_web_contents());
content::RenderFrameSubmissionObserver guest_frame_observer(
guest_web_contents());
gfx::Rect embedder_rect = embedder_web_contents()->GetContainerBounds();
gfx::Rect guest_rect = guest_web_contents()->GetContainerBounds();
guest_rect.set_x(guest_rect.x() - embedder_rect.x());
guest_rect.set_y(guest_rect.y() - embedder_rect.y());
gfx::Vector2dF default_offset;
guest_frame_observer.WaitForScrollOffset(default_offset);
embedder_frame_observer.WaitForScrollOffset(default_offset);
gfx::Point guest_scroll_location(guest_rect.width() / 2,
guest_rect.height() / 2);
float gesture_distance = 15.f;
{
gfx::Vector2dF expected_offset(0.f, gesture_distance);
content::SimulateGestureScrollSequence(
guest_web_contents(), guest_scroll_location,
gfx::Vector2dF(0, -gesture_distance));
guest_frame_observer.WaitForScrollOffset(expected_offset);
}
embedder_frame_observer.WaitForScrollOffset(default_offset);
// Use fling gesture to scroll back, velocity should be big enough to scroll
// content back.
float fling_velocity = 300.f;
{
content::SimulateGestureFlingSequence(guest_web_contents(),
guest_scroll_location,
gfx::Vector2dF(0, fling_velocity));
guest_frame_observer.WaitForScrollOffset(default_offset);
}
embedder_frame_observer.WaitForScrollOffset(default_offset);
}
IN_PROC_BROWSER_TEST_P(ChromeMimeHandlerViewCrossProcessTest,
UMA_SameOriginResource) {
auto url = embedded_test_server()->GetURL("a.com", "/testPostMessageUMA.csv");
auto page_url = embedded_test_server()->GetURL(
"a.com",
base::StringPrintf("/test_postmessage_uma.html?%s", url.spec().c_str()));
InitializeTestPage(page_url);
EXPECT_TRUE(ExecJs(embedder_web_contents(), "sendMessages();"));
const std::vector<std::pair<extensions::MimeHandlerViewUMATypes::Type, int>>
kTestCases = {
{(GetParam() ? UMAType::kCreateFrameContainer
: UMAType::kDidCreateMimeHandlerViewContainerBase),
1},
{UMAType::kDidLoadExtension, 1},
{UMAType::kAccessibleInvalid, 1},
{UMAType::kAccessibleSelectAll, 1},
{UMAType::kAccessibleGetSelectedText, 1},
{UMAType::kAccessiblePrint, 2},
{UMAType::kPostMessageToEmbeddedMimeHandlerView, 5}};
base::HistogramTester histogram_tester;
SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
for (const auto& pair : kTestCases) {
histogram_tester.ExpectBucketCount(
extensions::MimeHandlerViewUMATypes::kUMAName, pair.first, pair.second);
}
}
IN_PROC_BROWSER_TEST_P(ChromeMimeHandlerViewCrossProcessTest,
UMA_CrossOriginResource) {
auto url = embedded_test_server()->GetURL("b.com", "/testPostMessageUMA.csv");
auto page_url = embedded_test_server()->GetURL(
"a.com",
base::StringPrintf("/test_postmessage_uma.html?%s", url.spec().c_str()));
InitializeTestPage(page_url);
EXPECT_TRUE(ExecJs(embedder_web_contents(), "sendMessages();"));
const std::vector<std::pair<extensions::MimeHandlerViewUMATypes::Type, int>>
kTestCases = {
{(GetParam() ? UMAType::kCreateFrameContainer
: UMAType::kDidCreateMimeHandlerViewContainerBase),
1},
{UMAType::kDidLoadExtension, 1},
{UMAType::kInaccessibleInvalid, 1},
{UMAType::kInaccessibleSelectAll, 1},
{UMAType::kInaccessibleGetSelectedText, 1},
{UMAType::kInaccessiblePrint, 2},
{UMAType::kPostMessageToEmbeddedMimeHandlerView, 5}};
base::HistogramTester histogram_tester;
SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
for (const auto& pair : kTestCases) {
histogram_tester.ExpectBucketCount(
extensions::MimeHandlerViewUMATypes::kUMAName, pair.first, pair.second);
}
}
IN_PROC_BROWSER_TEST_P(ChromeMimeHandlerViewCrossProcessTest,
UMAPDFLoadStatsFullPage) {
base::HistogramTester histogram_tester;
GURL data_url("data:application/pdf,foo");
ui_test_utils::NavigateToURL(browser(), data_url);
auto* guest = GetGuestViewManager()->WaitForSingleGuestCreated();
while (guest->IsLoading()) {
base::RunLoop run_loop;
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, run_loop.QuitClosure(), TestTimeouts::tiny_timeout());
run_loop.Run();
}
SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
histogram_tester.ExpectBucketCount(
"PDF.LoadStatus", PDFLoadStatus::kLoadedFullPagePdfWithPdfium, 1);
}
IN_PROC_BROWSER_TEST_P(ChromeMimeHandlerViewCrossProcessTest,
UMAPDFLoadStatsEmbedded) {
base::HistogramTester histogram_tester;
ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
ASSERT_TRUE(content::ExecJs(
browser()->tab_strip_model()->GetWebContentsAt(0),
"document.write('<iframe></iframe>');"
"document.querySelector('iframe').src = 'data:application/pdf, foo';"));
auto* guest = GetGuestViewManager()->WaitForSingleGuestCreated();
while (guest->IsLoading()) {
base::RunLoop run_loop;
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, run_loop.QuitClosure(), TestTimeouts::tiny_timeout());
run_loop.Run();
}
SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
histogram_tester.ExpectBucketCount(
"PDF.LoadStatus", PDFLoadStatus::kLoadedEmbeddedPdfWithPdfium, 1);
}
namespace {
// A DevToolsAgentHostClient implementation doing nothing.
class StubDevToolsAgentHostClient : public content::DevToolsAgentHostClient {
public:
StubDevToolsAgentHostClient() {}
~StubDevToolsAgentHostClient() override {}
void AgentHostClosed(content::DevToolsAgentHost* agent_host) override {}
void DispatchProtocolMessage(content::DevToolsAgentHost* agent_host,
const std::string& message) override {}
};
} // namespace
IN_PROC_BROWSER_TEST_P(ChromeMimeHandlerViewCrossProcessTest,
GuestDevToolsReloadsEmbedder) {
GURL data_url("data:application/pdf,foo");
ui_test_utils::NavigateToURL(browser(), data_url);
auto* embedder_web_contents =
browser()->tab_strip_model()->GetWebContentsAt(0);
auto* guest_web_contents = GetGuestViewManager()->WaitForSingleGuestCreated();
EXPECT_NE(embedder_web_contents, guest_web_contents);
while (guest_web_contents->IsLoading()) {
base::RunLoop run_loop;
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, run_loop.QuitClosure(), TestTimeouts::tiny_timeout());
run_loop.Run();
}
// Load DevTools.
scoped_refptr<content::DevToolsAgentHost> devtools_agent_host =
content::DevToolsAgentHost::GetOrCreateFor(guest_web_contents);
StubDevToolsAgentHostClient devtools_agent_host_client;
devtools_agent_host->AttachClient(&devtools_agent_host_client);
// Reload via guest's DevTools, embedder should reload.
content::TestNavigationObserver reload_observer(embedder_web_contents);
devtools_agent_host->DispatchProtocolMessage(
&devtools_agent_host_client, R"({"id":1,"method": "Page.reload"})");
reload_observer.Wait();
devtools_agent_host->DetachClient(&devtools_agent_host_client);
}
// This test verifies that a display:none frame loading a MimeHandlerView type
// will end up creating a MimeHandlerview. NOTE: this is an exception to support
// printing in Google docs (see https://crbug.com/978240).
IN_PROC_BROWSER_TEST_P(ChromeMimeHandlerViewCrossProcessTest,
MimeHandlerViewInDisplayNoneFrameForGoogleApps) {
GURL data_url(
"data:text/html, <iframe src='data:application/pdf,foo' "
"style='display:none'></iframe>,foo");
ui_test_utils::NavigateToURL(browser(), data_url);
ASSERT_TRUE(GetGuestViewManager()->WaitForSingleGuestCreated());
}
INSTANTIATE_TEST_SUITE_P(,
ChromeMimeHandlerViewCrossProcessTest,
testing::Bool());