Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1392)

Unified Diff: content/browser/web_contents/drag_and_drop_browsertest.cc

Issue 2453693003: Browser tests for OOPIF support for drag-n-drop. (Closed)
Patch Set: Addressed CR feedback from nick@. Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | content/public/test/browser_test_utils.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: content/browser/web_contents/drag_and_drop_browsertest.cc
diff --git a/content/browser/web_contents/drag_and_drop_browsertest.cc b/content/browser/web_contents/drag_and_drop_browsertest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..71dfc4765bd61ba0b812ee8707e666878c31d0e8
--- /dev/null
+++ b/content/browser/web_contents/drag_and_drop_browsertest.cc
@@ -0,0 +1,324 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "base/strings/pattern.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "content/browser/frame_host/frame_tree.h"
+#include "content/browser/frame_host/frame_tree_node.h"
+#include "content/browser/frame_host/render_frame_host_impl.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/content_browser_test.h"
+#include "content/public/test/content_browser_test_utils.h"
+#include "content/public/test/test_utils.h"
+#include "content/shell/browser/shell.h"
+#include "net/base/escape.h"
+#include "net/dns/mock_host_resolver.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/aura/client/drag_drop_delegate.h"
+#include "ui/aura/client/screen_position_client.h"
+#include "ui/aura/window.h"
+#include "ui/base/dragdrop/drag_drop_types.h"
+#include "ui/base/dragdrop/drop_target_event.h"
+#include "ui/base/dragdrop/os_exchange_data.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/rect.h"
+#include "url/gurl.h"
+
+namespace content {
+
+namespace {
+
+// TODO(lukasza): Support testing on non-Aura platforms (i.e. Android + Mac?).
+//
+// Notes for the TODO above:
+//
+// - Why inject/simulate drag-and-drop events at the aura::Window* level.
+//
+// - It seems better to inject into UI libraries to cover code *inside* these
+// libraries. This might complicate simulation a little bit (i.e. picking
+// the right aura::Window and/or aura::client::DragDropDelegate to target),
+// but otherwise important bits of code wouldn't get test coverage (i.e.
+// directly injecting into RenderViewHost->DragTargetDragEnter seems wrong).
+//
+// - In theory, we could introduce WebContentsImpl::DragTargetDragEnter (to be
+// used by all UI platforms - so reused by web_contents_view_android.cc,
+// web_contents_view_aura.cc, web_drag_dest_mac.mm), but it feels wrong - UI
+// libraries should already know which widget is the target of the event and
+// so should be able to talk directly to the right widget (i.e. WebContents
+// should not be responsible for mapping coordinates to a widget - this is
+// the job of the UI library).
+//
+// - Unknowns:
+//
+// - Will this work for WebView and Plugin testing.
+//
+// - Will this work for simulating dragging from WebContents into outside of
+// the browser (without leaving OS drag&drop machinery in a weird state).
+
+// Test helper for simulating drag and drop happening in WebContents.
+class DragAndDropSimulator {
+ public:
+ explicit DragAndDropSimulator(WebContents* web_contents)
+ : web_contents_(web_contents) {}
+
+ // Simulates notification that |text| was dragged from outside of the browser,
+ // into the specified |location| inside |web_contents|.
+ // |location| is relative to |web_contents|.
+ // Returns true upon success.
+ bool SimulateDragEnter(gfx::Point location, const std::string& text) {
+ ui::OSExchangeData data;
+ data.SetString(base::UTF8ToUTF16(text));
+ return SimulateDragEnter(location, data);
+ }
+
+ // Simulates dropping of the drag-and-dropped item.
+ // SimulateDragEnter needs to be called first.
+ // Returns true upon success.
+ bool SimulateDrop(gfx::Point location) {
+ if (!active_drag_event_) {
+ ADD_FAILURE() << "Cannot drop a drag that hasn't started yet.";
+ return false;
+ }
+
+ aura::client::DragDropDelegate* delegate = GetDragDropDelegate();
+ if (!delegate)
+ return false;
+
+ gfx::Point event_location;
+ gfx::Point event_root_location;
+ CalculateEventLocations(location, &event_location, &event_root_location);
+ active_drag_event_->set_location(event_location);
+ active_drag_event_->set_root_location(event_root_location);
+
+ delegate->OnDragUpdated(*active_drag_event_);
+ delegate->OnPerformDrop(*active_drag_event_);
+ return true;
+ }
+
+ private:
+ bool SimulateDragEnter(gfx::Point location, const ui::OSExchangeData& data) {
+ if (active_drag_event_) {
+ ADD_FAILURE() << "Cannot start a new drag when old one hasn't ended yet.";
+ return false;
+ }
+
+ aura::client::DragDropDelegate* delegate = GetDragDropDelegate();
+ if (!delegate)
+ return false;
+
+ gfx::Point event_location;
+ gfx::Point event_root_location;
+ CalculateEventLocations(location, &event_location, &event_root_location);
+ active_drag_event_.reset(new ui::DropTargetEvent(
+ data, event_location, event_root_location, kDefaultSourceOperations));
+
+ delegate->OnDragEntered(*active_drag_event_);
+ delegate->OnDragUpdated(*active_drag_event_);
+ return true;
+ }
+
+ aura::client::DragDropDelegate* GetDragDropDelegate() {
+ gfx::NativeView view = web_contents_->GetContentNativeView();
+ aura::client::DragDropDelegate* delegate =
+ aura::client::GetDragDropDelegate(view);
+ EXPECT_TRUE(delegate) << "Expecting WebContents to have DragDropDelegate";
+ return delegate;
+ }
+
+ void CalculateEventLocations(gfx::Point web_contents_relative_location,
+ gfx::Point* out_event_location,
+ gfx::Point* out_event_root_location) {
+ gfx::NativeView view = web_contents_->GetNativeView();
+
+ *out_event_location = web_contents_relative_location;
+
+ gfx::Point root_location = web_contents_relative_location;
+ aura::Window::ConvertPointToTarget(view, view->GetRootWindow(),
+ &root_location);
+ *out_event_location = root_location;
+ }
+
+ // These are ui::DropTargetEvent::source_operations_ being sent when manually
+ // trying out drag&drop of an image file from Nemo (Ubuntu's file explorer)
+ // into a content_shell.
+ static constexpr int kDefaultSourceOperations = ui::DragDropTypes::DRAG_MOVE |
+ ui::DragDropTypes::DRAG_COPY |
+ ui::DragDropTypes::DRAG_LINK;
+
+ WebContents* web_contents_;
+ std::unique_ptr<ui::DropTargetEvent> active_drag_event_;
+
+ DISALLOW_COPY_AND_ASSIGN(DragAndDropSimulator);
+};
+
+// Helper for waiting for notifications from
+// content/test/data/drag_and_drop/event_monitoring.js
+class DOMDragEventWaiter {
+ public:
+ explicit DOMDragEventWaiter(const std::string& event_type_to_wait_for,
+ const ToRenderFrameHost& target)
+ : target_frame_name_(target.render_frame_host()->GetFrameName()),
+ event_type_to_wait_for_(event_type_to_wait_for),
+ dom_message_queue_(
+ WebContents::FromRenderFrameHost(target.render_frame_host())) {}
+
+ // Waits until |target| calls reportDragEvent in
+ // content/test/data/drag_and_drop/event_monitoring.js with event_type
+ // property set to |event_type_to_wait_for|. (|target| and
+ // |event_type_to_wait_for| are passed to the constructor).
+ //
+ // Returns the event details via |found_event| (in form of a JSON-encoded
+ // object). See content/test/data/drag_and_drop/event_monitoring.js for keys
+ // and properties that |found_event| is expected to have.
+ //
+ // Returns true upon success. It is okay if |response| is null.
+ bool WaitForNextMatchingEvent(std::string* found_event) WARN_UNUSED_RESULT {
+ std::string candidate_event;
+ bool got_right_event_type = false;
+ bool got_right_window_name = false;
+ do {
+ if (!dom_message_queue_.WaitForMessage(&candidate_event))
+ return false;
+
+ got_right_event_type = base::MatchPattern(
+ candidate_event, base::StringPrintf("*\"event_type\":\"%s\"*",
+ event_type_to_wait_for_.c_str()));
+
+ got_right_window_name = base::MatchPattern(
+ candidate_event, base::StringPrintf("*\"window_name\":\"%s\"*",
+ target_frame_name_.c_str()));
+ } while (!got_right_event_type || !got_right_window_name);
+
+ if (found_event)
+ *found_event = candidate_event;
+
+ return true;
+ }
+
+ private:
+ std::string target_frame_name_;
+ std::string event_type_to_wait_for_;
+ DOMMessageQueue dom_message_queue_;
+};
+
+const char kTestPagePath[] = "/drag_and_drop/page.html";
+
+} // namespace
+
+class DragAndDropBrowserTest : public ContentBrowserTest {
+ public:
+ DragAndDropBrowserTest(){};
+
+ protected:
+ void SetUpOnMainThread() override {
+ host_resolver()->AddRule("*", "127.0.0.1");
+ ASSERT_TRUE(embedded_test_server()->Start());
+ content::SetupCrossSiteRedirector(embedded_test_server());
+ drag_simulator_.reset(new DragAndDropSimulator(shell()->web_contents()));
+ }
+
+ // Navigates to content/test/data/drag_and_drop/page.html, with page origin
+ // and frame contents being controlled by the parameters.
+ bool NavigateToTestPage(const std::string& main_origin,
+ const std::string& left_frame,
+ const std::string& right_frame) {
+ GURL base_url = embedded_test_server()->GetURL(main_origin, kTestPagePath);
+
+ std::string left_arg;
+ if (!left_frame.empty()) {
+ left_arg = net::EscapeQueryParamValue(
+ std::string("/cross-site/") + left_frame, true);
+ }
+ std::string right_arg;
+ if (!right_frame.empty()) {
+ right_arg = net::EscapeQueryParamValue(
+ std::string("/cross-site/") + right_frame, true);
+ }
+ std::string query = base::StringPrintf("left=%s&right=%s", left_arg.c_str(),
+ right_arg.c_str());
+ GURL::Replacements query_replacement;
+ query_replacement.SetQueryStr(query);
+ GURL target_url = base_url.ReplaceComponents(query_replacement);
+
+ return NavigateToURL(shell(), target_url);
+ }
+
+ bool SimulateDragEnterToRightFrame(const std::string& text) {
+ AssertTestPageIsLoaded();
+ return drag_simulator_->SimulateDragEnter(kMiddleOfRightFrame, text);
+ }
+
+ bool SimulateDropInRightFrame() {
+ AssertTestPageIsLoaded();
+ return drag_simulator_->SimulateDrop(kMiddleOfRightFrame);
+ }
+
+ RenderFrameHost* right_frame() {
+ AssertTestPageIsLoaded();
+ return GetFrameByName("right");
+ }
+
+ private:
+ RenderFrameHost* GetFrameByName(const std::string& name_to_find) {
+ WebContentsImpl* web_contents =
+ static_cast<WebContentsImpl*>(shell()->web_contents());
+ FrameTree* frame_tree = web_contents->GetFrameTree();
+ return frame_tree->FindByName(name_to_find)->current_frame_host();
+ }
+
+ void AssertTestPageIsLoaded() {
+ ASSERT_EQ(kTestPagePath,
+ shell()->web_contents()->GetLastCommittedURL().path());
+ }
+
+ std::unique_ptr<DragAndDropSimulator> drag_simulator_;
+
+ // Constants with coordinates within content/test/data/drag_and_drop/page.html
+ const gfx::Point kMiddleOfLeftFrame = gfx::Point(200, 200);
+ const gfx::Point kMiddleOfRightFrame = gfx::Point(400, 200);
+
+ DISALLOW_COPY_AND_ASSIGN(DragAndDropBrowserTest);
+};
+
+IN_PROC_BROWSER_TEST_F(DragAndDropBrowserTest, DropTextFromOutside) {
+ ASSERT_TRUE(NavigateToTestPage("a.com",
+ "", // Left frame is unused by this test.
+ "c.com/drag_and_drop/drop_target.html"));
+
+ {
+ std::string dragover_event;
+ DOMDragEventWaiter dragover_waiter("dragover", right_frame());
+ ASSERT_TRUE(SimulateDragEnterToRightFrame("Dragged test text"));
+ ASSERT_TRUE(dragover_waiter.WaitForNextMatchingEvent(&dragover_event));
+ EXPECT_THAT(dragover_event, testing::HasSubstr("\"drop_effect\":\"none\""));
+ EXPECT_THAT(dragover_event,
+ testing::HasSubstr("\"effect_allowed\":\"all\""));
+ EXPECT_THAT(dragover_event,
+ testing::HasSubstr("\"mime_types\":\"text/plain\""));
+ }
+
+ {
+ std::string drop_event;
+ DOMDragEventWaiter drop_waiter("drop", right_frame());
+ ASSERT_TRUE(SimulateDropInRightFrame());
+ ASSERT_TRUE(drop_waiter.WaitForNextMatchingEvent(&drop_event));
+ EXPECT_THAT(drop_event, testing::HasSubstr("\"drop_effect\":\"none\""));
+ EXPECT_THAT(drop_event, testing::HasSubstr("\"effect_allowed\":\"all\""));
+ EXPECT_THAT(drop_event,
+ testing::HasSubstr("\"mime_types\":\"text/plain\""));
+ }
+}
+
+} // namespace chrome
« no previous file with comments | « no previous file | content/public/test/browser_test_utils.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698