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

Side by Side Diff: content/browser/web_contents/drag_and_drop_browsertest.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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <memory>
6 #include <string>
7
8 #include "base/macros.h"
9 #include "base/strings/pattern.h"
10 #include "base/strings/string_piece.h"
11 #include "base/strings/stringprintf.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "content/browser/frame_host/frame_tree.h"
14 #include "content/browser/frame_host/frame_tree_node.h"
15 #include "content/browser/frame_host/render_frame_host_impl.h"
16 #include "content/browser/web_contents/web_contents_impl.h"
17 #include "content/public/browser/render_frame_host.h"
18 #include "content/public/browser/web_contents.h"
19 #include "content/public/test/browser_test_utils.h"
20 #include "content/public/test/content_browser_test.h"
21 #include "content/public/test/content_browser_test_utils.h"
22 #include "content/public/test/test_utils.h"
23 #include "content/shell/browser/shell.h"
24 #include "net/base/escape.h"
25 #include "net/dns/mock_host_resolver.h"
26 #include "net/test/embedded_test_server/embedded_test_server.h"
27 #include "testing/gmock/include/gmock/gmock.h"
28 #include "testing/gtest/include/gtest/gtest.h"
29 #include "ui/aura/client/drag_drop_delegate.h"
30 #include "ui/aura/client/screen_position_client.h"
31 #include "ui/aura/window.h"
32 #include "ui/base/dragdrop/drag_drop_types.h"
33 #include "ui/base/dragdrop/drop_target_event.h"
34 #include "ui/base/dragdrop/os_exchange_data.h"
35 #include "ui/gfx/geometry/point.h"
36 #include "ui/gfx/geometry/rect.h"
37 #include "url/gurl.h"
38
39 namespace content {
40
41 namespace {
42
43 // TODO(lukasza): Support testing on non-Aura platforms (i.e. Android + Mac?).
44 //
45 // Notes for the TODO above:
46 //
47 // - Why inject/simulate drag-and-drop events at the aura::Window* level.
48 //
49 // - It seems better to inject into UI libraries to cover code *inside* these
50 // libraries. This might complicate simulation a little bit (i.e. picking
51 // the right aura::Window and/or aura::client::DragDropDelegate to target),
52 // but otherwise important bits of code wouldn't get test coverage (i.e.
53 // directly injecting into RenderViewHost->DragTargetDragEnter seems wrong).
54 //
55 // - In theory, we could introduce WebContentsImpl::DragTargetDragEnter (to be
56 // used by all UI platforms - so reused by web_contents_view_android.cc,
57 // web_contents_view_aura.cc, web_drag_dest_mac.mm), but it feels wrong - UI
58 // libraries should already know which widget is the target of the event and
59 // so should be able to talk directly to the right widget (i.e. WebContents
60 // should not be responsible for mapping coordinates to a widget - this is
61 // the job of the UI library).
62 //
63 // - Unknowns:
64 //
65 // - Will this work for WebView and Plugin testing.
66 //
67 // - Will this work for simulating dragging from WebContents into outside of
68 // the browser (without leaving OS drag&drop machinery in a weird state).
69
70 // Test helper for simulating drag and drop happening in WebContents.
71 class DragAndDropSimulator {
72 public:
73 explicit DragAndDropSimulator(WebContents* web_contents)
74 : web_contents_(web_contents) {}
75
76 // Simulates notification that |text| was dragged from outside of the browser,
77 // into the specified |location| inside |web_contents|.
78 // |location| is relative to |web_contents|.
79 // Returns true upon success.
80 bool SimulateDragEnter(gfx::Point location, const std::string& text) {
81 ui::OSExchangeData data;
82 data.SetString(base::UTF8ToUTF16(text));
83 return SimulateDragEnter(location, data);
84 }
85
86 // Simulates dropping of the drag-and-dropped item.
87 // SimulateDragEnter needs to be called first.
88 // Returns true upon success.
89 bool SimulateDrop(gfx::Point location) {
90 if (!active_drag_event_) {
91 ADD_FAILURE() << "Cannot drop a drag that hasn't started yet.";
92 return false;
93 }
94
95 aura::client::DragDropDelegate* delegate = GetDragDropDelegate();
96 if (!delegate)
97 return false;
98
99 gfx::Point event_location;
100 gfx::Point event_root_location;
101 CalculateEventLocations(location, &event_location, &event_root_location);
102 active_drag_event_->set_location(event_location);
103 active_drag_event_->set_root_location(event_root_location);
104
105 delegate->OnDragUpdated(*active_drag_event_);
106 delegate->OnPerformDrop(*active_drag_event_);
107 return true;
108 }
109
110 private:
111 bool SimulateDragEnter(gfx::Point location, const ui::OSExchangeData& data) {
112 if (active_drag_event_) {
113 ADD_FAILURE() << "Cannot start a new drag when old one hasn't ended yet.";
114 return false;
115 }
116
117 aura::client::DragDropDelegate* delegate = GetDragDropDelegate();
118 if (!delegate)
119 return false;
120
121 gfx::Point event_location;
122 gfx::Point event_root_location;
123 CalculateEventLocations(location, &event_location, &event_root_location);
124 active_drag_event_.reset(new ui::DropTargetEvent(
125 data, event_location, event_root_location, kDefaultSourceOperations));
126
127 delegate->OnDragEntered(*active_drag_event_);
128 delegate->OnDragUpdated(*active_drag_event_);
129 return true;
130 }
131
132 aura::client::DragDropDelegate* GetDragDropDelegate() {
133 gfx::NativeView view = web_contents_->GetContentNativeView();
134 aura::client::DragDropDelegate* delegate =
135 aura::client::GetDragDropDelegate(view);
136 EXPECT_TRUE(delegate) << "Expecting WebContents to have DragDropDelegate";
137 return delegate;
138 }
139
140 void CalculateEventLocations(gfx::Point web_contents_relative_location,
141 gfx::Point* out_event_location,
142 gfx::Point* out_event_root_location) {
143 gfx::NativeView view = web_contents_->GetNativeView();
144
145 *out_event_location = web_contents_relative_location;
146
147 gfx::Point root_location = web_contents_relative_location;
148 aura::Window::ConvertPointToTarget(view, view->GetRootWindow(),
149 &root_location);
150 *out_event_location = root_location;
151 }
152
153 // These are ui::DropTargetEvent::source_operations_ being sent when manually
154 // trying out drag&drop of an image file from Nemo (Ubuntu's file explorer)
155 // into a content_shell.
156 static constexpr int kDefaultSourceOperations = ui::DragDropTypes::DRAG_MOVE |
157 ui::DragDropTypes::DRAG_COPY |
158 ui::DragDropTypes::DRAG_LINK;
159
160 WebContents* web_contents_;
161 std::unique_ptr<ui::DropTargetEvent> active_drag_event_;
162
163 DISALLOW_COPY_AND_ASSIGN(DragAndDropSimulator);
164 };
165
166 // Helper for waiting for notifications from
167 // content/test/data/drag_and_drop/event_monitoring.js
168 class DOMDragEventWaiter {
169 public:
170 explicit DOMDragEventWaiter(const std::string& event_type_to_wait_for,
171 const ToRenderFrameHost& target)
172 : target_frame_name_(target.render_frame_host()->GetFrameName()),
173 event_type_to_wait_for_(event_type_to_wait_for),
174 dom_message_queue_(
175 WebContents::FromRenderFrameHost(target.render_frame_host())) {}
176
177 // Waits until |target| calls reportDragEvent in
178 // content/test/data/drag_and_drop/event_monitoring.js with event_type
179 // property set to |event_type_to_wait_for|. (|target| and
180 // |event_type_to_wait_for| are passed to the constructor).
181 //
182 // Returns the event details via |found_event| (in form of a JSON-encoded
183 // object). See content/test/data/drag_and_drop/event_monitoring.js for keys
184 // and properties that |found_event| is expected to have.
185 //
186 // Returns true upon success. It is okay if |response| is null.
187 bool WaitForNextMatchingEvent(std::string* found_event) WARN_UNUSED_RESULT {
188 std::string candidate_event;
189 bool got_right_event_type = false;
190 bool got_right_window_name = false;
191 do {
192 if (!dom_message_queue_.WaitForMessage(&candidate_event))
193 return false;
194
195 got_right_event_type = base::MatchPattern(
196 candidate_event, base::StringPrintf("*\"event_type\":\"%s\"*",
197 event_type_to_wait_for_.c_str()));
198
199 got_right_window_name = base::MatchPattern(
200 candidate_event, base::StringPrintf("*\"window_name\":\"%s\"*",
201 target_frame_name_.c_str()));
202 } while (!got_right_event_type || !got_right_window_name);
203
204 if (found_event)
205 *found_event = candidate_event;
206
207 return true;
208 }
209
210 private:
211 std::string target_frame_name_;
212 std::string event_type_to_wait_for_;
213 DOMMessageQueue dom_message_queue_;
214 };
215
216 const char kTestPagePath[] = "/drag_and_drop/page.html";
217
218 } // namespace
219
220 class DragAndDropBrowserTest : public ContentBrowserTest {
221 public:
222 DragAndDropBrowserTest(){};
223
224 protected:
225 void SetUpOnMainThread() override {
226 host_resolver()->AddRule("*", "127.0.0.1");
227 ASSERT_TRUE(embedded_test_server()->Start());
228 content::SetupCrossSiteRedirector(embedded_test_server());
229 drag_simulator_.reset(new DragAndDropSimulator(shell()->web_contents()));
230 }
231
232 // Navigates to content/test/data/drag_and_drop/page.html, with page origin
233 // and frame contents being controlled by the parameters.
234 bool NavigateToTestPage(const std::string& main_origin,
235 const std::string& left_frame,
236 const std::string& right_frame) {
237 GURL base_url = embedded_test_server()->GetURL(main_origin, kTestPagePath);
238
239 std::string left_arg;
240 if (!left_frame.empty()) {
241 left_arg = net::EscapeQueryParamValue(
242 std::string("/cross-site/") + left_frame, true);
243 }
244 std::string right_arg;
245 if (!right_frame.empty()) {
246 right_arg = net::EscapeQueryParamValue(
247 std::string("/cross-site/") + right_frame, true);
248 }
249 std::string query = base::StringPrintf("left=%s&right=%s", left_arg.c_str(),
250 right_arg.c_str());
251 GURL::Replacements query_replacement;
252 query_replacement.SetQueryStr(query);
253 GURL target_url = base_url.ReplaceComponents(query_replacement);
254
255 return NavigateToURL(shell(), target_url);
256 }
257
258 bool SimulateDragEnterToRightFrame(const std::string& text) {
259 AssertTestPageIsLoaded();
260 return drag_simulator_->SimulateDragEnter(kMiddleOfRightFrame, text);
261 }
262
263 bool SimulateDropInRightFrame() {
264 AssertTestPageIsLoaded();
265 return drag_simulator_->SimulateDrop(kMiddleOfRightFrame);
266 }
267
268 RenderFrameHost* right_frame() {
269 AssertTestPageIsLoaded();
270 return GetFrameByName("right");
271 }
272
273 private:
274 RenderFrameHost* GetFrameByName(const std::string& name_to_find) {
275 WebContentsImpl* web_contents =
276 static_cast<WebContentsImpl*>(shell()->web_contents());
277 FrameTree* frame_tree = web_contents->GetFrameTree();
278 return frame_tree->FindByName(name_to_find)->current_frame_host();
279 }
280
281 void AssertTestPageIsLoaded() {
282 ASSERT_EQ(kTestPagePath,
283 shell()->web_contents()->GetLastCommittedURL().path());
284 }
285
286 std::unique_ptr<DragAndDropSimulator> drag_simulator_;
287
288 // Constants with coordinates within content/test/data/drag_and_drop/page.html
289 const gfx::Point kMiddleOfLeftFrame = gfx::Point(200, 200);
290 const gfx::Point kMiddleOfRightFrame = gfx::Point(400, 200);
291
292 DISALLOW_COPY_AND_ASSIGN(DragAndDropBrowserTest);
293 };
294
295 IN_PROC_BROWSER_TEST_F(DragAndDropBrowserTest, DropTextFromOutside) {
296 ASSERT_TRUE(NavigateToTestPage("a.com",
297 "", // Left frame is unused by this test.
298 "c.com/drag_and_drop/drop_target.html"));
299
300 {
301 std::string dragover_event;
302 DOMDragEventWaiter dragover_waiter("dragover", right_frame());
303 ASSERT_TRUE(SimulateDragEnterToRightFrame("Dragged test text"));
304 ASSERT_TRUE(dragover_waiter.WaitForNextMatchingEvent(&dragover_event));
305 EXPECT_THAT(dragover_event, testing::HasSubstr("\"drop_effect\":\"none\""));
306 EXPECT_THAT(dragover_event,
307 testing::HasSubstr("\"effect_allowed\":\"all\""));
308 EXPECT_THAT(dragover_event,
309 testing::HasSubstr("\"mime_types\":\"text/plain\""));
310 }
311
312 {
313 std::string drop_event;
314 DOMDragEventWaiter drop_waiter("drop", right_frame());
315 ASSERT_TRUE(SimulateDropInRightFrame());
316 ASSERT_TRUE(drop_waiter.WaitForNextMatchingEvent(&drop_event));
317 EXPECT_THAT(drop_event, testing::HasSubstr("\"drop_effect\":\"none\""));
318 EXPECT_THAT(drop_event, testing::HasSubstr("\"effect_allowed\":\"all\""));
319 EXPECT_THAT(drop_event,
320 testing::HasSubstr("\"mime_types\":\"text/plain\""));
321 }
322 }
323
324 } // namespace chrome
OLDNEW
« no previous file with comments | « content/browser/top_document_isolation_browsertest.cc ('k') | content/public/test/test_frame_navigation_observer.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698