blob: ae580f003b467984d5139e95fe1010a709ba7ff2 [file] [log] [blame]
/*
* Copyright (C) 2012 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "third_party/blink/public/web/web_plugin_container.h"
#include <memory>
#include <string>
#include "build/build_config.h"
#include "cc/layers/layer.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/frame/frame_owner_element_type.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/platform/web_coalesced_input_event.h"
#include "third_party/blink/public/platform/web_mouse_wheel_event.h"
#include "third_party/blink/public/platform/web_pointer_event.h"
#include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
#include "third_party/blink/public/web/web_document.h"
#include "third_party/blink/public/web/web_element.h"
#include "third_party/blink/public/web/web_local_frame_client.h"
#include "third_party/blink/public/web/web_plugin_params.h"
#include "third_party/blink/public/web/web_print_params.h"
#include "third_party/blink/public/web/web_settings.h"
#include "third_party/blink/public/web/web_view.h"
#include "third_party/blink/renderer/core/clipboard/system_clipboard.h"
#include "third_party/blink/renderer/core/dom/element.h"
#include "third_party/blink/renderer/core/events/keyboard_event.h"
#include "third_party/blink/renderer/core/exported/web_plugin_container_impl.h"
#include "third_party/blink/renderer/core/exported/web_view_impl.h"
#include "third_party/blink/renderer/core/frame/event_handler_registry.h"
#include "third_party/blink/renderer/core/frame/frame_test_helpers.h"
#include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
#include "third_party/blink/renderer/core/html/html_element.h"
#include "third_party/blink/renderer/core/layout/layout_object.h"
#include "third_party/blink/renderer/core/page/page.h"
#include "third_party/blink/renderer/core/testing/fake_web_plugin.h"
#include "third_party/blink/renderer/core/testing/scoped_fake_plugin_registry.h"
#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
#include "third_party/blink/renderer/platform/graphics/paint/cull_rect.h"
#include "third_party/blink/renderer/platform/graphics/paint/foreign_layer_display_item.h"
#include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h"
#include "third_party/blink/renderer/platform/graphics/paint/paint_recorder.h"
#include "third_party/blink/renderer/platform/keyboard_codes.h"
#include "third_party/blink/renderer/platform/scheduler/public/thread.h"
#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
#include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
using blink::test::RunPendingTasks;
namespace blink {
class WebPluginContainerTest : public testing::Test {
public:
WebPluginContainerTest() : base_url_("http://www.test.com/") {}
void TearDown() override {
Platform::Current()
->GetURLLoaderMockFactory()
->UnregisterAllURLsAndClearMemoryCache();
}
void CalculateGeometry(WebPluginContainerImpl* plugin_container_impl,
IntRect& window_rect,
IntRect& clip_rect,
IntRect& unobscured_rect) {
plugin_container_impl->CalculateGeometry(window_rect, clip_rect,
unobscured_rect);
}
void RegisterMockedURL(
const std::string& file_name,
const std::string& mime_type = std::string("text/html")) {
url_test_helpers::RegisterMockedURLLoadFromBase(
WebString::FromUTF8(base_url_), test::CoreTestDataPath(),
WebString::FromUTF8(file_name), WebString::FromUTF8(mime_type));
}
void UpdateAllLifecyclePhases(WebViewImpl* web_view) {
web_view->MainFrameWidget()->UpdateAllLifecyclePhases(
WebWidget::LifecycleUpdateReason::kTest);
}
protected:
ScopedFakePluginRegistry fake_plugins_;
std::string base_url_;
};
namespace {
#if defined(OS_MACOSX)
const WebInputEvent::Modifiers kEditingModifier = WebInputEvent::kMetaKey;
#else
const WebInputEvent::Modifiers kEditingModifier = WebInputEvent::kControlKey;
#endif
template <typename T>
class CustomPluginWebFrameClient
: public frame_test_helpers::TestWebFrameClient {
public:
WebPlugin* CreatePlugin(const WebPluginParams& params) override {
return new T(params);
}
};
class TestPluginWebFrameClient;
// Subclass of FakeWebPlugin that has a selection of 'x' as plain text and 'y'
// as markup text.
class TestPlugin : public FakeWebPlugin {
public:
TestPlugin(const WebPluginParams& params,
TestPluginWebFrameClient* test_client)
: FakeWebPlugin(params), test_client_(test_client) {}
bool HasSelection() const override { return true; }
WebString SelectionAsText() const override { return WebString("x"); }
WebString SelectionAsMarkup() const override { return WebString("y"); }
bool SupportsPaginatedPrint() override { return true; }
int PrintBegin(const WebPrintParams& print_params) override { return 1; }
void PrintPage(int page_number, cc::PaintCanvas*) override;
private:
~TestPlugin() override = default;
TestPluginWebFrameClient* const test_client_;
};
// Subclass of FakeWebPlugin used for testing edit commands, so HasSelection()
// and CanEditText() return true by default.
class TestPluginWithEditableText : public FakeWebPlugin {
public:
static TestPluginWithEditableText* FromContainer(WebElement* element) {
WebPlugin* plugin =
ToWebPluginContainerImpl(element->PluginContainer())->Plugin();
return static_cast<TestPluginWithEditableText*>(plugin);
}
explicit TestPluginWithEditableText(const WebPluginParams& params)
: FakeWebPlugin(params), cut_called_(false), paste_called_(false) {}
bool HasSelection() const override { return true; }
bool CanEditText() const override { return true; }
bool ExecuteEditCommand(const WebString& name) override {
return ExecuteEditCommand(name, WebString());
}
bool ExecuteEditCommand(const WebString& name,
const WebString& value) override {
if (name == "Cut") {
cut_called_ = true;
return true;
}
if (name == "Paste" || name == "PasteAndMatchStyle") {
paste_called_ = true;
return true;
}
return false;
}
bool IsCutCalled() const { return cut_called_; }
bool IsPasteCalled() const { return paste_called_; }
void ResetEditCommandState() {
cut_called_ = false;
paste_called_ = false;
}
private:
~TestPluginWithEditableText() override = default;
bool cut_called_;
bool paste_called_;
};
class TestPluginWebFrameClient : public frame_test_helpers::TestWebFrameClient {
WebLocalFrame* CreateChildFrame(WebLocalFrame* parent,
WebTreeScopeType scope,
const WebString& name,
const WebString& fallback_name,
WebSandboxFlags sandbox_flags,
const ParsedFeaturePolicy& container_policy,
const WebFrameOwnerProperties&,
FrameOwnerElementType owner_type) override {
return CreateLocalChild(*parent, scope,
std::make_unique<TestPluginWebFrameClient>());
}
WebPlugin* CreatePlugin(const WebPluginParams& params) override {
if (params.mime_type == "application/x-webkit-test-webplugin" ||
params.mime_type == "application/pdf") {
if (has_editable_text_)
return new TestPluginWithEditableText(params);
return new TestPlugin(params, this);
}
return WebLocalFrameClient::CreatePlugin(params);
}
public:
void OnPrintPage() { printed_page_ = true; }
bool PrintedAtLeastOnePage() const { return printed_page_; }
void SetHasEditableText(bool has_editable_text) {
has_editable_text_ = has_editable_text;
}
private:
bool printed_page_ = false;
bool has_editable_text_ = false;
};
void TestPlugin::PrintPage(int page_number, cc::PaintCanvas* canvas) {
DCHECK(test_client_);
test_client_->OnPrintPage();
}
void EnablePlugins(WebView* web_view, const WebSize& size) {
DCHECK(web_view);
web_view->GetSettings()->SetPluginsEnabled(true);
web_view->MainFrameWidget()->Resize(size);
web_view->MainFrameWidget()->UpdateAllLifecyclePhases(
WebWidget::LifecycleUpdateReason::kTest);
RunPendingTasks();
}
WebPluginContainer* GetWebPluginContainer(WebViewImpl* web_view,
const WebString& id) {
WebElement element =
web_view->MainFrameImpl()->GetDocument().GetElementById(id);
return element.PluginContainer();
}
String ReadClipboard() {
// Run all tasks in a message loop to allow asynchronous clipboard writing
// to happen before reading from it synchronously.
test::RunPendingTasks();
return SystemClipboard::GetInstance().ReadPlainText();
}
void ClearClipboardBuffer() {
SystemClipboard::GetInstance().WritePlainText(String(""));
SystemClipboard::GetInstance().CommitWrite();
EXPECT_EQ(String(""), ReadClipboard());
}
void CreateAndHandleKeyboardEvent(WebElement* plugin_container_one_element,
WebInputEvent::Modifiers modifier_key,
int key_code) {
WebKeyboardEvent web_keyboard_event(
WebInputEvent::kRawKeyDown, modifier_key,
WebInputEvent::GetStaticTimeStampForTests());
web_keyboard_event.windows_key_code = key_code;
KeyboardEvent* key_event = KeyboardEvent::Create(web_keyboard_event, nullptr);
ToWebPluginContainerImpl(plugin_container_one_element->PluginContainer())
->HandleEvent(*key_event);
}
void ExecuteContextMenuCommand(WebViewImpl* web_view,
const WebString& command_name) {
auto event = frame_test_helpers::CreateMouseEvent(
WebMouseEvent::kMouseDown, WebMouseEvent::Button::kRight,
WebPoint(30, 30), 0);
event.click_count = 1;
web_view->MainFrameWidget()->HandleInputEvent(WebCoalescedInputEvent(event));
EXPECT_TRUE(
web_view->MainFrame()->ToWebLocalFrame()->ExecuteCommand(command_name));
}
} // namespace
TEST_F(WebPluginContainerTest, WindowToLocalPointTest) {
RegisterMockedURL("plugin_container.html");
// Must outlive |web_view_helper|.
TestPluginWebFrameClient plugin_web_frame_client;
frame_test_helpers::WebViewHelper web_view_helper;
WebViewImpl* web_view = web_view_helper.InitializeAndLoad(
base_url_ + "plugin_container.html", &plugin_web_frame_client);
EnablePlugins(web_view, WebSize(300, 300));
WebPluginContainer* plugin_container_one =
GetWebPluginContainer(web_view, WebString::FromUTF8("translated-plugin"));
DCHECK(plugin_container_one);
WebPoint point1 =
plugin_container_one->RootFrameToLocalPoint(WebPoint(10, 10));
ASSERT_EQ(0, point1.x);
ASSERT_EQ(0, point1.y);
WebPoint point2 =
plugin_container_one->RootFrameToLocalPoint(WebPoint(100, 100));
ASSERT_EQ(90, point2.x);
ASSERT_EQ(90, point2.y);
WebPluginContainer* plugin_container_two =
GetWebPluginContainer(web_view, WebString::FromUTF8("rotated-plugin"));
DCHECK(plugin_container_two);
WebPoint point3 =
plugin_container_two->RootFrameToLocalPoint(WebPoint(0, 10));
ASSERT_EQ(10, point3.x);
ASSERT_EQ(0, point3.y);
WebPoint point4 =
plugin_container_two->RootFrameToLocalPoint(WebPoint(-10, 10));
ASSERT_EQ(10, point4.x);
ASSERT_EQ(10, point4.y);
}
TEST_F(WebPluginContainerTest, PluginDocumentPluginIsFocused) {
RegisterMockedURL("test.pdf", "application/pdf");
// Must outlive |web_view_helper|.
TestPluginWebFrameClient plugin_web_frame_client;
frame_test_helpers::WebViewHelper web_view_helper;
WebViewImpl* web_view = web_view_helper.InitializeAndLoad(
base_url_ + "test.pdf", &plugin_web_frame_client);
DCHECK(web_view);
UpdateAllLifecyclePhases(web_view);
WebDocument document = web_view->MainFrameImpl()->GetDocument();
EXPECT_TRUE(document.IsPluginDocument());
WebPluginContainer* plugin_container =
GetWebPluginContainer(web_view, "plugin");
EXPECT_EQ(document.FocusedElement(), plugin_container->GetElement());
}
TEST_F(WebPluginContainerTest, IFramePluginDocumentNotFocused) {
RegisterMockedURL("test.pdf", "application/pdf");
RegisterMockedURL("iframe_pdf.html", "text/html");
// Must outlive |web_view_helper|.
TestPluginWebFrameClient plugin_web_frame_client;
frame_test_helpers::WebViewHelper web_view_helper;
WebViewImpl* web_view = web_view_helper.InitializeAndLoad(
base_url_ + "iframe_pdf.html", &plugin_web_frame_client);
DCHECK(web_view);
UpdateAllLifecyclePhases(web_view);
WebDocument document = web_view->MainFrameImpl()->GetDocument();
WebLocalFrame* iframe =
web_view->MainFrame()->FirstChild()->ToWebLocalFrame();
EXPECT_TRUE(iframe->GetDocument().IsPluginDocument());
WebPluginContainer* plugin_container =
iframe->GetDocument().GetElementById("plugin").PluginContainer();
EXPECT_NE(document.FocusedElement(), plugin_container->GetElement());
EXPECT_NE(iframe->GetDocument().FocusedElement(),
plugin_container->GetElement());
}
TEST_F(WebPluginContainerTest, PrintOnePage) {
RegisterMockedURL("test.pdf", "application/pdf");
// Must outlive |web_view_helper|.
TestPluginWebFrameClient plugin_web_frame_client;
frame_test_helpers::WebViewHelper web_view_helper;
WebViewImpl* web_view = web_view_helper.InitializeAndLoad(
base_url_ + "test.pdf", &plugin_web_frame_client);
DCHECK(web_view);
UpdateAllLifecyclePhases(web_view);
RunPendingTasks();
WebLocalFrame* frame = web_view->MainFrameImpl();
WebPrintParams print_params;
print_params.print_content_area.width = 500;
print_params.print_content_area.height = 500;
frame->PrintBegin(print_params);
PaintRecorder recorder;
frame->PrintPage(0, recorder.beginRecording(IntRect()));
frame->PrintEnd();
DCHECK(plugin_web_frame_client.PrintedAtLeastOnePage());
}
TEST_F(WebPluginContainerTest, PrintAllPages) {
RegisterMockedURL("test.pdf", "application/pdf");
// Must outlive |web_view_helper|.
TestPluginWebFrameClient plugin_web_frame_client;
frame_test_helpers::WebViewHelper web_view_helper;
WebViewImpl* web_view = web_view_helper.InitializeAndLoad(
base_url_ + "test.pdf", &plugin_web_frame_client);
DCHECK(web_view);
UpdateAllLifecyclePhases(web_view);
RunPendingTasks();
WebLocalFrame* frame = web_view->MainFrameImpl();
WebPrintParams print_params;
print_params.print_content_area.width = 500;
print_params.print_content_area.height = 500;
frame->PrintBegin(print_params);
PaintRecorder recorder;
frame->PrintPagesForTesting(recorder.beginRecording(IntRect()), WebSize());
frame->PrintEnd();
DCHECK(plugin_web_frame_client.PrintedAtLeastOnePage());
}
TEST_F(WebPluginContainerTest, LocalToWindowPointTest) {
RegisterMockedURL("plugin_container.html");
// Must outlive |web_view_helper|.
TestPluginWebFrameClient plugin_web_frame_client;
frame_test_helpers::WebViewHelper web_view_helper;
WebViewImpl* web_view = web_view_helper.InitializeAndLoad(
base_url_ + "plugin_container.html", &plugin_web_frame_client);
EnablePlugins(web_view, WebSize(300, 300));
WebPluginContainer* plugin_container_one =
GetWebPluginContainer(web_view, WebString::FromUTF8("translated-plugin"));
DCHECK(plugin_container_one);
WebPoint point1 = plugin_container_one->LocalToRootFramePoint(WebPoint(0, 0));
ASSERT_EQ(10, point1.x);
ASSERT_EQ(10, point1.y);
WebPoint point2 =
plugin_container_one->LocalToRootFramePoint(WebPoint(90, 90));
ASSERT_EQ(100, point2.x);
ASSERT_EQ(100, point2.y);
WebPluginContainer* plugin_container_two =
GetWebPluginContainer(web_view, WebString::FromUTF8("rotated-plugin"));
DCHECK(plugin_container_two);
WebPoint point3 =
plugin_container_two->LocalToRootFramePoint(WebPoint(10, 0));
ASSERT_EQ(0, point3.x);
ASSERT_EQ(10, point3.y);
WebPoint point4 =
plugin_container_two->LocalToRootFramePoint(WebPoint(10, 10));
ASSERT_EQ(-10, point4.x);
ASSERT_EQ(10, point4.y);
}
// Verifies executing the command 'Copy' results in copying to the clipboard.
TEST_F(WebPluginContainerTest, Copy) {
RegisterMockedURL("plugin_container.html");
// Must outlive |web_view_helper|.
TestPluginWebFrameClient plugin_web_frame_client;
frame_test_helpers::WebViewHelper web_view_helper;
WebViewImpl* web_view = web_view_helper.InitializeAndLoad(
base_url_ + "plugin_container.html", &plugin_web_frame_client);
EnablePlugins(web_view, WebSize(300, 300));
web_view->MainFrameImpl()
->GetDocument()
.Unwrap<Document>()
->body()
->getElementById("translated-plugin")
->focus();
EXPECT_TRUE(web_view->MainFrame()->ToWebLocalFrame()->ExecuteCommand("Copy"));
EXPECT_EQ(String("x"), ReadClipboard());
ClearClipboardBuffer();
}
TEST_F(WebPluginContainerTest, CopyFromContextMenu) {
RegisterMockedURL("plugin_container.html");
// Must outlive |web_view_helper|.
TestPluginWebFrameClient plugin_web_frame_client;
frame_test_helpers::WebViewHelper web_view_helper;
WebViewImpl* web_view = web_view_helper.InitializeAndLoad(
base_url_ + "plugin_container.html", &plugin_web_frame_client);
EnablePlugins(web_view, WebSize(300, 300));
// Make sure the right-click + command works in common scenario.
ExecuteContextMenuCommand(web_view, "Copy");
EXPECT_EQ(String("x"), ReadClipboard());
ClearClipboardBuffer();
auto event = frame_test_helpers::CreateMouseEvent(
WebMouseEvent::kMouseDown, WebMouseEvent::Button::kRight,
WebPoint(30, 30), 0);
event.click_count = 1;
// Now, let's try a more complex scenario:
// 1) open the context menu. This will focus the plugin.
web_view->MainFrameWidget()->HandleInputEvent(WebCoalescedInputEvent(event));
// 2) document blurs the plugin, because it can.
web_view->ClearFocusedElement();
// 3) Copy should still operate on the context node, even though the focus had
// shifted.
EXPECT_TRUE(web_view->MainFrameImpl()->ExecuteCommand("Copy"));
EXPECT_EQ(String("x"), ReadClipboard());
ClearClipboardBuffer();
}
// Verifies |Ctrl-C| and |Ctrl-Insert| keyboard events, results in copying to
// the clipboard.
TEST_F(WebPluginContainerTest, CopyInsertKeyboardEventsTest) {
RegisterMockedURL("plugin_container.html");
// Must outlive |web_view_helper|.
TestPluginWebFrameClient plugin_web_frame_client;
frame_test_helpers::WebViewHelper web_view_helper;
WebViewImpl* web_view = web_view_helper.InitializeAndLoad(
base_url_ + "plugin_container.html", &plugin_web_frame_client);
EnablePlugins(web_view, WebSize(300, 300));
WebElement plugin_container_one_element =
web_view->MainFrameImpl()->GetDocument().GetElementById(
WebString::FromUTF8("translated-plugin"));
WebInputEvent::Modifiers modifier_key = static_cast<WebInputEvent::Modifiers>(
kEditingModifier | WebInputEvent::kNumLockOn | WebInputEvent::kIsLeft);
CreateAndHandleKeyboardEvent(&plugin_container_one_element, modifier_key,
VKEY_C);
EXPECT_EQ(String("x"), ReadClipboard());
ClearClipboardBuffer();
CreateAndHandleKeyboardEvent(&plugin_container_one_element, modifier_key,
VKEY_INSERT);
EXPECT_EQ(String("x"), ReadClipboard());
ClearClipboardBuffer();
}
// Verifies |Ctrl-X| and |Shift-Delete| keyboard events, results in the "Cut"
// command being invoked.
TEST_F(WebPluginContainerTest, CutDeleteKeyboardEventsTest) {
RegisterMockedURL("plugin_container.html");
// Must outlive |web_view_helper|.
TestPluginWebFrameClient plugin_web_frame_client;
frame_test_helpers::WebViewHelper web_view_helper;
// Use TestPluginWithEditableText for testing "Cut".
plugin_web_frame_client.SetHasEditableText(true);
WebViewImpl* web_view = web_view_helper.InitializeAndLoad(
base_url_ + "plugin_container.html", &plugin_web_frame_client);
EnablePlugins(web_view, WebSize(300, 300));
WebElement plugin_container_one_element =
web_view->MainFrameImpl()->GetDocument().GetElementById(
WebString::FromUTF8("translated-plugin"));
WebInputEvent::Modifiers modifier_key = static_cast<WebInputEvent::Modifiers>(
kEditingModifier | WebInputEvent::kNumLockOn | WebInputEvent::kIsLeft);
CreateAndHandleKeyboardEvent(&plugin_container_one_element, modifier_key,
VKEY_X);
// Check that "Cut" command is invoked.
auto* test_plugin =
TestPluginWithEditableText::FromContainer(&plugin_container_one_element);
EXPECT_TRUE(test_plugin->IsCutCalled());
// Reset Cut status for next time.
test_plugin->ResetEditCommandState();
modifier_key = static_cast<WebInputEvent::Modifiers>(
WebInputEvent::kShiftKey | WebInputEvent::kNumLockOn |
WebInputEvent::kIsLeft);
CreateAndHandleKeyboardEvent(&plugin_container_one_element, modifier_key,
VKEY_DELETE);
// Check that "Cut" command is invoked.
EXPECT_TRUE(test_plugin->IsCutCalled());
}
// Verifies |Ctrl-V| and |Shift-Insert| keyboard events, results in the "Paste"
// command being invoked.
TEST_F(WebPluginContainerTest, PasteInsertKeyboardEventsTest) {
RegisterMockedURL("plugin_container.html");
// Must outlive |web_view_helper|.
TestPluginWebFrameClient plugin_web_frame_client;
frame_test_helpers::WebViewHelper web_view_helper;
// Use TestPluginWithEditableText for testing "Paste".
plugin_web_frame_client.SetHasEditableText(true);
WebViewImpl* web_view = web_view_helper.InitializeAndLoad(
base_url_ + "plugin_container.html", &plugin_web_frame_client);
EnablePlugins(web_view, WebSize(300, 300));
WebElement plugin_container_one_element =
web_view->MainFrameImpl()->GetDocument().GetElementById(
WebString::FromUTF8("translated-plugin"));
WebInputEvent::Modifiers modifier_key = static_cast<WebInputEvent::Modifiers>(
kEditingModifier | WebInputEvent::kNumLockOn | WebInputEvent::kIsLeft);
CreateAndHandleKeyboardEvent(&plugin_container_one_element, modifier_key,
VKEY_V);
// Check that "Paste" command is invoked.
auto* test_plugin =
TestPluginWithEditableText::FromContainer(&plugin_container_one_element);
EXPECT_TRUE(test_plugin->IsPasteCalled());
// Reset Paste status for next time.
test_plugin->ResetEditCommandState();
modifier_key = static_cast<WebInputEvent::Modifiers>(
WebInputEvent::kShiftKey | WebInputEvent::kNumLockOn |
WebInputEvent::kIsLeft);
CreateAndHandleKeyboardEvent(&plugin_container_one_element, modifier_key,
VKEY_INSERT);
// Check that "Paste" command is invoked.
EXPECT_TRUE(test_plugin->IsPasteCalled());
}
// Verifies |Ctrl-Shift-V| keyboard event results in the "PasteAndMatchStyle"
// command being invoked.
TEST_F(WebPluginContainerTest, PasteAndMatchStyleKeyboardEventsTest) {
RegisterMockedURL("plugin_container.html");
// Must outlive |web_view_helper|.
TestPluginWebFrameClient plugin_web_frame_client;
frame_test_helpers::WebViewHelper web_view_helper;
// Use TestPluginWithEditableText for testing "PasteAndMatchStyle".
plugin_web_frame_client.SetHasEditableText(true);
WebViewImpl* web_view = web_view_helper.InitializeAndLoad(
base_url_ + "plugin_container.html", &plugin_web_frame_client);
EnablePlugins(web_view, WebSize(300, 300));
WebElement plugin_container_one_element =
web_view->MainFrameImpl()->GetDocument().GetElementById(
WebString::FromUTF8("translated-plugin"));
WebInputEvent::Modifiers modifier_key = static_cast<WebInputEvent::Modifiers>(
kEditingModifier | WebInputEvent::kShiftKey | WebInputEvent::kNumLockOn |
WebInputEvent::kIsLeft);
CreateAndHandleKeyboardEvent(&plugin_container_one_element, modifier_key,
VKEY_V);
// Check that "PasteAndMatchStyle" command is invoked.
auto* test_plugin =
TestPluginWithEditableText::FromContainer(&plugin_container_one_element);
EXPECT_TRUE(test_plugin->IsPasteCalled());
}
TEST_F(WebPluginContainerTest, CutFromContextMenu) {
RegisterMockedURL("plugin_container.html");
// Must outlive |web_view_helper|.
TestPluginWebFrameClient plugin_web_frame_client;
frame_test_helpers::WebViewHelper web_view_helper;
// Use TestPluginWithEditableText for testing "Cut".
plugin_web_frame_client.SetHasEditableText(true);
WebViewImpl* web_view = web_view_helper.InitializeAndLoad(
base_url_ + "plugin_container.html", &plugin_web_frame_client);
EnablePlugins(web_view, WebSize(300, 300));
WebElement plugin_container_one_element =
web_view->MainFrameImpl()->GetDocument().GetElementById(
WebString::FromUTF8("translated-plugin"));
ExecuteContextMenuCommand(web_view, "Cut");
auto* test_plugin =
TestPluginWithEditableText::FromContainer(&plugin_container_one_element);
EXPECT_TRUE(test_plugin->IsCutCalled());
}
TEST_F(WebPluginContainerTest, PasteFromContextMenu) {
RegisterMockedURL("plugin_container.html");
// Must outlive |web_view_helper|.
TestPluginWebFrameClient plugin_web_frame_client;
frame_test_helpers::WebViewHelper web_view_helper;
// Use TestPluginWithEditableText for testing "Paste".
plugin_web_frame_client.SetHasEditableText(true);
WebViewImpl* web_view = web_view_helper.InitializeAndLoad(
base_url_ + "plugin_container.html", &plugin_web_frame_client);
EnablePlugins(web_view, WebSize(300, 300));
WebElement plugin_container_one_element =
web_view->MainFrameImpl()->GetDocument().GetElementById(
WebString::FromUTF8("translated-plugin"));
ExecuteContextMenuCommand(web_view, "Paste");
auto* test_plugin =
TestPluginWithEditableText::FromContainer(&plugin_container_one_element);
EXPECT_TRUE(test_plugin->IsPasteCalled());
}
TEST_F(WebPluginContainerTest, PasteAndMatchStyleFromContextMenu) {
RegisterMockedURL("plugin_container.html");
// Must outlive |web_view_helper|.
TestPluginWebFrameClient plugin_web_frame_client;
frame_test_helpers::WebViewHelper web_view_helper;
// Use TestPluginWithEditableText for testing "Paste".
plugin_web_frame_client.SetHasEditableText(true);
WebViewImpl* web_view = web_view_helper.InitializeAndLoad(
base_url_ + "plugin_container.html", &plugin_web_frame_client);
EnablePlugins(web_view, WebSize(300, 300));
WebElement plugin_container_one_element =
web_view->MainFrameImpl()->GetDocument().GetElementById(
WebString::FromUTF8("translated-plugin"));
ExecuteContextMenuCommand(web_view, "PasteAndMatchStyle");
auto* test_plugin =
TestPluginWithEditableText::FromContainer(&plugin_container_one_element);
EXPECT_TRUE(test_plugin->IsPasteCalled());
}
// A class to facilitate testing that events are correctly received by plugins.
class EventTestPlugin : public FakeWebPlugin {
public:
explicit EventTestPlugin(const WebPluginParams& params)
: FakeWebPlugin(params),
last_event_type_(WebInputEvent::kUndefined),
last_event_modifiers_(WebInputEvent::kNoModifiers) {}
WebInputEventResult HandleInputEvent(
const WebCoalescedInputEvent& coalesced_event,
WebCursorInfo&) override {
const WebInputEvent& event = coalesced_event.Event();
coalesced_event_count_ = coalesced_event.CoalescedEventSize();
last_event_type_ = event.GetType();
last_event_modifiers_ = event.GetModifiers();
if (WebInputEvent::IsMouseEventType(event.GetType()) ||
event.GetType() == WebInputEvent::kMouseWheel) {
const WebMouseEvent& mouse_event =
static_cast<const WebMouseEvent&>(event);
last_event_location_ = IntPoint(mouse_event.PositionInWidget().x,
mouse_event.PositionInWidget().y);
} else if (WebInputEvent::IsTouchEventType(event.GetType())) {
const WebTouchEvent& touch_event =
static_cast<const WebTouchEvent&>(event);
if (touch_event.touches_length == 1) {
last_event_location_ =
IntPoint(touch_event.touches[0].PositionInWidget().x,
touch_event.touches[0].PositionInWidget().y);
} else {
last_event_location_ = IntPoint();
}
}
return WebInputEventResult::kHandledSystem;
}
WebInputEvent::Type GetLastInputEventType() { return last_event_type_; }
IntPoint GetLastEventLocation() { return last_event_location_; }
int GetLastEventModifiers() { return last_event_modifiers_; }
void ClearLastEventType() { last_event_type_ = WebInputEvent::kUndefined; }
size_t GetCoalescedEventCount() { return coalesced_event_count_; }
private:
~EventTestPlugin() override = default;
size_t coalesced_event_count_;
WebInputEvent::Type last_event_type_;
IntPoint last_event_location_;
int last_event_modifiers_;
};
TEST_F(WebPluginContainerTest, GestureLongPressReachesPlugin) {
RegisterMockedURL("plugin_container.html");
// Must outlive |web_view_helper|.
CustomPluginWebFrameClient<EventTestPlugin> plugin_web_frame_client;
frame_test_helpers::WebViewHelper web_view_helper;
WebViewImpl* web_view = web_view_helper.InitializeAndLoad(
base_url_ + "plugin_container.html", &plugin_web_frame_client);
EnablePlugins(web_view, WebSize(300, 300));
WebElement plugin_container_one_element =
web_view->MainFrameImpl()->GetDocument().GetElementById(
WebString::FromUTF8("translated-plugin"));
WebPlugin* plugin = static_cast<WebPluginContainerImpl*>(
plugin_container_one_element.PluginContainer())
->Plugin();
EventTestPlugin* test_plugin = static_cast<EventTestPlugin*>(plugin);
WebGestureEvent event(WebInputEvent::kGestureLongPress,
WebInputEvent::kNoModifiers,
WebInputEvent::GetStaticTimeStampForTests(),
kWebGestureDeviceTouchscreen);
// First, send an event that doesn't hit the plugin to verify that the
// plugin doesn't receive it.
event.SetPositionInWidget(WebFloatPoint(0, 0));
web_view->MainFrameWidget()->HandleInputEvent(WebCoalescedInputEvent(event));
RunPendingTasks();
EXPECT_EQ(WebInputEvent::kUndefined, test_plugin->GetLastInputEventType());
// Next, send an event that does hit the plugin, and verify it does receive
// it.
WebRect rect = plugin_container_one_element.BoundsInViewport();
event.SetPositionInWidget(
WebFloatPoint(rect.x + rect.width / 2, rect.y + rect.height / 2));
web_view->MainFrameWidget()->HandleInputEvent(WebCoalescedInputEvent(event));
RunPendingTasks();
EXPECT_EQ(WebInputEvent::kGestureLongPress,
test_plugin->GetLastInputEventType());
}
TEST_F(WebPluginContainerTest, MouseEventButtons) {
RegisterMockedURL("plugin_container.html");
// Must outlive |web_view_helper|.
CustomPluginWebFrameClient<EventTestPlugin> plugin_web_frame_client;
frame_test_helpers::WebViewHelper web_view_helper;
WebViewImpl* web_view = web_view_helper.InitializeAndLoad(
base_url_ + "plugin_container.html", &plugin_web_frame_client);
EnablePlugins(web_view, WebSize(300, 300));
WebElement plugin_container_one_element =
web_view->MainFrameImpl()->GetDocument().GetElementById(
WebString::FromUTF8("translated-plugin"));
WebPlugin* plugin = static_cast<WebPluginContainerImpl*>(
plugin_container_one_element.PluginContainer())
->Plugin();
EventTestPlugin* test_plugin = static_cast<EventTestPlugin*>(plugin);
WebMouseEvent event = frame_test_helpers::CreateMouseEvent(
WebMouseEvent::kMouseMove, WebMouseEvent::Button::kNoButton,
WebPoint(30, 30),
WebInputEvent::kMiddleButtonDown | WebInputEvent::kShiftKey);
WebRect rect = plugin_container_one_element.BoundsInViewport();
event.SetPositionInWidget(rect.x + rect.width / 2, rect.y + rect.height / 2);
web_view->MainFrameWidget()->HandleInputEvent(WebCoalescedInputEvent(event));
RunPendingTasks();
EXPECT_EQ(WebInputEvent::kMouseMove, test_plugin->GetLastInputEventType());
EXPECT_EQ(WebInputEvent::kMiddleButtonDown | WebInputEvent::kShiftKey,
test_plugin->GetLastEventModifiers());
}
TEST_F(WebPluginContainerTest, MouseWheelEventTranslated) {
RegisterMockedURL("plugin_container.html");
// Must outlive |web_view_helper|.
CustomPluginWebFrameClient<EventTestPlugin> plugin_web_frame_client;
frame_test_helpers::WebViewHelper web_view_helper;
WebViewImpl* web_view = web_view_helper.InitializeAndLoad(
base_url_ + "plugin_container.html", &plugin_web_frame_client);
EnablePlugins(web_view, WebSize(300, 300));
WebElement plugin_container_one_element =
web_view->MainFrameImpl()->GetDocument().GetElementById(
WebString::FromUTF8("translated-plugin"));
WebPlugin* plugin = static_cast<WebPluginContainerImpl*>(
plugin_container_one_element.PluginContainer())
->Plugin();
EventTestPlugin* test_plugin = static_cast<EventTestPlugin*>(plugin);
WebMouseWheelEvent event(WebInputEvent::kMouseWheel,
WebInputEvent::kNoModifiers,
WebInputEvent::GetStaticTimeStampForTests());
WebRect rect = plugin_container_one_element.BoundsInViewport();
event.SetPositionInWidget(rect.x + rect.width / 2, rect.y + rect.height / 2);
web_view->MainFrameWidget()->HandleInputEvent(WebCoalescedInputEvent(event));
RunPendingTasks();
EXPECT_EQ(WebInputEvent::kMouseWheel, test_plugin->GetLastInputEventType());
EXPECT_EQ(rect.width / 2, test_plugin->GetLastEventLocation().X());
EXPECT_EQ(rect.height / 2, test_plugin->GetLastEventLocation().Y());
}
TEST_F(WebPluginContainerTest, TouchEventScrolled) {
RegisterMockedURL("plugin_scroll.html");
// Must outlive |web_view_helper|.
CustomPluginWebFrameClient<EventTestPlugin> plugin_web_frame_client;
frame_test_helpers::WebViewHelper web_view_helper;
WebViewImpl* web_view = web_view_helper.InitializeAndLoad(
base_url_ + "plugin_scroll.html", &plugin_web_frame_client);
EnablePlugins(web_view, WebSize(300, 300));
web_view->SmoothScroll(0, 200, 0);
UpdateAllLifecyclePhases(web_view);
RunPendingTasks();
WebElement plugin_container_one_element =
web_view->MainFrameImpl()->GetDocument().GetElementById(
WebString::FromUTF8("scrolled-plugin"));
plugin_container_one_element.PluginContainer()->RequestTouchEventType(
WebPluginContainer::kTouchEventRequestTypeRaw);
WebPlugin* plugin = static_cast<WebPluginContainerImpl*>(
plugin_container_one_element.PluginContainer())
->Plugin();
EventTestPlugin* test_plugin = static_cast<EventTestPlugin*>(plugin);
WebRect rect = plugin_container_one_element.BoundsInViewport();
WebPointerEvent event(
WebInputEvent::kPointerDown,
WebPointerProperties(
1, WebPointerProperties::PointerType::kTouch,
WebPointerProperties::Button::kLeft,
WebFloatPoint(rect.x + rect.width / 2, rect.y + rect.height / 2),
WebFloatPoint(rect.x + rect.width / 2, rect.y + rect.height / 2)),
1.0f, 1.0f);
web_view->MainFrameWidget()->HandleInputEvent(WebCoalescedInputEvent(event));
web_view->MainFrameWidget()->DispatchBufferedTouchEvents();
RunPendingTasks();
EXPECT_EQ(WebInputEvent::kTouchStart, test_plugin->GetLastInputEventType());
EXPECT_EQ(rect.width / 2, test_plugin->GetLastEventLocation().X());
EXPECT_EQ(rect.height / 2, test_plugin->GetLastEventLocation().Y());
}
TEST_F(WebPluginContainerTest, TouchEventScrolledWithCoalescedTouches) {
RegisterMockedURL("plugin_scroll.html");
// Must outlive |web_view_helper|.
CustomPluginWebFrameClient<EventTestPlugin> plugin_web_frame_client;
frame_test_helpers::WebViewHelper web_view_helper;
WebViewImpl* web_view = web_view_helper.InitializeAndLoad(
base_url_ + "plugin_scroll.html", &plugin_web_frame_client);
EnablePlugins(web_view, WebSize(300, 300));
web_view->SmoothScroll(0, 200, 0);
UpdateAllLifecyclePhases(web_view);
RunPendingTasks();
WebElement plugin_container_one_element =
web_view->MainFrameImpl()->GetDocument().GetElementById(
WebString::FromUTF8("scrolled-plugin"));
plugin_container_one_element.PluginContainer()->RequestTouchEventType(
WebPluginContainer::kTouchEventRequestTypeRawLowLatency);
WebPlugin* plugin = static_cast<WebPluginContainerImpl*>(
plugin_container_one_element.PluginContainer())
->Plugin();
EventTestPlugin* test_plugin = static_cast<EventTestPlugin*>(plugin);
{
WebRect rect = plugin_container_one_element.BoundsInViewport();
WebPointerEvent event(
WebInputEvent::kPointerDown,
WebPointerProperties(
1, WebPointerProperties::PointerType::kTouch,
WebPointerProperties::Button::kLeft,
WebFloatPoint(rect.x + rect.width / 2, rect.y + rect.height / 2),
WebFloatPoint(rect.x + rect.width / 2, rect.y + rect.height / 2)),
1.0f, 1.0f);
WebCoalescedInputEvent coalesced_event(event);
web_view->MainFrameWidget()->HandleInputEvent(coalesced_event);
web_view->MainFrameWidget()->DispatchBufferedTouchEvents();
RunPendingTasks();
EXPECT_EQ(static_cast<const size_t>(1),
test_plugin->GetCoalescedEventCount());
EXPECT_EQ(WebInputEvent::kTouchStart, test_plugin->GetLastInputEventType());
EXPECT_EQ(rect.width / 2, test_plugin->GetLastEventLocation().X());
EXPECT_EQ(rect.height / 2, test_plugin->GetLastEventLocation().Y());
}
{
WebRect rect = plugin_container_one_element.BoundsInViewport();
WebPointerEvent event1(
WebInputEvent::kPointerMove,
WebPointerProperties(1, WebPointerProperties::PointerType::kTouch,
WebPointerProperties::Button::kLeft,
WebFloatPoint(rect.x + rect.width / 2 + 1,
rect.y + rect.height / 2 + 1),
WebFloatPoint(rect.x + rect.width / 2 + 1,
rect.y + rect.height / 2 + 1)),
1.0f, 1.0f);
WebCoalescedInputEvent coalesced_event(event1);
WebPointerEvent event2(
WebInputEvent::kPointerMove,
WebPointerProperties(1, WebPointerProperties::PointerType::kTouch,
WebPointerProperties::Button::kLeft,
WebFloatPoint(rect.x + rect.width / 2 + 2,
rect.y + rect.height / 2 + 2),
WebFloatPoint(rect.x + rect.width / 2 + 2,
rect.y + rect.height / 2 + 2)),
1.0f, 1.0f);
WebPointerEvent event3(
WebInputEvent::kPointerMove,
WebPointerProperties(1, WebPointerProperties::PointerType::kTouch,
WebPointerProperties::Button::kLeft,
WebFloatPoint(rect.x + rect.width / 2 + 3,
rect.y + rect.height / 2 + 3),
WebFloatPoint(rect.x + rect.width / 2 + 3,
rect.y + rect.height / 2 + 3)),
1.0f, 1.0f);
coalesced_event.AddCoalescedEvent(event2);
coalesced_event.AddCoalescedEvent(event3);
web_view->MainFrameWidget()->HandleInputEvent(coalesced_event);
web_view->MainFrameWidget()->DispatchBufferedTouchEvents();
RunPendingTasks();
EXPECT_EQ(static_cast<const size_t>(3),
test_plugin->GetCoalescedEventCount());
EXPECT_EQ(WebInputEvent::kTouchMove, test_plugin->GetLastInputEventType());
EXPECT_EQ(rect.width / 2 + 1, test_plugin->GetLastEventLocation().X());
EXPECT_EQ(rect.height / 2 + 1, test_plugin->GetLastEventLocation().Y());
}
}
TEST_F(WebPluginContainerTest, MouseWheelEventScrolled) {
RegisterMockedURL("plugin_scroll.html");
// Must outlive |web_view_helper|.
CustomPluginWebFrameClient<EventTestPlugin> plugin_web_frame_client;
frame_test_helpers::WebViewHelper web_view_helper;
WebViewImpl* web_view = web_view_helper.InitializeAndLoad(
base_url_ + "plugin_scroll.html", &plugin_web_frame_client);
EnablePlugins(web_view, WebSize(300, 300));
web_view->SmoothScroll(0, 200, 0);
UpdateAllLifecyclePhases(web_view);
RunPendingTasks();
WebElement plugin_container_one_element =
web_view->MainFrameImpl()->GetDocument().GetElementById(
WebString::FromUTF8("scrolled-plugin"));
plugin_container_one_element.PluginContainer()->RequestTouchEventType(
WebPluginContainer::kTouchEventRequestTypeRaw);
WebPlugin* plugin = static_cast<WebPluginContainerImpl*>(
plugin_container_one_element.PluginContainer())
->Plugin();
EventTestPlugin* test_plugin = static_cast<EventTestPlugin*>(plugin);
WebMouseWheelEvent event(WebInputEvent::kMouseWheel,
WebInputEvent::kNoModifiers,
WebInputEvent::GetStaticTimeStampForTests());
WebRect rect = plugin_container_one_element.BoundsInViewport();
event.SetPositionInWidget(rect.x + rect.width / 2, rect.y + rect.height / 2);
web_view->MainFrameWidget()->HandleInputEvent(WebCoalescedInputEvent(event));
RunPendingTasks();
EXPECT_EQ(WebInputEvent::kMouseWheel, test_plugin->GetLastInputEventType());
EXPECT_EQ(rect.width / 2, test_plugin->GetLastEventLocation().X());
EXPECT_EQ(rect.height / 2, test_plugin->GetLastEventLocation().Y());
}
TEST_F(WebPluginContainerTest, MouseEventScrolled) {
RegisterMockedURL("plugin_scroll.html");
// Must outlive |web_view_helper|.
CustomPluginWebFrameClient<EventTestPlugin> plugin_web_frame_client;
frame_test_helpers::WebViewHelper web_view_helper;
WebViewImpl* web_view = web_view_helper.InitializeAndLoad(
base_url_ + "plugin_scroll.html", &plugin_web_frame_client);
EnablePlugins(web_view, WebSize(300, 300));
web_view->SmoothScroll(0, 200, 0);
UpdateAllLifecyclePhases(web_view);
RunPendingTasks();
WebElement plugin_container_one_element =
web_view->MainFrameImpl()->GetDocument().GetElementById(
WebString::FromUTF8("scrolled-plugin"));
plugin_container_one_element.PluginContainer()->RequestTouchEventType(
WebPluginContainer::kTouchEventRequestTypeRaw);
WebPlugin* plugin = static_cast<WebPluginContainerImpl*>(
plugin_container_one_element.PluginContainer())
->Plugin();
EventTestPlugin* test_plugin = static_cast<EventTestPlugin*>(plugin);
WebMouseEvent event(WebInputEvent::kMouseMove, WebInputEvent::kNoModifiers,
WebInputEvent::GetStaticTimeStampForTests());
WebRect rect = plugin_container_one_element.BoundsInViewport();
event.SetPositionInWidget(rect.x + rect.width / 2, rect.y + rect.height / 2);
web_view->MainFrameWidget()->HandleInputEvent(WebCoalescedInputEvent(event));
RunPendingTasks();
EXPECT_EQ(WebInputEvent::kMouseMove, test_plugin->GetLastInputEventType());
EXPECT_EQ(rect.width / 2, test_plugin->GetLastEventLocation().X());
EXPECT_EQ(rect.height / 2, test_plugin->GetLastEventLocation().Y());
}
TEST_F(WebPluginContainerTest, MouseEventZoomed) {
RegisterMockedURL("plugin_scroll.html");
// Must outlive |web_view_helper|.
CustomPluginWebFrameClient<EventTestPlugin> plugin_web_frame_client;
frame_test_helpers::WebViewHelper web_view_helper;
WebViewImpl* web_view = web_view_helper.InitializeAndLoad(
base_url_ + "plugin_scroll.html", &plugin_web_frame_client);
DCHECK(web_view);
web_view->GetSettings()->SetPluginsEnabled(true);
web_view->MainFrameWidget()->Resize(WebSize(300, 300));
web_view->SetPageScaleFactor(2);
web_view->SmoothScroll(0, 300, 0);
UpdateAllLifecyclePhases(web_view);
RunPendingTasks();
WebElement plugin_container_one_element =
web_view->MainFrameImpl()->GetDocument().GetElementById(
WebString::FromUTF8("scrolled-plugin"));
plugin_container_one_element.PluginContainer()->RequestTouchEventType(
WebPluginContainer::kTouchEventRequestTypeRaw);
WebPlugin* plugin = static_cast<WebPluginContainerImpl*>(
plugin_container_one_element.PluginContainer())
->Plugin();
EventTestPlugin* test_plugin = static_cast<EventTestPlugin*>(plugin);
WebMouseEvent event(WebInputEvent::kMouseMove, WebInputEvent::kNoModifiers,
WebInputEvent::GetStaticTimeStampForTests());
WebRect rect = plugin_container_one_element.BoundsInViewport();
event.SetPositionInWidget(rect.x + rect.width / 2, rect.y + rect.height / 2);
web_view->MainFrameWidget()->HandleInputEvent(WebCoalescedInputEvent(event));
RunPendingTasks();
// rect.width/height divided by 4 because the rect is in viewport bounds and
// there is a scale of 2 set.
EXPECT_EQ(WebInputEvent::kMouseMove, test_plugin->GetLastInputEventType());
EXPECT_EQ(rect.width / 4, test_plugin->GetLastEventLocation().X());
EXPECT_EQ(rect.height / 4, test_plugin->GetLastEventLocation().Y());
}
TEST_F(WebPluginContainerTest, MouseWheelEventZoomed) {
RegisterMockedURL("plugin_scroll.html");
// Must outlive |web_view_helper|.
CustomPluginWebFrameClient<EventTestPlugin> plugin_web_frame_client;
frame_test_helpers::WebViewHelper web_view_helper;
WebViewImpl* web_view = web_view_helper.InitializeAndLoad(
base_url_ + "plugin_scroll.html", &plugin_web_frame_client);
DCHECK(web_view);
web_view->GetSettings()->SetPluginsEnabled(true);
web_view->MainFrameWidget()->Resize(WebSize(300, 300));
web_view->SetPageScaleFactor(2);
web_view->SmoothScroll(0, 300, 0);
UpdateAllLifecyclePhases(web_view);
RunPendingTasks();
WebElement plugin_container_one_element =
web_view->MainFrameImpl()->GetDocument().GetElementById(
WebString::FromUTF8("scrolled-plugin"));
plugin_container_one_element.PluginContainer()->RequestTouchEventType(
WebPluginContainer::kTouchEventRequestTypeRaw);
WebPlugin* plugin = static_cast<WebPluginContainerImpl*>(
plugin_container_one_element.PluginContainer())
->Plugin();
EventTestPlugin* test_plugin = static_cast<EventTestPlugin*>(plugin);
WebMouseWheelEvent event(WebInputEvent::kMouseWheel,
WebInputEvent::kNoModifiers,
WebInputEvent::GetStaticTimeStampForTests());
WebRect rect = plugin_container_one_element.BoundsInViewport();
event.SetPositionInWidget(rect.x + rect.width / 2, rect.y + rect.height / 2);
web_view->MainFrameWidget()->HandleInputEvent(WebCoalescedInputEvent(event));
RunPendingTasks();
// rect.width/height divided by 4 because the rect is in viewport bounds and
// there is a scale of 2 set.
EXPECT_EQ(WebInputEvent::kMouseWheel, test_plugin->GetLastInputEventType());
EXPECT_EQ(rect.width / 4, test_plugin->GetLastEventLocation().X());
EXPECT_EQ(rect.height / 4, test_plugin->GetLastEventLocation().Y());
}
TEST_F(WebPluginContainerTest, TouchEventZoomed) {
RegisterMockedURL("plugin_scroll.html");
// Must outlive |web_view_helper|.
CustomPluginWebFrameClient<EventTestPlugin> plugin_web_frame_client;
frame_test_helpers::WebViewHelper web_view_helper;
WebViewImpl* web_view = web_view_helper.InitializeAndLoad(
base_url_ + "plugin_scroll.html", &plugin_web_frame_client);
DCHECK(web_view);
web_view->GetSettings()->SetPluginsEnabled(true);
web_view->MainFrameWidget()->Resize(WebSize(300, 300));
web_view->SetPageScaleFactor(2);
web_view->SmoothScroll(0, 300, 0);
UpdateAllLifecyclePhases(web_view);
RunPendingTasks();
WebElement plugin_container_one_element =
web_view->MainFrameImpl()->GetDocument().GetElementById(
WebString::FromUTF8("scrolled-plugin"));
plugin_container_one_element.PluginContainer()->RequestTouchEventType(
WebPluginContainer::kTouchEventRequestTypeRaw);
WebPlugin* plugin = static_cast<WebPluginContainerImpl*>(
plugin_container_one_element.PluginContainer())
->Plugin();
EventTestPlugin* test_plugin = static_cast<EventTestPlugin*>(plugin);
WebRect rect = plugin_container_one_element.BoundsInViewport();
WebPointerEvent event(
WebInputEvent::kPointerDown,
WebPointerProperties(
1, WebPointerProperties::PointerType::kTouch,
WebPointerProperties::Button::kLeft,
WebFloatPoint(rect.x + rect.width / 2, rect.y + rect.height / 2),
WebFloatPoint(rect.x + rect.width / 2, rect.y + rect.height / 2)),
1.0f, 1.0f);
web_view->MainFrameWidget()->HandleInputEvent(WebCoalescedInputEvent(event));
web_view->MainFrameWidget()->DispatchBufferedTouchEvents();
RunPendingTasks();
// rect.width/height divided by 4 because the rect is in viewport bounds and
// there is a scale of 2 set.
EXPECT_EQ(WebInputEvent::kTouchStart, test_plugin->GetLastInputEventType());
EXPECT_EQ(rect.width / 4, test_plugin->GetLastEventLocation().X());
EXPECT_EQ(rect.height / 4, test_plugin->GetLastEventLocation().Y());
}
// Verify that isRectTopmost returns false when the document is detached.
TEST_F(WebPluginContainerTest, IsRectTopmostTest) {
RegisterMockedURL("plugin_container.html");
// Must outlive |web_view_helper|.
TestPluginWebFrameClient plugin_web_frame_client;
frame_test_helpers::WebViewHelper web_view_helper;
WebViewImpl* web_view = web_view_helper.InitializeAndLoad(
base_url_ + "plugin_container.html", &plugin_web_frame_client);
EnablePlugins(web_view, WebSize(300, 300));
WebPluginContainerImpl* plugin_container_impl =
ToWebPluginContainerImpl(GetWebPluginContainer(
web_view, WebString::FromUTF8("translated-plugin")));
plugin_container_impl->SetFrameRect(IntRect(0, 0, 300, 300));
WebRect rect = plugin_container_impl->GetElement().BoundsInViewport();
EXPECT_TRUE(plugin_container_impl->IsRectTopmost(rect));
// Cause the plugin's frame to be detached.
web_view_helper.Reset();
EXPECT_FALSE(plugin_container_impl->IsRectTopmost(rect));
}
// Verify that IsRectTopmost works with odd and even dimensions.
TEST_F(WebPluginContainerTest, IsRectTopmostTestWithOddAndEvenDimensions) {
RegisterMockedURL("plugin_container.html");
// Must outlive |web_view_helper|.
TestPluginWebFrameClient plugin_web_frame_client;
frame_test_helpers::WebViewHelper web_view_helper;
WebViewImpl* web_view = web_view_helper.InitializeAndLoad(
base_url_ + "plugin_container.html", &plugin_web_frame_client);
EnablePlugins(web_view, WebSize(300, 300));
WebPluginContainerImpl* even_plugin_container_impl =
ToWebPluginContainerImpl(GetWebPluginContainer(
web_view, WebString::FromUTF8("translated-plugin")));
even_plugin_container_impl->SetFrameRect(IntRect(0, 0, 300, 300));
auto even_rect = even_plugin_container_impl->GetElement().BoundsInViewport();
EXPECT_TRUE(even_plugin_container_impl->IsRectTopmost(even_rect));
WebPluginContainerImpl* odd_plugin_container_impl =
ToWebPluginContainerImpl(GetWebPluginContainer(
web_view, WebString::FromUTF8("odd-dimensions-plugin")));
odd_plugin_container_impl->SetFrameRect(IntRect(0, 0, 300, 300));
auto odd_rect = odd_plugin_container_impl->GetElement().BoundsInViewport();
EXPECT_TRUE(odd_plugin_container_impl->IsRectTopmost(odd_rect));
}
TEST_F(WebPluginContainerTest, ClippedRectsForIframedElement) {
RegisterMockedURL("plugin_container.html");
RegisterMockedURL("plugin_containing_page.html");
// Must outlive |web_view_helper|.
TestPluginWebFrameClient plugin_web_frame_client;
frame_test_helpers::WebViewHelper web_view_helper;
WebView* web_view = web_view_helper.InitializeAndLoad(
base_url_ + "plugin_containing_page.html", &plugin_web_frame_client);
EnablePlugins(web_view, WebSize(300, 300));
WebElement plugin_element = web_view->MainFrame()
->FirstChild()
->ToWebLocalFrame()
->GetDocument()
.GetElementById("translated-plugin");
WebPluginContainerImpl* plugin_container_impl =
ToWebPluginContainerImpl(plugin_element.PluginContainer());
DCHECK(plugin_container_impl);
IntRect window_rect, clip_rect, unobscured_rect;
CalculateGeometry(plugin_container_impl, window_rect, clip_rect,
unobscured_rect);
EXPECT_EQ(IntRect(20, 220, 40, 40), window_rect);
EXPECT_EQ(IntRect(0, 0, 40, 40), clip_rect);
EXPECT_EQ(IntRect(0, 0, 40, 40), unobscured_rect);
// Cause the plugin's frame to be detached.
web_view_helper.Reset();
}
TEST_F(WebPluginContainerTest, ClippedRectsForShiftedIframedElement) {
RegisterMockedURL("plugin_hidden_before_scroll.html");
RegisterMockedURL("shifted_plugin_containing_page.html");
// Must outlive |web_view_helper|.
TestPluginWebFrameClient plugin_web_frame_client;
frame_test_helpers::WebViewHelper web_view_helper;
WebViewImpl* web_view = web_view_helper.InitializeAndLoad(
base_url_ + "shifted_plugin_containing_page.html",
&plugin_web_frame_client);
EnablePlugins(web_view, WebSize(300, 300));
UpdateAllLifecyclePhases(web_view);
WebLocalFrame* iframe =
web_view->MainFrame()->FirstChild()->ToWebLocalFrame();
WebElement plugin_element =
iframe->GetDocument().GetElementById("plugin-hidden-before-scroll");
WebPluginContainerImpl* plugin_container_impl =
ToWebPluginContainerImpl(plugin_element.PluginContainer());
DCHECK(plugin_container_impl);
IntSize plugin_size(40, 40);
IntSize iframe_size(40, 40);
IntPoint iframe_offset_in_root_frame(0, 300);
IntPoint plugin_offset_in_iframe(0, 40);
auto compute_expected_values = [=](IntSize root_document_scroll_to,
IntSize iframe_scroll_to) {
IntPoint offset_in_iframe = plugin_offset_in_iframe - iframe_scroll_to;
IntPoint offset_in_root_document =
iframe_offset_in_root_frame - root_document_scroll_to;
// window_rect is a plugin rectangle in the root frame coordinates.
IntRect expected_window_rect =
IntRect(offset_in_root_document + offset_in_iframe, plugin_size);
// unobscured_rect is the visible part of the plugin, inside the iframe.
IntRect expected_unobscured_rect(IntPoint(iframe_scroll_to), iframe_size);
expected_unobscured_rect.Intersect({plugin_offset_in_iframe, plugin_size});
expected_unobscured_rect.MoveBy(-plugin_offset_in_iframe);
// clip_rect is the visible part of the unobscured_rect, inside the
// root_frame.
IntRect expected_clip_rect = expected_unobscured_rect;
expected_clip_rect.MoveBy(expected_window_rect.Location());
expected_clip_rect.Intersect({{0, 0}, IntSize(300, 300)});
expected_clip_rect.MoveBy(-expected_window_rect.Location());
return std::make_tuple(expected_window_rect, expected_clip_rect,
expected_unobscured_rect);
};
IntSize root_document_scrolls_to[] = {IntSize(0, 0),
IntSize(0, 20),
IntSize(0, 300),
IntSize(0, 320),
IntSize(0, 340)};
IntSize iframe_scrolls_to[] = {IntSize(0, 0),
IntSize(0, 20),
IntSize(0, 40),
IntSize(0, 60),
IntSize(0, 80)};
for (auto& root_document_scroll_to : root_document_scrolls_to) {
for (auto& iframe_scroll_to : iframe_scrolls_to) {
web_view->SmoothScroll(root_document_scroll_to.Width(),
root_document_scroll_to.Height(), 0);
iframe->SetScrollOffset(iframe_scroll_to);
UpdateAllLifecyclePhases(web_view);
RunPendingTasks();
auto expected_values =
compute_expected_values(root_document_scroll_to, iframe_scroll_to);
IntRect window_rect, clip_rect, unobscured_rect;
CalculateGeometry(plugin_container_impl, window_rect, clip_rect,
unobscured_rect);
EXPECT_EQ(std::get<0>(expected_values), window_rect);
EXPECT_EQ(std::get<1>(expected_values), clip_rect);
// It seems that CalculateGeometry calculates x and y values for empty
// rectangles slightly differently, but these values are not important in
// the empty case.
if(std::get<2>(expected_values).IsEmpty())
EXPECT_TRUE(unobscured_rect.IsEmpty());
else
EXPECT_EQ(std::get<2>(expected_values), unobscured_rect);
}
}
// Cause the plugin's frame to be detached.
web_view_helper.Reset();
}
TEST_F(WebPluginContainerTest, ClippedRectsForSubpixelPositionedPlugin) {
RegisterMockedURL("plugin_container.html");
// Must outlive |web_view_helper|.
TestPluginWebFrameClient plugin_web_frame_client;
frame_test_helpers::WebViewHelper web_view_helper;
WebViewImpl* web_view = web_view_helper.InitializeAndLoad(
base_url_ + "plugin_container.html", &plugin_web_frame_client);
EnablePlugins(web_view, WebSize(300, 300));
WebElement plugin_element =
web_view->MainFrameImpl()->GetDocument().GetElementById(
"subpixel-positioned-plugin");
WebPluginContainerImpl* plugin_container_impl =
ToWebPluginContainerImpl(plugin_element.PluginContainer());
DCHECK(plugin_container_impl);
IntRect window_rect, clip_rect, unobscured_rect;
CalculateGeometry(plugin_container_impl, window_rect, clip_rect,
unobscured_rect);
EXPECT_EQ(IntRect(0, 0, 40, 40), window_rect);
EXPECT_EQ(IntRect(0, 0, 40, 40), clip_rect);
EXPECT_EQ(IntRect(0, 0, 40, 40), unobscured_rect);
// Cause the plugin's frame to be detached.
web_view_helper.Reset();
}
TEST_F(WebPluginContainerTest, TopmostAfterDetachTest) {
static WebRect topmost_rect(10, 10, 40, 40);
// Plugin that checks isRectTopmost in destroy().
class TopmostPlugin : public FakeWebPlugin {
public:
explicit TopmostPlugin(const WebPluginParams& params)
: FakeWebPlugin(params) {}
bool IsRectTopmost() { return Container()->IsRectTopmost(topmost_rect); }
void Destroy() override {
// In destroy, IsRectTopmost is no longer valid.
EXPECT_FALSE(Container()->IsRectTopmost(topmost_rect));
FakeWebPlugin::Destroy();
}
private:
~TopmostPlugin() override = default;
};
RegisterMockedURL("plugin_container.html");
// The client must outlive WebViewHelper.
CustomPluginWebFrameClient<TopmostPlugin> plugin_web_frame_client;
frame_test_helpers::WebViewHelper web_view_helper;
WebViewImpl* web_view = web_view_helper.InitializeAndLoad(
base_url_ + "plugin_container.html", &plugin_web_frame_client);
EnablePlugins(web_view, WebSize(300, 300));
WebPluginContainerImpl* plugin_container_impl =
ToWebPluginContainerImpl(GetWebPluginContainer(
web_view, WebString::FromUTF8("translated-plugin")));
plugin_container_impl->SetFrameRect(IntRect(0, 0, 300, 300));
EXPECT_TRUE(plugin_container_impl->IsRectTopmost(topmost_rect));
TopmostPlugin* test_plugin =
static_cast<TopmostPlugin*>(plugin_container_impl->Plugin());
EXPECT_TRUE(test_plugin->IsRectTopmost());
// Cause the plugin's frame to be detached.
web_view_helper.Reset();
EXPECT_FALSE(plugin_container_impl->IsRectTopmost(topmost_rect));
}
namespace {
class CompositedPlugin : public FakeWebPlugin {
public:
explicit CompositedPlugin(const WebPluginParams& params)
: FakeWebPlugin(params), layer_(cc::Layer::Create()) {}
cc::Layer* GetCcLayer() const { return layer_.get(); }
// WebPlugin
bool Initialize(WebPluginContainer* container) override {
if (!FakeWebPlugin::Initialize(container))
return false;
container->SetCcLayer(layer_.get(), false);
return true;
}
void Destroy() override {
Container()->SetCcLayer(nullptr, false);
FakeWebPlugin::Destroy();
}
private:
~CompositedPlugin() override = default;
scoped_refptr<cc::Layer> layer_;
};
} // namespace
TEST_F(WebPluginContainerTest, CompositedPluginCAP) {
ScopedCompositeAfterPaintForTest enable_cap(true);
RegisterMockedURL("plugin.html");
// Must outlive |web_view_helper|
CustomPluginWebFrameClient<CompositedPlugin> web_frame_client;
frame_test_helpers::WebViewHelper web_view_helper;
WebViewImpl* web_view = web_view_helper.InitializeAndLoad(
base_url_ + "plugin.html", &web_frame_client);
EnablePlugins(web_view, WebSize(800, 600));
WebPluginContainerImpl* container = static_cast<WebPluginContainerImpl*>(
GetWebPluginContainer(web_view, WebString::FromUTF8("plugin")));
ASSERT_TRUE(container);
const auto* plugin =
static_cast<const CompositedPlugin*>(container->Plugin());
auto paint_controller = std::make_unique<PaintController>();
paint_controller->UpdateCurrentPaintChunkProperties(
base::nullopt, PropertyTreeState::Root());
GraphicsContext graphics_context(*paint_controller);
container->Paint(graphics_context, kGlobalPaintNormalPhase,
CullRect(IntRect(10, 10, 400, 300)));
paint_controller->CommitNewDisplayItems();
const auto& display_items =
paint_controller->GetPaintArtifact().GetDisplayItemList();
ASSERT_EQ(1u, display_items.size());
ASSERT_EQ(DisplayItem::kForeignLayerPlugin, display_items[0].GetType());
const auto& foreign_layer_display_item =
static_cast<const ForeignLayerDisplayItem&>(display_items[0]);
EXPECT_EQ(plugin->GetCcLayer(), foreign_layer_display_item.GetLayer());
}
TEST_F(WebPluginContainerTest, NeedsWheelEvents) {
RegisterMockedURL("plugin_container.html");
// Must outlive |web_view_helper|
TestPluginWebFrameClient plugin_web_frame_client;
frame_test_helpers::WebViewHelper web_view_helper;
WebViewImpl* web_view = web_view_helper.InitializeAndLoad(
base_url_ + "plugin_container.html", &plugin_web_frame_client);
EnablePlugins(web_view, WebSize(300, 300));
WebElement plugin_container_one_element =
web_view->MainFrameImpl()->GetDocument().GetElementById(
WebString::FromUTF8("translated-plugin"));
plugin_container_one_element.PluginContainer()->SetWantsWheelEvents(true);
RunPendingTasks();
EXPECT_TRUE(web_view->MainFrameImpl()
->GetFrame()
->GetEventHandlerRegistry()
.HasEventHandlers(EventHandlerRegistry::kWheelEventBlocking));
}
TEST_F(WebPluginContainerTest, IFramePluginDocumentDisplayNone) {
RegisterMockedURL("test.pdf", "application/pdf");
RegisterMockedURL("iframe_pdf_display_none.html", "text/html");
TestPluginWebFrameClient plugin_web_frame_client;
frame_test_helpers::WebViewHelper web_view_helper;
WebViewImpl* web_view = web_view_helper.InitializeAndLoad(
base_url_ + "iframe_pdf_display_none.html", &plugin_web_frame_client);
UpdateAllLifecyclePhases(web_view);
WebFrame* web_iframe = web_view->MainFrame()->FirstChild();
LocalFrame* iframe = To<LocalFrame>(WebFrame::ToCoreFrame(*web_iframe));
EXPECT_TRUE(iframe->GetWebPluginContainer());
}
} // namespace blink