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

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

Issue 2549023003: Automated test for dragging between same-page, cross-site frames. (Closed)
Patch Set: Fix the pattern used in the test filter file. Created 4 years 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: chrome/browser/ui/views/drag_and_drop_interactive_uitest.cc
diff --git a/chrome/browser/ui/views/drag_and_drop_interactive_uitest.cc b/chrome/browser/ui/views/drag_and_drop_interactive_uitest.cc
index 869bd7a7f06ababbd6f62fec9ffba74da562906f..1b18839666aaf3bdd3c2653f9c0a9f2128fbd857 100644
--- a/chrome/browser/ui/views/drag_and_drop_interactive_uitest.cc
+++ b/chrome/browser/ui/views/drag_and_drop_interactive_uitest.cc
@@ -2,8 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <algorithm>
+#include <initializer_list>
#include <memory>
#include <string>
+#include <vector>
#include "base/callback.h"
#include "base/macros.h"
@@ -307,8 +310,8 @@ class DragStartWaiter : public aura::client::DragDropClient {
// content/test/data/drag_and_drop/event_monitoring.js
class DOMDragEventWaiter {
public:
- explicit DOMDragEventWaiter(const std::string& event_type_to_wait_for,
- const content::ToRenderFrameHost& target)
+ DOMDragEventWaiter(const std::string& event_type_to_wait_for,
+ 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_(content::WebContents::FromRenderFrameHost(
@@ -332,13 +335,10 @@ class DOMDragEventWaiter {
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()));
+ got_right_event_type =
+ IsExpectedEventType(candidate_event, event_type_to_wait_for_);
+ got_right_window_name =
+ IsExpectedWindowName(candidate_event, target_frame_name_);
} while (!got_right_event_type || !got_right_window_name);
if (found_event)
@@ -347,7 +347,29 @@ class DOMDragEventWaiter {
return true;
}
+ static bool IsExpectedEventType(const std::string& actual_event_body,
+ const std::string& expected_event_type) {
+ return IsExpectedPropertyValue(actual_event_body, "event_type",
+ expected_event_type);
+ }
+
+ static bool IsExpectedWindowName(const std::string& actual_event_body,
+ const std::string& expected_window_name) {
+ return IsExpectedPropertyValue(actual_event_body, "window_name",
+ expected_window_name);
+ }
+
private:
+ static bool IsExpectedPropertyValue(
+ const std::string& actual_event_body,
+ const std::string& property_name,
+ const std::string& expected_property_value) {
+ return base::MatchPattern(
+ actual_event_body,
+ base::StringPrintf("*\"%s\":\"%s\"*", property_name.c_str(),
+ expected_property_value.c_str()));
+ }
+
std::string target_frame_name_;
std::string event_type_to_wait_for_;
content::DOMMessageQueue dom_message_queue_;
@@ -412,6 +434,76 @@ class DOMDragEventVerifier {
DISALLOW_COPY_AND_ASSIGN(DOMDragEventVerifier);
};
+// Helper for monitoring event notifications from
+// content/test/data/drag_and_drop/event_monitoring.js
+// and counting how many events of a given type were received.
+class DOMDragEventCounter {
+ public:
+ explicit DOMDragEventCounter(const content::ToRenderFrameHost& target)
+ : target_frame_name_(target.render_frame_host()->GetFrameName()),
+ dom_message_queue_(content::WebContents::FromRenderFrameHost(
+ target.render_frame_host())) {}
+
+ // Resets all the accumulated event counts to zeros.
+ void Reset() {
+ StoreAccumulatedEvents();
+ received_events_.clear();
+ }
+
+ // Returns the number of events of the specified |event_type| received since
+ // construction, or since the last time Reset was called. |event_type| should
+ // be one of possible |type| property values for a DOM drag-and-drop event -
+ // e.g. "dragenter" or "dragover".
+ int GetNumberOfReceivedEvents(const std::string& event_type) {
+ std::vector<std::string> v({event_type});
+ return GetNumberOfReceivedEvents(v.begin(), v.end());
+ }
+
+ // Returns the number of events of the specified |event_types| received since
+ // construction, or since the last time Reset was called. Elements of
+ // |event_types| should be one of possible |type| property values for a DOM
+ // drag-and-drop event - e.g. "dragenter" or "dragover".
+ int GetNumberOfReceivedEvents(
+ std::initializer_list<const char*> event_types) {
+ return GetNumberOfReceivedEvents(event_types.begin(), event_types.end());
+ }
+
+ private:
+ template <typename T>
+ int GetNumberOfReceivedEvents(T event_types_begin, T event_types_end) {
+ StoreAccumulatedEvents();
+
+ auto received_event_has_matching_event_type =
+ [&event_types_begin,
+ &event_types_end](const std::string& received_event) {
+ return std::any_of(event_types_begin, event_types_end,
+ [&received_event](const std::string& event_type) {
+ return DOMDragEventWaiter::IsExpectedEventType(
+ received_event, event_type);
+ });
+ };
+
+ return std::count_if(received_events_.begin(), received_events_.end(),
+ received_event_has_matching_event_type);
+ }
+
+ void StoreAccumulatedEvents() {
+ std::string candidate_event;
+ while (dom_message_queue_.PopMessage(&candidate_event)) {
+ if (DOMDragEventWaiter::IsExpectedWindowName(candidate_event,
+ target_frame_name_)) {
+ received_events_.push_back(candidate_event);
+ }
+ }
+ }
+
+ std::string target_frame_name_;
+ content::DOMMessageQueue dom_message_queue_;
+ std::vector<std::string> received_events_;
+
+ DISALLOW_COPY_AND_ASSIGN(DOMDragEventCounter);
+};
+
const char kTestPagePath[] = "/drag_and_drop/page.html";
} // namespace
@@ -425,6 +517,10 @@ class DragAndDropBrowserTest : public InProcessBrowserTest,
void DragImageBetweenFrames_Step2(DragImageBetweenFrames_TestState*);
void DragImageBetweenFrames_Step3(DragImageBetweenFrames_TestState*);
+ struct CrossSiteDrag_TestState;
+ void CrossSiteDrag_Step2(CrossSiteDrag_TestState*);
+ void CrossSiteDrag_Step3(CrossSiteDrag_TestState*);
+
protected:
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
@@ -730,6 +826,8 @@ struct DragAndDropBrowserTest::DragImageBetweenFrames_TestState {
std::unique_ptr<DOMDragEventWaiter> dragstart_event_waiter;
std::unique_ptr<DOMDragEventWaiter> drop_event_waiter;
std::unique_ptr<DOMDragEventWaiter> dragend_event_waiter;
+ std::unique_ptr<DOMDragEventCounter> left_frame_events_counter;
+ std::unique_ptr<DOMDragEventCounter> right_frame_events_counter;
};
// Scenario: drag an image from the left into the right frame.
@@ -745,6 +843,9 @@ IN_PROC_BROWSER_TEST_P(DragAndDropBrowserTest, MAYBE_DragImageBetweenFrames) {
// Setup test expectations.
DragAndDropBrowserTest::DragImageBetweenFrames_TestState state;
+ state.left_frame_events_counter.reset(new DOMDragEventCounter(left_frame()));
+ state.right_frame_events_counter.reset(
+ new DOMDragEventCounter(right_frame()));
state.expected_dom_event_data.set_expected_client_position("(55, 50)");
state.expected_dom_event_data.set_expected_drop_effect("none");
// (dragstart event handler in image_source.html is asking for "copy" only).
@@ -778,6 +879,18 @@ void DragAndDropBrowserTest::DragImageBetweenFrames_Step2(
EXPECT_TRUE(state->dragstart_event_waiter->WaitForNextMatchingEvent(
&dragstart_event));
state->dragstart_event_waiter.reset();
+
+ // Only a single "dragstart" should have fired in the left frame since the
+ // start of the test. We also allow any number of "dragover" events.
+ EXPECT_EQ(1, state->left_frame_events_counter->GetNumberOfReceivedEvents(
+ "dragstart"));
+ EXPECT_EQ(0, state->left_frame_events_counter->GetNumberOfReceivedEvents(
+ {"dragleave", "dragenter", "drop", "dragend"}));
+
+ // No events should have fired in the right frame yet.
+ EXPECT_EQ(0, state->right_frame_events_counter->GetNumberOfReceivedEvents(
+ {"dragstart", "dragleave", "dragenter", "dragover", "drop",
+ "dragend"}));
}
// While dragging, move mouse within the left frame.
@@ -789,6 +902,8 @@ void DragAndDropBrowserTest::DragImageBetweenFrames_Step2(
{
DOMDragEventWaiter dragleave_event_waiter("dragleave", left_frame());
DOMDragEventWaiter dragenter_event_waiter("dragenter", right_frame());
+ state->left_frame_events_counter->Reset();
+ state->right_frame_events_counter->Reset();
ASSERT_TRUE(SimulateMouseMoveToRightFrame());
{ // Verify dragleave DOM event.
@@ -841,10 +956,28 @@ void DragAndDropBrowserTest::DragImageBetweenFrames_Step2(
}
}
+ // Only a single "dragleave" should have fired in the left frame since the
+ // last checkpoint. We also allow any number of "dragover" events.
+ EXPECT_EQ(1, state->left_frame_events_counter->GetNumberOfReceivedEvents(
+ "dragleave"));
+ EXPECT_EQ(0, state->left_frame_events_counter->GetNumberOfReceivedEvents(
+ {"dragstart", "dragenter", "drop", "dragend"}));
+
+ // A single "dragenter" + at least one "dragover" event should have fired in
+ // the right frame since the last checkpoint.
+ EXPECT_EQ(1, state->right_frame_events_counter->GetNumberOfReceivedEvents(
+ "dragenter"));
+ EXPECT_LE(1, state->right_frame_events_counter->GetNumberOfReceivedEvents(
+ "dragover"));
ncarter (slow) 2016/12/09 18:28:39 Is this value just inconsistent across platforms,
Łukasz Anforowicz 2016/12/09 22:33:27 It is inconsistent across platforms. For example
+ EXPECT_EQ(0, state->right_frame_events_counter->GetNumberOfReceivedEvents(
+ {"dragstart", "dragleave", "drop", "dragend"}));
+
// Release the mouse button to end the drag.
state->drop_event_waiter.reset(new DOMDragEventWaiter("drop", right_frame()));
state->dragend_event_waiter.reset(
new DOMDragEventWaiter("dragend", left_frame()));
+ state->left_frame_events_counter->Reset();
+ state->right_frame_events_counter->Reset();
SimulateMouseUp();
// The test will continue in DragImageBetweenFrames_Step3.
}
@@ -880,6 +1013,130 @@ void DragAndDropBrowserTest::DragImageBetweenFrames_Step3(
state->dragend_event_waiter.reset();
EXPECT_THAT(dragend_event, state->expected_dom_event_data.Matches());
}
+
+ // Only a single "dragend" should have fired in the left frame since the last
+ // checkpoint.
+ EXPECT_EQ(1, state->left_frame_events_counter->GetNumberOfReceivedEvents(
+ "dragend"));
+ EXPECT_EQ(0,
+ state->left_frame_events_counter->GetNumberOfReceivedEvents(
+ {"dragstart", "dragleave", "dragenter", "dragover", "drop"}));
+
+ // A single "drop" + possibly some "dragover" events should have fired in the
+ // right frame since the last checkpoint.
+ EXPECT_EQ(
+ 1, state->right_frame_events_counter->GetNumberOfReceivedEvents("drop"));
+ EXPECT_EQ(0, state->right_frame_events_counter->GetNumberOfReceivedEvents(
+ {"dragstart", "dragleave", "dragenter", "dragend"}));
+}
+
+// There is no known way to execute test-controlled tasks during
+// a drag-and-drop loop run by Windows OS.
+#if defined(OS_WIN)
+#define MAYBE_CrossSiteDrag DISABLED_CrossSiteDrag
+#else
+#define MAYBE_CrossSiteDrag CrossSiteDrag
+#endif
+
+// Data that needs to be shared across multiple test steps below
+// (i.e. across CrossSiteDrag_Step2 and CrossSiteDrag_Step3).
+struct DragAndDropBrowserTest::CrossSiteDrag_TestState {
+ std::unique_ptr<DOMDragEventWaiter> dragend_event_waiter;
+ std::unique_ptr<DOMDragEventCounter> left_frame_events_counter;
+ std::unique_ptr<DOMDragEventCounter> right_frame_events_counter;
+};
+
+// Scenario: drag an image from the left into the right frame when the
+// left-vs-right frames are cross-site. This is a regression test for
+// https://crbug.com/59081.
+//
+// Test coverage: absence of dragenter, dragover, drop DOM events
+// + presence of dragstart, dragleave and dragend.
+IN_PROC_BROWSER_TEST_P(DragAndDropBrowserTest, MAYBE_CrossSiteDrag) {
+ std::string left_frame_site = "c.com"; // Always cross-site VS main frame.
+ std::string right_frame_site = use_cross_site_subframe() ? "b.com" : "a.com";
+ ASSERT_TRUE(NavigateToTestPage("a.com"));
+ ASSERT_TRUE(NavigateLeftFrame(left_frame_site, "image_source.html"));
+ ASSERT_TRUE(NavigateRightFrame(right_frame_site, "drop_target.html"));
+
+ // Setup test expectations.
+ DragAndDropBrowserTest::CrossSiteDrag_TestState state;
+ state.left_frame_events_counter.reset(new DOMDragEventCounter(left_frame()));
+ state.right_frame_events_counter.reset(
+ new DOMDragEventCounter(right_frame()));
+
+ // Start the drag in the left frame.
+ DragStartWaiter drag_start_waiter(web_contents());
+ drag_start_waiter.PostTaskWhenDragStarts(
+ base::Bind(&DragAndDropBrowserTest::CrossSiteDrag_Step2,
+ base::Unretained(this), base::Unretained(&state)));
+ EXPECT_TRUE(SimulateMouseDownAndDragStartInLeftFrame());
+
+ // The next step of the test (CrossSiteDrag_Step2) runs inside the
+ // nested drag-and-drop message loop - the call below won't return until the
+ // drag-and-drop has already ended.
+ drag_start_waiter.WaitUntilDragStart(nullptr, nullptr, nullptr, nullptr);
+
+ CrossSiteDrag_Step3(&state);
+}
+
+void DragAndDropBrowserTest::CrossSiteDrag_Step2(
+ DragAndDropBrowserTest::CrossSiteDrag_TestState* state) {
+ // While "dragleave" and "drop" events are not expected in this test, we
+ // simulate extra mouse operations for consistency with
+ // DragImageBetweenFrames_Step2.
+ ASSERT_TRUE(SimulateMouseMoveToLeftFrame());
+ for (int i = 0; i < 3; i++) {
+ content::DOMMessageQueue dom_message_queue(web_contents());
+ ASSERT_TRUE(SimulateMouseMoveToRightFrame());
+
+ // No events are expected from the right frame, so we can't wait for a
+ // dragover event here. Still - we do want to wait until the right frame
+ // has had a chance to process any previous browser IPCs, so that in case
+ // there *is* a bug and a dragover event *does* happen, we won't terminate
+ // the test before the event has had a chance to be reported back to the
+ // browser.
+ std::string expected_response = base::StringPrintf("\"i%d\"", i);
+ right_frame()->ExecuteJavaScriptWithUserGestureForTests(base::UTF8ToUTF16(
ncarter (slow) 2016/12/09 18:28:39 It's unfortunate that we can't use ExecuteScript i
Łukasz Anforowicz 2016/12/09 22:33:27 We could either relax ExecuteScriptAndExtractFoo t
+ base::StringPrintf("domAutomationController.setAutomationId(0);\n"
+ "domAutomationController.send(%s);\n",
+ expected_response.c_str())));
+
+ // Wait until our response comes back (it might be mixed with responses
+ // carrying events that are sent by event_monitoring.js).
+ std::string actual_response;
+ do {
+ ASSERT_TRUE(dom_message_queue.WaitForMessage(&actual_response));
+ } while (actual_response != expected_response);
+ }
+
+ // Release the mouse button to end the drag.
+ state->dragend_event_waiter.reset(
+ new DOMDragEventWaiter("dragend", left_frame()));
+ SimulateMouseUp();
+ // The test will continue in DragImageBetweenFrames_Step3.
+}
+
+void DragAndDropBrowserTest::CrossSiteDrag_Step3(
+ DragAndDropBrowserTest::CrossSiteDrag_TestState* state) {
+ EXPECT_TRUE(state->dragend_event_waiter->WaitForNextMatchingEvent(nullptr));
+
+ // Since the start of the test the left frame should have seen a single
+ // "dragstart",
+ // and a "dragend" event (and possibly a "dragleave" and some "dragover"
+ // events).
+ EXPECT_EQ(1, state->left_frame_events_counter->GetNumberOfReceivedEvents(
+ "dragstart"));
+ EXPECT_EQ(1, state->left_frame_events_counter->GetNumberOfReceivedEvents(
+ "dragend"));
+ EXPECT_EQ(
+ 0, state->left_frame_events_counter->GetNumberOfReceivedEvents("drop"));
+
+ // No events should have fired in the right frame, because it is cross-site
+ // from the source of the drag. This is the essence of this test.
+ EXPECT_EQ(0, state->right_frame_events_counter->GetNumberOfReceivedEvents(
+ {"dragstart", "dragleave", "dragenter", "dragover", "drop",
+ "dragend"}));
}
INSTANTIATE_TEST_CASE_P(
« 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