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

Unified Diff: chrome/browser/ui/views/drag_and_drop_interactive_uitest.cc

Issue 2478583005: Browser tests for starting a drag-and-drop out of an OOPIF. (Closed)
Patch Set: Rebasing... 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 | « chrome/browser/ui/views/OWNERS ('k') | chrome/test/BUILD.gn » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/browser/ui/views/drag_and_drop_interactive_uitest.cc
diff --git a/content/browser/web_contents/drag_and_drop_browsertest.cc b/chrome/browser/ui/views/drag_and_drop_interactive_uitest.cc
similarity index 39%
rename from content/browser/web_contents/drag_and_drop_browsertest.cc
rename to chrome/browser/ui/views/drag_and_drop_interactive_uitest.cc
index 71dfc4765bd61ba0b812ee8707e666878c31d0e8..cf9d080dbf1c12513deb1462b93d5279d6668ef0 100644
--- a/content/browser/web_contents/drag_and_drop_browsertest.cc
+++ b/chrome/browser/ui/views/drag_and_drop_interactive_uitest.cc
@@ -5,27 +5,33 @@
#include <memory>
#include <string>
+#include "base/callback.h"
#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/sequenced_task_runner.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 "base/threading/sequenced_task_runner_handle.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/interactive_test_utils.h"
+#include "chrome/test/base/ui_test_utils.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_frame_navigation_observer.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_client.h"
#include "ui/aura/client/drag_drop_delegate.h"
#include "ui/aura/client/screen_position_client.h"
#include "ui/aura/window.h"
@@ -36,7 +42,7 @@
#include "ui/gfx/geometry/rect.h"
#include "url/gurl.h"
-namespace content {
+namespace chrome {
namespace {
@@ -63,14 +69,11 @@ namespace {
// - 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)
+ explicit DragAndDropSimulator(content::WebContents* web_contents)
: web_contents_(web_contents) {}
// Simulates notification that |text| was dragged from outside of the browser,
@@ -157,30 +160,142 @@ class DragAndDropSimulator {
ui::DragDropTypes::DRAG_COPY |
ui::DragDropTypes::DRAG_LINK;
- WebContents* web_contents_;
+ content::WebContents* web_contents_;
std::unique_ptr<ui::DropTargetEvent> active_drag_event_;
DISALLOW_COPY_AND_ASSIGN(DragAndDropSimulator);
};
+// Helper for waiting until a drag-and-drop starts (e.g. in response to a
+// mouse-down + mouse-move simulated by the test).
+class DragStartWaiter : public aura::client::DragDropClient {
+ public:
+ // Starts monitoring |web_contents| for a start of a drag-and-drop.
+ // While alive, prevents a real, OS-level drag-and-drop from starting
+ // for this particular |web_contents|.
+ explicit DragStartWaiter(content::WebContents* web_contents)
+ : web_contents_(web_contents),
+ message_loop_runner_(new content::MessageLoopRunner),
+ drag_started_(false) {
+ DCHECK(web_contents_);
+
+ // Intercept calls to the old DragDropClient.
+ gfx::NativeWindow root_window =
+ web_contents_->GetContentNativeView()->GetRootWindow();
+ old_client_ = aura::client::GetDragDropClient(root_window);
+ aura::client::SetDragDropClient(root_window, this);
+ }
+
+ ~DragStartWaiter() override {
+ // Restore the original DragDropClient.
+ gfx::NativeWindow root_window =
+ web_contents_->GetContentNativeView()->GetRootWindow();
+ aura::client::SetDragDropClient(root_window, old_client_);
+ }
+
+ // Waits until we almost report a drag-and-drop start to the OS
+ // (notifying the OS will be prevented to help the test continue
+ // without entering a nested message loop).
+ //
+ // Returns true if drag and drop has indeed started (and in this
+ // case populates |text|, |html| and other parameters with data
+ // that would have been passed to the OS).
+ void WaitUntilDragStartIsIntercepted(
+ std::string* text,
+ std::string* html,
+ int* operation,
+ gfx::Point* location_inside_web_contents) {
+ message_loop_runner_->Run();
+
+ // message_loop_runner_->Quit is only called from StartDragAndDrop.
+ DCHECK(drag_started_);
+
+ *text = text_;
+ *html = html_;
+ *operation = operation_;
+ *location_inside_web_contents = location_inside_web_contents_;
+ }
+
+ // aura::client::DragDropClient overrides:
+ int StartDragAndDrop(const ui::OSExchangeData& data,
+ aura::Window* root_window,
+ aura::Window* source_window,
+ const gfx::Point& screen_location,
+ int operation,
+ ui::DragDropTypes::DragEventSource source) override {
+ DCHECK(!drag_started_);
+ if (!drag_started_) {
+ drag_started_ = true;
+ message_loop_runner_->Quit();
+
+ base::string16 text;
+ if (data.GetString(&text))
+ text_ = base::UTF16ToUTF8(text);
+ else
+ text_ = "<no text>";
+
+ GURL base_url;
+ base::string16 html;
+ if (data.GetHtml(&html, &base_url))
+ html_ = base::UTF16ToUTF8(html);
+ else
+ html_ = "<no html>";
+
+ gfx::Rect bounds =
+ web_contents_->GetContentNativeView()->GetBoundsInScreen();
+ location_inside_web_contents_ =
+ screen_location - gfx::Vector2d(bounds.x(), bounds.y());
+
+ operation_ = operation;
+ }
+
+ // Forwarding to |old_client_| is undesirable, because test cannot control
+ // next steps after a nested drag-and-drop loop is entered at the OS level
+ // (as is the case in Windows, via DoDragDrop). Instead, in the test we
+ // kind of ignore renderer's request to start drag and drop and never
+ // forward this request to the OS-specific layers.
+ return 0;
+ }
+
+ void DragCancel() override {
+ ADD_FAILURE() << "Unexpected call to DragCancel";
+ }
+
+ bool IsDragDropInProgress() override { return drag_started_; }
+
+ private:
+ content::WebContents* web_contents_;
+ scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
+ aura::client::DragDropClient* old_client_;
+
+ // Data captured during the first intercepted StartDragAndDrop call.
+ bool drag_started_;
+ std::string text_;
+ std::string html_;
+ int operation_;
+ gfx::Point location_inside_web_contents_;
+
+ DISALLOW_COPY_AND_ASSIGN(DragStartWaiter);
+};
+
// 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)
+ const content::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())) {}
+ dom_message_queue_(content::WebContents::FromRenderFrameHost(
+ target.render_frame_host())) {}
// Waits until |target| calls reportDragEvent in
- // content/test/data/drag_and_drop/event_monitoring.js with event_type
+ // chrome/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
+ // object). See chrome/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.
@@ -210,14 +325,61 @@ class DOMDragEventWaiter {
private:
std::string target_frame_name_;
std::string event_type_to_wait_for_;
- DOMMessageQueue dom_message_queue_;
+ content::DOMMessageQueue dom_message_queue_;
+
+ DISALLOW_COPY_AND_ASSIGN(DOMDragEventWaiter);
+};
+
+// Helper for verifying contents of DOM events associated with drag-and-drop.
+class DOMDragEventVerifier {
+ public:
+ DOMDragEventVerifier() {}
+
+ void set_expected_drop_effect(const std::string& value) {
+ expected_drop_effect_ = value;
+ }
+
+ void set_expected_effect_allowed(const std::string& value) {
+ expected_effect_allowed_ = value;
+ }
+
+ void set_expected_mime_types(const std::string& value) {
+ expected_mime_types_ = value;
+ }
+
+ // Returns a matcher that will match a std::string (drag event data - e.g.
+ // one returned by DOMDragEventWaiter::WaitForNextMatchingEvent) if it matches
+ // the expectations of this DOMDragEventVerifier.
+ testing::Matcher<std::string> Matches() const {
+ return testing::AllOf(
+ FieldMatches("drop_effect", expected_drop_effect_),
+ FieldMatches("effect_allowed", expected_effect_allowed_),
+ FieldMatches("mime_types", expected_mime_types_));
+ }
+
+ private:
+ static testing::Matcher<std::string> FieldMatches(
+ const std::string& field_name,
+ const std::string& expected_value) {
+ if (expected_value == "<no expectation>")
+ return testing::A<std::string>();
+
+ return testing::HasSubstr(base::StringPrintf(
+ "\"%s\":\"%s\"", field_name.c_str(), expected_value.c_str()));
+ }
+
+ std::string expected_drop_effect_ = "<no expectation>";
+ std::string expected_effect_allowed_ = "<no expectation>";
+ std::string expected_mime_types_ = "<no expectation>";
+
+ DISALLOW_COPY_AND_ASSIGN(DOMDragEventVerifier);
};
const char kTestPagePath[] = "/drag_and_drop/page.html";
} // namespace
-class DragAndDropBrowserTest : public ContentBrowserTest {
+class DragAndDropBrowserTest : public InProcessBrowserTest {
public:
DragAndDropBrowserTest(){};
@@ -226,35 +388,77 @@ class DragAndDropBrowserTest : public ContentBrowserTest {
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()));
+ drag_simulator_.reset(new DragAndDropSimulator(web_contents()));
+ }
+
+ content::RenderFrameHost* left_frame() {
+ AssertTestPageIsLoaded();
+ return GetFrameByName("left");
}
- // 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);
+ content::RenderFrameHost* right_frame() {
+ AssertTestPageIsLoaded();
+ return GetFrameByName("right");
+ }
- 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);
+ content::WebContents* web_contents() {
+ return browser()->tab_strip_model()->GetActiveWebContents();
+ }
+
+ //////////////////////
+ // Navigation helpers.
+
+ bool NavigateToTestPage(const std::string& origin_of_main_frame) {
+ GURL url =
+ embedded_test_server()->GetURL(origin_of_main_frame, kTestPagePath);
+ ui_test_utils::NavigateToURL(browser(), url);
+ return web_contents()->GetLastCommittedURL() == url;
+ }
+
+ // Navigates the left frame to |filename| (found under
+ // chrome/test/data/drag_and_drop directory).
+ bool NavigateLeftFrame(const std::string& origin,
+ const std::string& filename) {
+ AssertTestPageIsLoaded();
+ return NavigateNamedFrame("left", origin, filename);
+ }
- return NavigateToURL(shell(), target_url);
+ // Navigates the right frame to |filename| (found under
+ // chrome/test/data/drag_and_drop directory).
+ bool NavigateRightFrame(const std::string& origin,
+ const std::string& filename) {
+ AssertTestPageIsLoaded();
+ return NavigateNamedFrame("right", origin, filename);
}
+ ////////////////////////////////////////////////////////////
+ // Simulation of starting a drag-and-drop (using the mouse).
+
+ bool SimulateMouseDownAndDragStartInLeftFrame() {
+ AssertTestPageIsLoaded();
+ if (!SimulateMouseMove(kMiddleOfLeftFrame) || !SimulateMouseDown() ||
+ !SimulateMouseMove(expected_location_of_drag_start_in_left_frame()))
+ return false;
+
+ return true;
+ }
+
+ gfx::Point expected_location_of_drag_start_in_left_frame() {
+ // TODO(crbug.com/653490): The delta below should exceed kDragThresholdX and
+ // kDragThresholdY from MouseEventManager.cpp in blink. Ideally, it would
+ // come from the OS instead.
+ return kMiddleOfLeftFrame + gfx::Vector2d(10, 10);
+ }
+
+ bool SimulateMouseUp() {
+ return ui_test_utils::SendMouseEventsSync(ui_controls::LEFT,
+ ui_controls::UP);
+ }
+
+ ////////////////////////////////////////////////////////////////////
+ // Simulation of dragging from outside the browser into web contents
+ // (using DragAndDropSimulator, not simulating mouse events).
+
bool SimulateDragEnterToRightFrame(const std::string& text) {
AssertTestPageIsLoaded();
return drag_simulator_->SimulateDragEnter(kMiddleOfRightFrame, text);
@@ -265,22 +469,84 @@ class DragAndDropBrowserTest : public ContentBrowserTest {
return drag_simulator_->SimulateDrop(kMiddleOfRightFrame);
}
- RenderFrameHost* right_frame() {
- AssertTestPageIsLoaded();
- return GetFrameByName("right");
+ private:
+ bool SimulateMouseDown() {
+ return ui_test_utils::SendMouseEventsSync(ui_controls::LEFT,
+ ui_controls::DOWN);
}
- 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();
+ bool SimulateMouseMove(const gfx::Point& location_inside_web_contents) {
+ gfx::Rect bounds = web_contents()->GetContainerBounds();
+ return ui_test_utils::SendMouseMoveSync(
+ gfx::Point(bounds.x() + location_inside_web_contents.x(),
+ bounds.y() + location_inside_web_contents.y()));
+ }
+
+ bool NavigateNamedFrame(const std::string& frame_name,
+ const std::string& origin,
+ const std::string& filename) {
+ content::RenderFrameHost* frame = GetFrameByName(frame_name);
+ if (!frame)
+ return false;
+
+ std::string script;
+ int response = 0;
+
+ // Navigate the frame and wait for the load event.
+ script = base::StringPrintf(
+ "location.href = '/cross-site/%s/drag_and_drop/%s';\n"
+ "setTimeout(function() { domAutomationController.send(42); }, 0);",
+ origin.c_str(), filename.c_str());
+ content::TestFrameNavigationObserver observer(frame);
+ if (!content::ExecuteScriptAndExtractInt(frame, script, &response))
+ return false;
+ if (response != 42)
+ return false;
+ observer.Wait();
+
+ // |frame| might have been swapped-out during a cross-site navigation,
+ // therefore we need to get the current RenderFrameHost to work against
+ // going forward.
+ frame = GetFrameByName(frame_name);
+ DCHECK(frame);
+
+ // Wait until frame contents (e.g. images) have painted (which should happen
+ // in the animation frame that *starts* after the onload event - therefore
+ // we need to wait for 2 animation frames).
+ script = std::string(
+ "requestAnimationFrame(function() {\n"
+ " requestAnimationFrame(function() {\n"
+ " domAutomationController.send(43);\n"
+ " });\n"
+ "});\n");
+ if (!content::ExecuteScriptAndExtractInt(frame, script, &response))
+ return false;
+ if (response != 43)
+ return false;
+
+ return true;
+ }
+
+ content::RenderFrameHost* GetFrameByName(const std::string& name_to_find) {
+ content::RenderFrameHost* result = nullptr;
+ for (content::RenderFrameHost* rfh : web_contents()->GetAllFrames()) {
+ if (rfh->GetFrameName() == name_to_find) {
+ if (result) {
+ ADD_FAILURE() << "More than one frame named "
+ << "'" << name_to_find << "'";
+ return nullptr;
+ }
+ result = rfh;
+ }
+ }
+
+ EXPECT_TRUE(result) << "Couldn't find a frame named "
+ << "'" << name_to_find << "'";
+ return result;
}
void AssertTestPageIsLoaded() {
- ASSERT_EQ(kTestPagePath,
- shell()->web_contents()->GetLastCommittedURL().path());
+ ASSERT_EQ(kTestPagePath, web_contents()->GetLastCommittedURL().path());
}
std::unique_ptr<DragAndDropSimulator> drag_simulator_;
@@ -293,32 +559,91 @@ class DragAndDropBrowserTest : public ContentBrowserTest {
};
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"));
+ ASSERT_TRUE(NavigateToTestPage("a.com"));
+ ASSERT_TRUE(NavigateRightFrame("b.com", "drop_target.html"));
+ // Setup test expectations.
+ DOMDragEventVerifier expected_dom_event_data;
+ expected_dom_event_data.set_expected_drop_effect("none");
+ expected_dom_event_data.set_expected_effect_allowed("all");
+ expected_dom_event_data.set_expected_mime_types("text/plain");
+
+ // Drag text from outside the browser into/over the right frame.
{
- std::string dragover_event;
DOMDragEventWaiter dragover_waiter("dragover", right_frame());
ASSERT_TRUE(SimulateDragEnterToRightFrame("Dragged test text"));
+
+ std::string dragover_event;
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\""));
+ EXPECT_THAT(dragover_event, expected_dom_event_data.Matches());
}
+ // Drop into the right frame.
{
- std::string drop_event;
DOMDragEventWaiter drop_waiter("drop", right_frame());
ASSERT_TRUE(SimulateDropInRightFrame());
+
+ std::string drop_event;
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\""));
+ EXPECT_THAT(drop_event, expected_dom_event_data.Matches());
+ }
+}
+
+// The test is disabled on Ozone because of trouble with mouse simulation
+// during the test - see https://crbug.com/665042.
+#if defined(USE_OZONE)
+#define MAYBE_DragStartInFrame DISABLED_DragStartInFrame
+#else
+#define MAYBE_DragStartInFrame DragStartInFrame
+#endif
+IN_PROC_BROWSER_TEST_F(DragAndDropBrowserTest, MAYBE_DragStartInFrame) {
+ std::string frame_site = "b.com";
+ ASSERT_TRUE(NavigateToTestPage("a.com"));
+ ASSERT_TRUE(NavigateLeftFrame(frame_site, "image_source.html"));
+
+ // Setup test expectations.
+ // (dragstart event handler in image_source.html is asking for "copy" only).
+ DOMDragEventVerifier expected_dom_event_data;
+ expected_dom_event_data.set_expected_drop_effect("none");
+ expected_dom_event_data.set_expected_effect_allowed("copy");
+ expected_dom_event_data.set_expected_mime_types(
+ "Files,text/html,text/uri-list");
+
+ // Start the drag in the left frame.
+ DragStartWaiter drag_start_waiter(web_contents());
+ DOMDragEventWaiter dragstart_event_waiter("dragstart", left_frame());
+ EXPECT_TRUE(SimulateMouseDownAndDragStartInLeftFrame());
+
+ // Verify Javascript event data.
+ {
+ std::string dragstart_event;
+ EXPECT_TRUE(
+ dragstart_event_waiter.WaitForNextMatchingEvent(&dragstart_event));
+ EXPECT_THAT(dragstart_event, expected_dom_event_data.Matches());
+ }
+
+ // Verify data being passed to the OS.
+ {
+ std::string text;
+ std::string html;
+ int operation = 0;
+ gfx::Point location_inside_web_contents;
+ drag_start_waiter.WaitUntilDragStartIsIntercepted(
+ &text, &html, &operation, &location_inside_web_contents);
+ EXPECT_EQ(embedded_test_server()->GetURL(frame_site,
+ "/image_decoding/droids.jpg"),
+ text);
+ EXPECT_THAT(html,
+ testing::MatchesRegex("<img .*src=\""
+ "http://.*/image_decoding/droids.jpg"
+ "\">"));
+ EXPECT_EQ(expected_location_of_drag_start_in_left_frame(),
+ location_inside_web_contents);
+ EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, operation);
}
+
+ // Try to leave everything in a clean state.
+ SimulateMouseUp();
}
} // namespace chrome
« no previous file with comments | « chrome/browser/ui/views/OWNERS ('k') | chrome/test/BUILD.gn » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698