Chromium Code Reviews| Index: chrome/browser/drag_and_drop_browsertest.cc |
| diff --git a/content/browser/web_contents/drag_and_drop_browsertest.cc b/chrome/browser/drag_and_drop_browsertest.cc |
| similarity index 47% |
| rename from content/browser/web_contents/drag_and_drop_browsertest.cc |
| rename to chrome/browser/drag_and_drop_browsertest.cc |
| index 71dfc4765bd61ba0b812ee8707e666878c31d0e8..b5c44dee86706c24437f5be8842089cd8eba826f 100644 |
| --- a/content/browser/web_contents/drag_and_drop_browsertest.cc |
| +++ b/chrome/browser/drag_and_drop_browsertest.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 { |
| @@ -70,7 +76,7 @@ namespace { |
| // 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 +163,114 @@ 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 nested message loop starts |
| +// (i.e. in response to a mouse-down + mouse-move simulated by the test) |
| +// and for running a test continuation inside the nested message loop. |
| +class DragStartWaiter : public aura::client::DragDropClient { |
| + public: |
| + using TestContinuationCallback = |
| + base::Callback<void(const std::string& text, |
| + const std::string& html, |
| + int operation, |
| + bool did_originate_from_renderer)>; |
| + |
| + // Starts monitoring |web_contents| for a start of a drag-and-drop. |
| + // Will call |test_continuation| when drag-and-drop starts. |
| + DragStartWaiter(content::WebContents* web_contents, |
| + const TestContinuationCallback& test_continuation) |
| + : web_contents_(web_contents), |
| + test_continuation_(test_continuation), |
| + message_loop_runner_(new content::MessageLoopRunner) { |
| + DCHECK(web_contents_); |
| + DCHECK(!test_continuation_.is_null()); |
| + |
| + // 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 drag-and-drop starts, ensures |test_continuation| is run (see |
| + // the constructor) and then returns after the drag-and-drop has ended. |
| + void WaitUntilFullDragAndDropCompletes() { message_loop_runner_->Run(); } |
| + |
| + 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 { |
| + if (!test_continuation_.is_null()) { |
| + base::string16 text; |
| + if (!data.GetString(&text)) |
| + text = base::ASCIIToUTF16("<no text>"); |
| + |
| + GURL base_url; |
| + base::string16 html; |
| + if (!data.GetHtml(&html, &base_url)) |
| + html = base::ASCIIToUTF16("<no html>"); |
| + |
| + base::SequencedTaskRunnerHandle::Get()->PostTask( |
| + FROM_HERE, |
| + base::Bind(std::move(test_continuation_), base::UTF16ToUTF8(text), |
| + base::UTF16ToUTF8(html), operation, |
| + data.DidOriginateFromRenderer())); |
| + |
| + test_continuation_.Reset(); |
| + message_loop_runner_->Quit(); |
| + } |
| + |
| + // This will kick-off a nested message loop and won't return until |
| + // drag-and-drop ends. |
| + return old_client_->StartDragAndDrop(data, root_window, source_window, |
| + screen_location, operation, source); |
| + } |
| + |
| + void DragCancel() override { old_client_->DragCancel(); } |
| + |
| + bool IsDragDropInProgress() override { |
| + return old_client_->IsDragDropInProgress(); |
| + } |
| + |
| + private: |
| + content::WebContents* web_contents_; |
| + DragStartWaiter::TestContinuationCallback test_continuation_; |
| + scoped_refptr<content::MessageLoopRunner> message_loop_runner_; |
| + aura::client::DragDropClient* old_client_; |
| +}; |
| + |
| // 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,51 +300,85 @@ class DOMDragEventWaiter { |
| private: |
| std::string target_frame_name_; |
| std::string event_type_to_wait_for_; |
| - DOMMessageQueue dom_message_queue_; |
| + content::DOMMessageQueue dom_message_queue_; |
| }; |
| const char kTestPagePath[] = "/drag_and_drop/page.html"; |
| } // namespace |
| -class DragAndDropBrowserTest : public ContentBrowserTest { |
| +class DragAndDropBrowserTest : public InProcessBrowserTest { |
| public: |
| DragAndDropBrowserTest(){}; |
| + // Declarations of test continuations. |
| + void DragStartInFrame_Step2(const std::string& frame_site, |
| + const std::string& text, |
| + const std::string& html, |
| + int operation, |
| + bool did_originate_from_renderer); |
| + |
| 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())); |
| + drag_simulator_.reset(new DragAndDropSimulator(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); |
| + content::RenderFrameHost* left_frame() { |
| + AssertTestPageIsLoaded(); |
| + return GetFrameByName("left"); |
| + } |
| + |
| + content::RenderFrameHost* right_frame() { |
| + AssertTestPageIsLoaded(); |
| + return GetFrameByName("right"); |
| + } |
| + |
| + // 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); |
| + } |
| + |
| + // 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); |
| + } |
| + |
| + // Drag-and-drop simulation using the mouse. |
| + |
| + bool SimulateMouseDownAndDragStartInLeftFrame() { |
| + AssertTestPageIsLoaded(); |
| + if (!SimulateMouseMove(kMiddleOfLeftFrame) || !SimulateMouseDown() || |
| + !SimulateMouseMove(kMiddleOfLeftFrame + gfx::Vector2d(5, 5))) |
| + return false; |
| + |
| + return true; |
| + } |
| - return NavigateToURL(shell(), target_url); |
| + bool SimulateMouseUp() { |
| + return ui_test_utils::SendMouseEventsSync(ui_controls::LEFT, |
| + ui_controls::UP); |
| } |
| + // Drag-and-drop simulation via DragAndDropSimulator. |
| + |
| bool SimulateDragEnterToRightFrame(const std::string& text) { |
| AssertTestPageIsLoaded(); |
| return drag_simulator_->SimulateDragEnter(kMiddleOfRightFrame, text); |
| @@ -265,22 +389,82 @@ class DragAndDropBrowserTest : public ContentBrowserTest { |
| return drag_simulator_->SimulateDrop(kMiddleOfRightFrame); |
| } |
| - RenderFrameHost* right_frame() { |
| - AssertTestPageIsLoaded(); |
| - return GetFrameByName("right"); |
| + content::WebContents* web_contents() { |
| + return browser()->tab_strip_model()->GetActiveWebContents(); |
| } |
| 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 SimulateMouseDown() { |
| + return ui_test_utils::SendMouseEventsSync(ui_controls::LEFT, |
| + ui_controls::DOWN); |
| + } |
| + |
| + 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(); |
| + |
| + // 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) { |
|
Łukasz Anforowicz
2016/11/10 19:26:24
content::FrameTree::FindByName is not exposed via
|
| + 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,14 +477,15 @@ 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")); |
| + // 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, |
| @@ -309,10 +494,12 @@ IN_PROC_BROWSER_TEST_F(DragAndDropBrowserTest, DropTextFromOutside) { |
| testing::HasSubstr("\"mime_types\":\"text/plain\"")); |
| } |
| + // 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\"")); |
| @@ -321,4 +508,55 @@ IN_PROC_BROWSER_TEST_F(DragAndDropBrowserTest, DropTextFromOutside) { |
| } |
| } |
| +IN_PROC_BROWSER_TEST_F(DragAndDropBrowserTest, DragStartInFrame) { |
| + std::string frame_site = "b.com"; |
| + ASSERT_TRUE(NavigateToTestPage("a.com")); |
| + ASSERT_TRUE(NavigateLeftFrame(frame_site, "image_source.html")); |
| + |
| + // Start the drag in the left frame. |
| + DragStartWaiter drag_start_waiter( |
| + web_contents(), |
| + base::Bind(&DragAndDropBrowserTest::DragStartInFrame_Step2, |
| + base::Unretained(this), frame_site)); |
| + DOMDragEventWaiter dragstart_event_waiter("dragstart", left_frame()); |
| + ASSERT_TRUE(SimulateMouseDownAndDragStartInLeftFrame()); |
| + |
| + // Verify Javascript event data. |
| + std::string dragstart_event; |
| + ASSERT_TRUE( |
| + dragstart_event_waiter.WaitForNextMatchingEvent(&dragstart_event)); |
| + EXPECT_THAT(dragstart_event, testing::HasSubstr("\"drop_effect\":\"none\"")); |
| + // |effect_allowed| is intialized *inside* dragstart event handler - no |
| + // point in verifying this value here (this would be tautological). |
| + EXPECT_THAT( |
| + dragstart_event, |
| + testing::HasSubstr("\"mime_types\":\"Files,text/html,text/uri-list\"")); |
| + |
| + // Run the nested drag-and-drop loop |
| + // (test will continue in DragStartInFrame_Step2). |
| + drag_start_waiter.WaitUntilFullDragAndDropCompletes(); |
| +} |
| + |
| +void DragAndDropBrowserTest::DragStartInFrame_Step2( |
| + const std::string& frame_site, |
| + const std::string& text, |
| + const std::string& html, |
| + int operation, |
| + bool did_originate_from_renderer) { |
| + 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_TRUE(did_originate_from_renderer); |
| + |
| + // dragstart event handler in image_source.html is asking for "copy" only. |
| + EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, operation); |
| + |
| + // This will exit the nested drag-and-drag loop. |
| + ASSERT_TRUE(SimulateMouseUp()); |
| +} |
| + |
| } // namespace chrome |