diff options
Diffstat (limited to 'chromium/ui/views/widget')
41 files changed, 2330 insertions, 1500 deletions
diff --git a/chromium/ui/views/widget/ax_native_widget_mac_unittest.mm b/chromium/ui/views/widget/ax_native_widget_mac_unittest.mm index faf79e0e6c7..ca7575056b0 100644 --- a/chromium/ui/views/widget/ax_native_widget_mac_unittest.mm +++ b/chromium/ui/views/widget/ax_native_widget_mac_unittest.mm @@ -772,7 +772,7 @@ class TestComboboxModel : public ui::ComboboxModel { // ui::ComboboxModel: int GetItemCount() const override { return 2; } - base::string16 GetItemAt(int index) override { + base::string16 GetItemAt(int index) const override { return index == 0 ? base::SysNSStringToUTF16(kTestStringValue) : base::ASCIIToUTF16("Second Item"); } diff --git a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.cc b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.cc index 3ee330f3598..80090c42e7d 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.cc @@ -11,7 +11,6 @@ #include "base/macros.h" #include "base/memory/ptr_util.h" -#include "base/metrics/histogram_macros.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/aura/client/capture_client.h" #include "ui/aura/client/drag_drop_client.h" @@ -24,6 +23,7 @@ #include "ui/base/dragdrop/os_exchange_data_provider_x11.h" #include "ui/base/layout.h" #include "ui/base/x/selection_utils.h" +#include "ui/base/x/x11_cursor.h" #include "ui/base/x/x11_drag_context.h" #include "ui/base/x/x11_util.h" #include "ui/base/x/x11_whole_screen_move_loop.h" @@ -32,6 +32,7 @@ #include "ui/events/event_utils.h" #include "ui/gfx/image/image_skia.h" #include "ui/gfx/x/x11.h" +#include "ui/gfx/x/xproto.h" #include "ui/platform_window/x11/x11_topmost_window_finder.h" #include "ui/views/controls/image_view.h" #include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h" @@ -115,9 +116,8 @@ DesktopDragDropClientAuraX11* DesktopDragDropClientAuraX11::DesktopDragDropClientAuraX11( aura::Window* root_window, views::DesktopNativeCursorManager* cursor_manager, - ::Display* display, - XID window) - : XDragDropClient(this, display, window), + x11::Window window) + : XDragDropClient(this, window), root_window_(root_window), cursor_manager_(cursor_manager) {} @@ -140,9 +140,6 @@ int DesktopDragDropClientAuraX11::StartDragAndDrop( const gfx::Point& /*screen_location*/, int operation, ui::DragDropTypes::DragEventSource source) { - UMA_HISTOGRAM_ENUMERATION("Event.DragDrop.Start", source, - ui::DragDropTypes::DRAG_EVENT_SOURCE_COUNT); - DCHECK(!g_current_drag_drop_client); g_current_drag_drop_client = this; @@ -171,28 +168,24 @@ int DesktopDragDropClientAuraX11::StartDragAndDrop( // Windows has a specific method, DoDragDrop(), which performs the entire // drag. We have to emulate this, so we spin off a nested runloop which will // track all cursor movement and reroute events to a specific handler. + auto* last_cursor = static_cast<ui::X11Cursor*>( + source_window->GetHost()->last_cursor().platform()); move_loop_->RunMoveLoop( !source_window->HasCapture(), - source_window->GetHost()->last_cursor().platform(), - cursor_manager_->GetInitializedCursor(ui::mojom::CursorType::kGrabbing) - .platform()); + last_cursor ? last_cursor->xcursor() : x11::None, + static_cast<ui::X11Cursor*>( + cursor_manager_ + ->GetInitializedCursor(ui::mojom::CursorType::kGrabbing) + .platform()) + ->xcursor()); if (alive) { auto resulting_operation = negotiated_operation(); - if (resulting_operation == ui::DragDropTypes::DRAG_NONE) { - UMA_HISTOGRAM_ENUMERATION("Event.DragDrop.Cancel", source, - ui::DragDropTypes::DRAG_EVENT_SOURCE_COUNT); - } else { - UMA_HISTOGRAM_ENUMERATION("Event.DragDrop.Drop", source, - ui::DragDropTypes::DRAG_EVENT_SOURCE_COUNT); - } drag_widget_.reset(); g_current_drag_drop_client = nullptr; CleanupDrag(); return resulting_operation; } - UMA_HISTOGRAM_ENUMERATION("Event.DragDrop.Cancel", source, - ui::DragDropTypes::DRAG_EVENT_SOURCE_COUNT); return ui::DragDropTypes::DRAG_NONE; } @@ -214,12 +207,13 @@ void DesktopDragDropClientAuraX11::RemoveObserver( NOTIMPLEMENTED(); } -bool DesktopDragDropClientAuraX11::DispatchXEvent(XEvent* event) { - if (!target_current_context() || - event->xany.window != target_current_context()->source_window()) { +bool DesktopDragDropClientAuraX11::DispatchXEvent(x11::Event* event) { + auto* prop = event->As<x11::PropertyNotifyEvent>(); + if (!target_current_context() || !prop || + prop->window != target_current_context()->source_window()) { return false; } - return target_current_context()->DispatchXEvent(event); + return target_current_context()->DispatchPropertyNotifyEvent(*prop); } void DesktopDragDropClientAuraX11::OnWindowDestroyed(aura::Window* window) { @@ -339,13 +333,8 @@ int DesktopDragDropClientAuraX11::UpdateDrag(const gfx::Point& screen_point) { std::unique_ptr<ui::DropTargetEvent> drop_target_event; DragDropDelegate* delegate = nullptr; DragTranslate(screen_point, &data, &drop_target_event, &delegate); - int drag_operation = - delegate ? drag_operation = delegate->OnDragUpdated(*drop_target_event) - : ui::DragDropTypes::DRAG_NONE; - UMA_HISTOGRAM_BOOLEAN("Event.DragDrop.AcceptDragUpdate", - drag_operation != ui::DragDropTypes::DRAG_NONE); - - return drag_operation; + return delegate ? delegate->OnDragUpdated(*drop_target_event) + : ui::DragDropTypes::DRAG_NONE; } void DesktopDragDropClientAuraX11::UpdateCursor( @@ -366,10 +355,12 @@ void DesktopDragDropClientAuraX11::UpdateCursor( break; } move_loop_->UpdateCursor( - cursor_manager_->GetInitializedCursor(cursor_type).platform()); + static_cast<ui::X11Cursor*>( + cursor_manager_->GetInitializedCursor(cursor_type).platform()) + ->xcursor()); } -void DesktopDragDropClientAuraX11::OnBeginForeignDrag(XID window) { +void DesktopDragDropClientAuraX11::OnBeginForeignDrag(x11::Window window) { DCHECK(target_current_context()); DCHECK(!target_current_context()->source_client()); @@ -413,10 +404,6 @@ int DesktopDragDropClientAuraX11::PerformDrop() { drop_event.set_flags(ui::XGetMaskAsEventFlags()); } - if (!IsDragDropInProgress()) { - UMA_HISTOGRAM_COUNTS_1M("Event.DragDrop.ExternalOriginDrop", 1); - } - drag_operation = delegate->OnPerformDrop(drop_event, std::move(data)); } @@ -426,7 +413,7 @@ int DesktopDragDropClientAuraX11::PerformDrop() { return drag_operation; } -void DesktopDragDropClientAuraX11::EndMoveLoop() { +void DesktopDragDropClientAuraX11::EndDragLoop() { move_loop_->EndMoveLoop(); } diff --git a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h index a06220b1864..b8f6cceafbf 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h @@ -22,6 +22,7 @@ #include "ui/events/x/x11_window_event_manager.h" #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/size.h" +#include "ui/gfx/x/event.h" #include "ui/gfx/x/x11.h" #include "ui/views/views_export.h" @@ -61,8 +62,7 @@ class VIEWS_EXPORT DesktopDragDropClientAuraX11 DesktopDragDropClientAuraX11( aura::Window* root_window, views::DesktopNativeCursorManager* cursor_manager, - Display* xdisplay, - XID xwindow); + x11::Window xwindow); ~DesktopDragDropClientAuraX11() override; void Init(); @@ -80,7 +80,7 @@ class VIEWS_EXPORT DesktopDragDropClientAuraX11 void RemoveObserver(aura::client::DragDropClientObserver* observer) override; // XEventDispatcher: - bool DispatchXEvent(XEvent* event) override; + bool DispatchXEvent(x11::Event* event) override; // aura::WindowObserver: void OnWindowDestroyed(aura::Window* window) override; @@ -118,11 +118,11 @@ class VIEWS_EXPORT DesktopDragDropClientAuraX11 int UpdateDrag(const gfx::Point& screen_point) override; void UpdateCursor( ui::DragDropTypes::DragOperation negotiated_operation) override; - void OnBeginForeignDrag(XID window) override; + void OnBeginForeignDrag(x11::Window window) override; void OnEndForeignDrag() override; void OnBeforeDragLeave() override; int PerformDrop() override; - void EndMoveLoop() override; + void EndDragLoop() override; // A nested run loop that notifies this object of events through the // ui::X11MoveLoopDelegate interface. diff --git a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11_unittest.cc b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11_unittest.cc index 68c1ba2c47d..1ab88c73c14 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11_unittest.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11_unittest.cc @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h" + #include <map> #include <memory> #include <utility> @@ -26,23 +28,26 @@ #include "ui/gfx/x/x11.h" #include "ui/gfx/x/x11_atom_cache.h" #include "ui/gfx/x/x11_types.h" +#include "ui/gfx/x/xproto.h" #include "ui/views/test/views_test_base.h" -#include "ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h" #include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h" #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" #include "ui/views/widget/widget.h" +// TODO(crbug.com/990756): Transfer all tests from this file to better places +// when DDDClientAuraX11 goes away. + namespace views { namespace { class TestDragDropClient; -// Collects messages which would otherwise be sent to |xid_| via +// Collects messages which would otherwise be sent to |window_| via // SendXClientEvent(). class ClientMessageEventCollector { public: - ClientMessageEventCollector(::Window xid, TestDragDropClient* client); + ClientMessageEventCollector(x11::Window window, TestDragDropClient* client); virtual ~ClientMessageEventCollector(); // Returns true if |events_| is non-empty. @@ -50,18 +55,18 @@ class ClientMessageEventCollector { // Pops all of |events_| and returns the popped events in the order that they // were on the stack - std::vector<XClientMessageEvent> PopAllEvents(); + std::vector<x11::ClientMessageEvent> PopAllEvents(); // Adds |event| to the stack. - void RecordEvent(const XClientMessageEvent& event); + void RecordEvent(const x11::ClientMessageEvent& event); private: - ::Window xid_; + x11::Window window_; // Not owned. TestDragDropClient* client_; - std::vector<XClientMessageEvent> events_; + std::vector<x11::ClientMessageEvent> events_; DISALLOW_COPY_AND_ASSIGN(ClientMessageEventCollector); }; @@ -101,8 +106,8 @@ class SimpleTestDragDropClient : public DesktopDragDropClientAuraX11 { DesktopNativeCursorManager* cursor_manager); ~SimpleTestDragDropClient() override; - // Sets |xid| as the topmost window for all mouse positions. - void SetTopmostXWindow(XID xid); + // Sets |window| as the topmost window for all mouse positions. + void SetTopmostXWindow(x11::Window window); // Returns true if the move loop is running. bool IsMoveLoopRunning(); @@ -113,10 +118,10 @@ class SimpleTestDragDropClient : public DesktopDragDropClientAuraX11 { // DesktopDragDropClientAuraX11: std::unique_ptr<ui::X11MoveLoop> CreateMoveLoop( ui::X11MoveLoopDelegate* delegate) override; - XID FindWindowFor(const gfx::Point& screen_point) override; + x11::Window FindWindowFor(const gfx::Point& screen_point) override; - // The XID of the window which is simulated to be the topmost window. - XID target_xid_ = x11::None; + // The x11::Window of the window which is simulated to be the topmost window. + x11::Window target_window_ = x11::Window::None; // The move loop. Not owned. TestMoveLoop* loop_ = nullptr; @@ -137,46 +142,47 @@ class TestDragDropClient : public SimpleTestDragDropClient { DesktopNativeCursorManager* cursor_manager); ~TestDragDropClient() override; - // Returns the XID of the window which initiated the drag. - ::Window source_xwindow() { return source_xid_; } + // Returns the x11::Window of the window which initiated the drag. + x11::Window source_xwindow() { return source_window_; } // Returns the Atom with |name|. - Atom GetAtom(const char* name); + x11::Atom GetAtom(const char* name); // Returns true if the event's message has |type|. - bool MessageHasType(const XClientMessageEvent& event, const char* type); + bool MessageHasType(const x11::ClientMessageEvent& event, const char* type); - // Sets |collector| to collect XClientMessageEvents which would otherwise + // Sets |collector| to collect x11::ClientMessageEvents which would otherwise // have been sent to the drop target window. - void SetEventCollectorFor(::Window xid, + void SetEventCollectorFor(x11::Window window, ClientMessageEventCollector* collector); // Builds an XdndStatus message and sends it to // DesktopDragDropClientAuraX11. - void OnStatus(XID target_window, + void OnStatus(x11::Window target_window, bool will_accept_drop, - ::Atom accepted_action); + x11::Atom accepted_action); // Builds an XdndFinished message and sends it to // DesktopDragDropClientAuraX11. - void OnFinished(XID target_window, + void OnFinished(x11::Window target_window, bool accepted_drop, - ::Atom performed_action); + x11::Atom performed_action); - // Sets |xid| as the topmost window at the current mouse position and + // Sets |window| as the topmost window at the current mouse position and // generates a synthetic mouse move. - void SetTopmostXWindowAndMoveMouse(::Window xid); + void SetTopmostXWindowAndMoveMouse(x11::Window window); private: // DesktopDragDropClientAuraX11: - void SendXClientEvent(::Window xid, XEvent* event) override; + void SendXClientEvent(x11::Window window, + const x11::ClientMessageEvent& event) override; - // The XID of the window which initiated the drag. - ::Window source_xid_; + // The x11::Window of the window which initiated the drag. + x11::Window source_window_; - // Map of ::Windows to the collector which intercepts XClientMessageEvents - // for that window. - std::map<::Window, ClientMessageEventCollector*> collectors_; + // Map of x11::Windows to the collector which intercepts + // x11::ClientMessageEvents for that window. + std::map<x11::Window, ClientMessageEventCollector*> collectors_; DISALLOW_COPY_AND_ASSIGN(TestDragDropClient); }; @@ -185,24 +191,25 @@ class TestDragDropClient : public SimpleTestDragDropClient { // ClientMessageEventCollector ClientMessageEventCollector::ClientMessageEventCollector( - ::Window xid, + x11::Window window, TestDragDropClient* client) - : xid_(xid), client_(client) { - client->SetEventCollectorFor(xid, this); + : window_(window), client_(client) { + client->SetEventCollectorFor(window, this); } ClientMessageEventCollector::~ClientMessageEventCollector() { - client_->SetEventCollectorFor(xid_, nullptr); + client_->SetEventCollectorFor(window_, nullptr); } -std::vector<XClientMessageEvent> ClientMessageEventCollector::PopAllEvents() { - std::vector<XClientMessageEvent> to_return; +std::vector<x11::ClientMessageEvent> +ClientMessageEventCollector::PopAllEvents() { + std::vector<x11::ClientMessageEvent> to_return; to_return.swap(events_); return to_return; } void ClientMessageEventCollector::RecordEvent( - const XClientMessageEvent& event) { + const x11::ClientMessageEvent& event) { events_.push_back(event); } @@ -246,13 +253,12 @@ SimpleTestDragDropClient::SimpleTestDragDropClient( DesktopNativeCursorManager* cursor_manager) : DesktopDragDropClientAuraX11(window, cursor_manager, - gfx::GetXDisplay(), window->GetHost()->GetAcceleratedWidget()) {} SimpleTestDragDropClient::~SimpleTestDragDropClient() = default; -void SimpleTestDragDropClient::SetTopmostXWindow(XID xid) { - target_xid_ = xid; +void SimpleTestDragDropClient::SetTopmostXWindow(x11::Window window) { + target_window_ = window; } bool SimpleTestDragDropClient::IsMoveLoopRunning() { @@ -265,8 +271,9 @@ std::unique_ptr<ui::X11MoveLoop> SimpleTestDragDropClient::CreateMoveLoop( return base::WrapUnique(loop_); } -XID SimpleTestDragDropClient::FindWindowFor(const gfx::Point& screen_point) { - return target_xid_; +x11::Window SimpleTestDragDropClient::FindWindowFor( + const gfx::Point& screen_point) { + return target_window_; } /////////////////////////////////////////////////////////////////////////////// @@ -276,68 +283,70 @@ TestDragDropClient::TestDragDropClient( aura::Window* window, DesktopNativeCursorManager* cursor_manager) : SimpleTestDragDropClient(window, cursor_manager), - source_xid_(window->GetHost()->GetAcceleratedWidget()) {} + source_window_(window->GetHost()->GetAcceleratedWidget()) {} TestDragDropClient::~TestDragDropClient() = default; -Atom TestDragDropClient::GetAtom(const char* name) { +x11::Atom TestDragDropClient::GetAtom(const char* name) { return gfx::GetAtom(name); } -bool TestDragDropClient::MessageHasType(const XClientMessageEvent& event, +bool TestDragDropClient::MessageHasType(const x11::ClientMessageEvent& event, const char* type) { - return event.message_type == GetAtom(type); + return event.type == GetAtom(type); } void TestDragDropClient::SetEventCollectorFor( - ::Window xid, + x11::Window window, ClientMessageEventCollector* collector) { if (collector) - collectors_[xid] = collector; + collectors_[window] = collector; else - collectors_.erase(xid); + collectors_.erase(window); } -void TestDragDropClient::OnStatus(XID target_window, +void TestDragDropClient::OnStatus(x11::Window target_window, bool will_accept_drop, - ::Atom accepted_action) { - XClientMessageEvent event; - event.message_type = GetAtom("XdndStatus"); + x11::Atom accepted_action) { + x11::ClientMessageEvent event; + event.type = GetAtom("XdndStatus"); event.format = 32; - event.window = source_xid_; - event.data.l[0] = target_window; - event.data.l[1] = will_accept_drop ? 1 : 0; - event.data.l[2] = 0; - event.data.l[3] = 0; - event.data.l[4] = accepted_action; + event.window = source_window_; + event.data.data32[0] = static_cast<uint32_t>(target_window); + event.data.data32[1] = will_accept_drop ? 1 : 0; + event.data.data32[2] = 0; + event.data.data32[3] = 0; + event.data.data32[4] = static_cast<uint32_t>(accepted_action); HandleXdndEvent(event); } -void TestDragDropClient::OnFinished(XID target_window, +void TestDragDropClient::OnFinished(x11::Window target_window, bool accepted_drop, - ::Atom performed_action) { - XClientMessageEvent event; - event.message_type = GetAtom("XdndFinished"); + x11::Atom performed_action) { + x11::ClientMessageEvent event; + event.type = GetAtom("XdndFinished"); event.format = 32; - event.window = source_xid_; - event.data.l[0] = target_window; - event.data.l[1] = accepted_drop ? 1 : 0; - event.data.l[2] = performed_action; - event.data.l[3] = 0; - event.data.l[4] = 0; + event.window = source_window_; + event.data.data32[0] = static_cast<uint32_t>(target_window); + event.data.data32[1] = accepted_drop ? 1 : 0; + event.data.data32[2] = static_cast<uint32_t>(performed_action); + event.data.data32[3] = 0; + event.data.data32[4] = 0; HandleXdndEvent(event); } -void TestDragDropClient::SetTopmostXWindowAndMoveMouse(::Window xid) { - SetTopmostXWindow(xid); +void TestDragDropClient::SetTopmostXWindowAndMoveMouse(x11::Window window) { + SetTopmostXWindow(window); OnMouseMovement(gfx::Point(kMouseMoveX, kMouseMoveY), ui::EF_NONE, ui::EventTimeForNow()); } -void TestDragDropClient::SendXClientEvent(::Window xid, XEvent* event) { - auto it = collectors_.find(xid); +void TestDragDropClient::SendXClientEvent( + x11::Window window, + const x11::ClientMessageEvent& event) { + auto it = collectors_.find(window); if (it != collectors_.end()) - it->second->RecordEvent(event->xclient); + it->second->RecordEvent(event); } } // namespace @@ -402,103 +411,6 @@ class DesktopDragDropClientAuraX11Test : public ViewsTestBase { DISALLOW_COPY_AND_ASSIGN(DesktopDragDropClientAuraX11Test); }; -namespace { - -void BasicStep2(TestDragDropClient* client, XID toplevel) { - EXPECT_TRUE(client->IsMoveLoopRunning()); - - ClientMessageEventCollector collector(toplevel, client); - client->SetTopmostXWindowAndMoveMouse(toplevel); - - // XdndEnter should have been sent to |toplevel| before the XdndPosition - // message. - std::vector<XClientMessageEvent> events = collector.PopAllEvents(); - ASSERT_EQ(2u, events.size()); - - EXPECT_TRUE(client->MessageHasType(events[0], "XdndEnter")); - EXPECT_EQ(client->source_xwindow(), static_cast<XID>(events[0].data.l[0])); - EXPECT_EQ(1, events[0].data.l[1] & 1); - std::vector<Atom> targets; - ui::GetAtomArrayProperty(client->source_xwindow(), "XdndTypeList", &targets); - EXPECT_FALSE(targets.empty()); - - EXPECT_TRUE(client->MessageHasType(events[1], "XdndPosition")); - EXPECT_EQ(client->source_xwindow(), static_cast<XID>(events[0].data.l[0])); - const int kCoords = - TestDragDropClient::kMouseMoveX << 16 | TestDragDropClient::kMouseMoveY; - EXPECT_EQ(kCoords, events[1].data.l[2]); - EXPECT_EQ(client->GetAtom("XdndActionCopy"), - static_cast<Atom>(events[1].data.l[4])); - - client->OnStatus(toplevel, true, client->GetAtom("XdndActionCopy")); - - // Because there is no unprocessed XdndPosition, the drag drop client should - // send XdndDrop immediately after the mouse is released. - client->OnMouseReleased(); - - events = collector.PopAllEvents(); - ASSERT_EQ(1u, events.size()); - EXPECT_TRUE(client->MessageHasType(events[0], "XdndDrop")); - EXPECT_EQ(client->source_xwindow(), static_cast<XID>(events[0].data.l[0])); - - // Send XdndFinished to indicate that the drag drop client can cleanup any - // data related to this drag. The move loop should end only after the - // XdndFinished message was received. - EXPECT_TRUE(client->IsMoveLoopRunning()); - client->OnFinished(toplevel, true, client->GetAtom("XdndActionCopy")); - EXPECT_FALSE(client->IsMoveLoopRunning()); -} - -void BasicStep3(TestDragDropClient* client, XID toplevel) { - EXPECT_TRUE(client->IsMoveLoopRunning()); - - ClientMessageEventCollector collector(toplevel, client); - client->SetTopmostXWindowAndMoveMouse(toplevel); - - std::vector<XClientMessageEvent> events = collector.PopAllEvents(); - ASSERT_EQ(2u, events.size()); - EXPECT_TRUE(client->MessageHasType(events[0], "XdndEnter")); - EXPECT_TRUE(client->MessageHasType(events[1], "XdndPosition")); - - client->OnStatus(toplevel, true, client->GetAtom("XdndActionCopy")); - client->SetTopmostXWindowAndMoveMouse(toplevel); - events = collector.PopAllEvents(); - ASSERT_EQ(1u, events.size()); - EXPECT_TRUE(client->MessageHasType(events[0], "XdndPosition")); - - // We have not received an XdndStatus ack for the second XdndPosition message. - // Test that sending XdndDrop is delayed till the XdndStatus ack is received. - client->OnMouseReleased(); - EXPECT_FALSE(collector.HasEvents()); - - client->OnStatus(toplevel, true, client->GetAtom("XdndActionCopy")); - events = collector.PopAllEvents(); - ASSERT_EQ(1u, events.size()); - EXPECT_TRUE(client->MessageHasType(events[0], "XdndDrop")); - - EXPECT_TRUE(client->IsMoveLoopRunning()); - client->OnFinished(toplevel, true, client->GetAtom("XdndActionCopy")); - EXPECT_FALSE(client->IsMoveLoopRunning()); -} - -} // namespace - -TEST_F(DesktopDragDropClientAuraX11Test, Basic) { - XID toplevel = 1; - - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::BindOnce(&BasicStep2, client(), toplevel)); - int result = StartDragAndDrop(); - EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, result); - - // Do another drag and drop to test that the data is properly cleaned up as a - // result of the XdndFinished message. - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::BindOnce(&BasicStep3, client(), toplevel)); - result = StartDragAndDrop(); - EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, result); -} - void HighDPIStep(TestDragDropClient* client) { float scale = display::Screen::GetScreen()->GetPrimaryDisplay().device_scale_factor(); @@ -520,6 +432,8 @@ void HighDPIStep(TestDragDropClient* client) { client->OnMouseReleased(); } +// TODO(crbug.com/990756): Turn this into tests of DesktopDragDropClientOzone +// or its equivalent. TEST_F(DesktopDragDropClientAuraX11Test, HighDPI200) { aura::TestScreen* screen = static_cast<aura::TestScreen*>(display::Screen::GetScreen()); @@ -531,6 +445,8 @@ TEST_F(DesktopDragDropClientAuraX11Test, HighDPI200) { EXPECT_EQ(ui::DragDropTypes::DRAG_NONE, result); } +// TODO(crbug.com/990756): Turn this into tests of DesktopDragDropClientOzone +// or its equivalent. TEST_F(DesktopDragDropClientAuraX11Test, HighDPI150) { aura::TestScreen* screen = static_cast<aura::TestScreen*>(display::Screen::GetScreen()); @@ -544,212 +460,6 @@ TEST_F(DesktopDragDropClientAuraX11Test, HighDPI150) { namespace { -void TargetDoesNotRespondStep2(TestDragDropClient* client) { - EXPECT_TRUE(client->IsMoveLoopRunning()); - - XID toplevel = 1; - ClientMessageEventCollector collector(toplevel, client); - client->SetTopmostXWindowAndMoveMouse(toplevel); - - std::vector<XClientMessageEvent> events = collector.PopAllEvents(); - ASSERT_EQ(2u, events.size()); - EXPECT_TRUE(client->MessageHasType(events[0], "XdndEnter")); - EXPECT_TRUE(client->MessageHasType(events[1], "XdndPosition")); - - client->OnMouseReleased(); - events = collector.PopAllEvents(); - ASSERT_EQ(1u, events.size()); - EXPECT_TRUE(client->MessageHasType(events[0], "XdndLeave")); - EXPECT_FALSE(client->IsMoveLoopRunning()); -} - -} // namespace - -// Test that we do not wait for the target to send XdndStatus if we have not -// received any XdndStatus messages at all from the target. The Unity -// DNDCollectionWindow is an example of an XdndAware target which does not -// respond to XdndPosition messages at all. -TEST_F(DesktopDragDropClientAuraX11Test, TargetDoesNotRespond) { - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::BindOnce(&TargetDoesNotRespondStep2, client())); - int result = StartDragAndDrop(); - EXPECT_EQ(ui::DragDropTypes::DRAG_NONE, result); -} - -namespace { - -void QueuePositionStep2(TestDragDropClient* client) { - EXPECT_TRUE(client->IsMoveLoopRunning()); - - XID toplevel = 1; - ClientMessageEventCollector collector(toplevel, client); - client->SetTopmostXWindowAndMoveMouse(toplevel); - client->SetTopmostXWindowAndMoveMouse(toplevel); - client->SetTopmostXWindowAndMoveMouse(toplevel); - - std::vector<XClientMessageEvent> events = collector.PopAllEvents(); - ASSERT_EQ(2u, events.size()); - EXPECT_TRUE(client->MessageHasType(events[0], "XdndEnter")); - EXPECT_TRUE(client->MessageHasType(events[1], "XdndPosition")); - - client->OnStatus(toplevel, true, client->GetAtom("XdndActionCopy")); - events = collector.PopAllEvents(); - ASSERT_EQ(1u, events.size()); - EXPECT_TRUE(client->MessageHasType(events[0], "XdndPosition")); - - client->OnStatus(toplevel, true, client->GetAtom("XdndActionCopy")); - EXPECT_FALSE(collector.HasEvents()); - - client->OnMouseReleased(); - events = collector.PopAllEvents(); - ASSERT_EQ(1u, events.size()); - EXPECT_TRUE(client->MessageHasType(events[0], "XdndDrop")); - - EXPECT_TRUE(client->IsMoveLoopRunning()); - client->OnFinished(toplevel, true, client->GetAtom("XdndActionCopy")); - EXPECT_FALSE(client->IsMoveLoopRunning()); -} - -} // namespace - -// Test that XdndPosition messages are queued till the pending XdndPosition -// message is acked via an XdndStatus message. -TEST_F(DesktopDragDropClientAuraX11Test, QueuePosition) { - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::BindOnce(&QueuePositionStep2, client())); - int result = StartDragAndDrop(); - EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, result); -} - -namespace { - -void TargetChangesStep2(TestDragDropClient* client) { - EXPECT_TRUE(client->IsMoveLoopRunning()); - - XID toplevel1 = 1; - ClientMessageEventCollector collector1(toplevel1, client); - client->SetTopmostXWindowAndMoveMouse(toplevel1); - - std::vector<XClientMessageEvent> events1 = collector1.PopAllEvents(); - ASSERT_EQ(2u, events1.size()); - EXPECT_TRUE(client->MessageHasType(events1[0], "XdndEnter")); - EXPECT_TRUE(client->MessageHasType(events1[1], "XdndPosition")); - - XID toplevel2 = 2; - ClientMessageEventCollector collector2(toplevel2, client); - client->SetTopmostXWindowAndMoveMouse(toplevel2); - - // It is possible for |toplevel1| to send XdndStatus after the source has sent - // XdndLeave but before |toplevel1| has received the XdndLeave message. The - // XdndStatus message should be ignored. - client->OnStatus(toplevel1, true, client->GetAtom("XdndActionCopy")); - events1 = collector1.PopAllEvents(); - ASSERT_EQ(1u, events1.size()); - EXPECT_TRUE(client->MessageHasType(events1[0], "XdndLeave")); - - std::vector<XClientMessageEvent> events2 = collector2.PopAllEvents(); - ASSERT_EQ(2u, events2.size()); - EXPECT_TRUE(client->MessageHasType(events2[0], "XdndEnter")); - EXPECT_TRUE(client->MessageHasType(events2[1], "XdndPosition")); - - client->OnStatus(toplevel2, true, client->GetAtom("XdndActionCopy")); - client->OnMouseReleased(); - events2 = collector2.PopAllEvents(); - ASSERT_EQ(1u, events2.size()); - EXPECT_TRUE(client->MessageHasType(events2[0], "XdndDrop")); - - EXPECT_TRUE(client->IsMoveLoopRunning()); - client->OnFinished(toplevel2, true, client->GetAtom("XdndActionCopy")); - EXPECT_FALSE(client->IsMoveLoopRunning()); -} - -} // namespace - -// Test the behavior when the target changes during a drag. -TEST_F(DesktopDragDropClientAuraX11Test, TargetChanges) { - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::BindOnce(&TargetChangesStep2, client())); - int result = StartDragAndDrop(); - EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, result); -} - -namespace { - -void RejectAfterMouseReleaseStep2(TestDragDropClient* client) { - EXPECT_TRUE(client->IsMoveLoopRunning()); - - XID toplevel = 1; - ClientMessageEventCollector collector(toplevel, client); - client->SetTopmostXWindowAndMoveMouse(toplevel); - - std::vector<XClientMessageEvent> events = collector.PopAllEvents(); - ASSERT_EQ(2u, events.size()); - EXPECT_TRUE(client->MessageHasType(events[0], "XdndEnter")); - EXPECT_TRUE(client->MessageHasType(events[1], "XdndPosition")); - - client->OnStatus(toplevel, true, client->GetAtom("XdndActionCopy")); - EXPECT_FALSE(collector.HasEvents()); - - // Send another mouse move such that there is a pending XdndPosition. - client->SetTopmostXWindowAndMoveMouse(toplevel); - events = collector.PopAllEvents(); - ASSERT_EQ(1u, events.size()); - EXPECT_TRUE(client->MessageHasType(events[0], "XdndPosition")); - - client->OnMouseReleased(); - // Reject the drop. - client->OnStatus(toplevel, false, x11::None); - - events = collector.PopAllEvents(); - ASSERT_EQ(1u, events.size()); - EXPECT_TRUE(client->MessageHasType(events[0], "XdndLeave")); - EXPECT_FALSE(client->IsMoveLoopRunning()); -} - -void RejectAfterMouseReleaseStep3(TestDragDropClient* client) { - EXPECT_TRUE(client->IsMoveLoopRunning()); - - XID toplevel = 2; - ClientMessageEventCollector collector(toplevel, client); - client->SetTopmostXWindowAndMoveMouse(toplevel); - - std::vector<XClientMessageEvent> events = collector.PopAllEvents(); - ASSERT_EQ(2u, events.size()); - EXPECT_TRUE(client->MessageHasType(events[0], "XdndEnter")); - EXPECT_TRUE(client->MessageHasType(events[1], "XdndPosition")); - - client->OnStatus(toplevel, true, client->GetAtom("XdndActionCopy")); - EXPECT_FALSE(collector.HasEvents()); - - client->OnMouseReleased(); - events = collector.PopAllEvents(); - ASSERT_EQ(1u, events.size()); - EXPECT_TRUE(client->MessageHasType(events[0], "XdndDrop")); - - EXPECT_TRUE(client->IsMoveLoopRunning()); - client->OnFinished(toplevel, false, x11::None); - EXPECT_FALSE(client->IsMoveLoopRunning()); -} - -} // namespace - -// Test that the source sends XdndLeave instead of XdndDrop if the drag -// operation is rejected after the mouse is released. -TEST_F(DesktopDragDropClientAuraX11Test, RejectAfterMouseRelease) { - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::BindOnce(&RejectAfterMouseReleaseStep2, client())); - int result = StartDragAndDrop(); - EXPECT_EQ(ui::DragDropTypes::DRAG_NONE, result); - - // Repeat the test but reject the drop in the XdndFinished message instead. - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::BindOnce(&RejectAfterMouseReleaseStep3, client())); - result = StartDragAndDrop(); - EXPECT_EQ(ui::DragDropTypes::DRAG_NONE, result); -} - -namespace { - // DragDropDelegate which counts the number of each type of drag-drop event and // keeps track of the most recent drag-drop event. class TestDragDropDelegate : public aura::client::DragDropDelegate { @@ -928,6 +638,8 @@ void ChromeSourceTargetStep2(SimpleTestDragDropClient* client, } // namespace +// TODO(crbug.com/990756): Turn this into tests of DesktopDragDropClientOzone +// or its equivalent. TEST_F(DesktopDragDropClientAuraX11ChromeSourceTargetTest, Basic) { base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, @@ -936,6 +648,8 @@ TEST_F(DesktopDragDropClientAuraX11ChromeSourceTargetTest, Basic) { EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, result); } +// TODO(crbug.com/990756): Turn this into tests of DesktopDragDropClientOzone +// or its equivalent. // Test that if 'Ctrl' is pressed during a drag and drop operation, that // the aura::client::DragDropDelegate is properly notified. TEST_F(DesktopDragDropClientAuraX11ChromeSourceTargetTest, CtrlPressed) { diff --git a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.cc b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.cc index 24a959b321d..dd9232bb7e9 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.cc @@ -21,10 +21,13 @@ #include "ui/base/cursor/mojom/cursor_type.mojom-shared.h" #include "ui/base/dragdrop/drag_drop_types.h" #include "ui/base/dragdrop/drop_target_event.h" -#include "ui/base/dragdrop/os_exchange_data_provider_aura.h" +#include "ui/base/layout.h" +#include "ui/display/screen.h" #include "ui/platform_window/platform_window_delegate.h" #include "ui/platform_window/platform_window_handler/wm_drag_handler.h" +#include "ui/views/controls/image_view.h" #include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h" +#include "ui/views/widget/widget.h" namespace views { @@ -37,8 +40,64 @@ aura::Window* GetTargetWindow(aura::Window* root_window, return root_window->GetEventHandlerForPoint(root_location); } +// The minimum alpha required so we would treat the pixel as visible. +constexpr uint32_t kMinAlpha = 32; + +// Returns true if |image| has any visible regions (defined as having a pixel +// with alpha > |kMinAlpha|). +bool IsValidDragImage(const gfx::ImageSkia& image) { + if (image.isNull()) + return false; + + // Because we need a GL context per window, we do a quick check so that we + // don't make another context if the window would just be displaying a mostly + // transparent image. + const SkBitmap* in_bitmap = image.bitmap(); + for (int y = 0; y < in_bitmap->height(); ++y) { + uint32_t* in_row = in_bitmap->getAddr32(0, y); + + for (int x = 0; x < in_bitmap->width(); ++x) { + if (SkColorGetA(in_row[x]) > kMinAlpha) + return true; + } + } + + return false; +} + +std::unique_ptr<views::Widget> CreateDragWidget( + const gfx::Point& root_location, + const gfx::ImageSkia& image, + const gfx::Vector2d& drag_widget_offset) { + auto widget = std::make_unique<views::Widget>(); + views::Widget::InitParams params(views::Widget::InitParams::TYPE_DRAG); + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.accept_events = false; + + gfx::Point location = root_location - drag_widget_offset; + params.bounds = gfx::Rect(location, image.size()); + widget->set_focus_on_creation(false); + widget->set_frame_type(views::Widget::FrameType::kForceNative); + widget->Init(std::move(params)); + widget->GetNativeWindow()->SetName("DragWindow"); + + std::unique_ptr<views::ImageView> image_view = + std::make_unique<views::ImageView>(); + image_view->SetImage(image); + widget->SetContentsView(std::move(image_view)); + widget->Show(); + widget->GetNativeWindow()->layer()->SetFillsBoundsOpaquely(false); + widget->StackAtTop(); + + return widget; +} + } // namespace +DesktopDragDropClientOzone::DragContext::DragContext() = default; + +DesktopDragDropClientOzone::DragContext::~DragContext() = default; + DesktopDragDropClientOzone::DesktopDragDropClientOzone( aura::Window* root_window, views::DesktopNativeCursorManager* cursor_manager, @@ -50,7 +109,7 @@ DesktopDragDropClientOzone::DesktopDragDropClientOzone( DesktopDragDropClientOzone::~DesktopDragDropClientOzone() { ResetDragDropTarget(); - if (in_move_loop_) + if (IsDragDropInProgress()) DragCancel(); } @@ -64,9 +123,11 @@ int DesktopDragDropClientOzone::StartDragAndDrop( if (!drag_handler_) return ui::DragDropTypes::DragOperation::DRAG_NONE; - DCHECK(!in_move_loop_); + DCHECK(!drag_context_); + drag_context_ = std::make_unique<DragContext>(); + base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed); - quit_closure_ = run_loop.QuitClosure(); + drag_context_->quit_closure = run_loop.QuitClosure(); // Chrome expects starting drag and drop to release capture. aura::Window* capture_window = @@ -77,18 +138,38 @@ int DesktopDragDropClientOzone::StartDragAndDrop( aura::client::CursorClient* cursor_client = aura::client::GetCursorClient(root_window); - initial_cursor_ = source_window->GetHost()->last_cursor(); + auto initial_cursor = source_window->GetHost()->last_cursor(); drag_operation_ = operation; - cursor_client->SetCursor( - cursor_manager_->GetInitializedCursor(ui::mojom::CursorType::kGrabbing)); - - drag_handler_->StartDrag( - *data.get(), operation, cursor_client->GetCursor(), - base::BindOnce(&DesktopDragDropClientOzone::OnDragSessionClosed, - base::Unretained(this))); - in_move_loop_ = true; + if (cursor_client) { + cursor_client->SetCursor(cursor_manager_->GetInitializedCursor( + ui::mojom::CursorType::kGrabbing)); + } + + const auto& provider = data->provider(); + gfx::ImageSkia drag_image = provider.GetDragImage(); + if (IsValidDragImage(drag_image)) { + drag_context_->size = drag_image.size(); + drag_context_->offset = provider.GetDragImageOffset(); + drag_context_->widget = + CreateDragWidget(root_location, drag_image, drag_context_->offset); + } + + // This object is owned by a DesktopNativeWidgetAura that can be destroyed + // during the drag loop, which will also destroy this object. So keep track + // of whether we are still alive after the drag ends. + auto alive = weak_factory_.GetWeakPtr(); + + drag_handler_->StartDrag(*data.get(), operation, cursor_client->GetCursor(), + this); run_loop.Run(); - DragDropSessionCompleted(); + + if (!alive) + return ui::DragDropTypes::DRAG_NONE; + + if (cursor_client) + cursor_client->SetCursor(initial_cursor); + drag_context_.reset(); + return drag_operation_; } @@ -97,7 +178,7 @@ void DesktopDragDropClientOzone::DragCancel() { } bool DesktopDragDropClientOzone::IsDragDropInProgress() { - return in_move_loop_; + return bool(drag_context_) && bool(drag_context_->quit_closure); } void DesktopDragDropClientOzone::AddObserver( @@ -117,129 +198,193 @@ void DesktopDragDropClientOzone::OnDragEnter( last_drag_point_ = point; drag_operation_ = operation; - // If it doesn't have |data|, it defers sending events to - // |drag_drop_delegate_|. It will try again before handling drop. + // If |data| is empty, we defer sending any events to the + // |drag_drop_delegate_|. All necessary events will be sent on dropping. if (!data) return; - os_exchange_data_ = std::move(data); - std::unique_ptr<ui::DropTargetEvent> event = CreateDropTargetEvent(point); - if (drag_drop_delegate_ && event) - drag_drop_delegate_->OnDragEntered(*event); + data_to_drop_ = std::move(data); + UpdateTargetAndCreateDropEvent(point); } int DesktopDragDropClientOzone::OnDragMotion(const gfx::PointF& point, int operation) { last_drag_point_ = point; drag_operation_ = operation; - int client_operation = - ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_MOVE; - - if (os_exchange_data_) { - std::unique_ptr<ui::DropTargetEvent> event = CreateDropTargetEvent(point); - // If |os_exchange_data_| has a valid data, |drag_drop_delegate_| returns - // the operation which it expects. - if (drag_drop_delegate_ && event) - client_operation = drag_drop_delegate_->OnDragUpdated(*event); - } + + // If |data_to_drop_| doesn't have data, return that we accept everything. + if (!data_to_drop_) + return ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_MOVE; + + // Ask the delegate what operation it would accept for the current data. + int client_operation = ui::DragDropTypes::DRAG_NONE; + std::unique_ptr<ui::DropTargetEvent> event = + UpdateTargetAndCreateDropEvent(point); + if (drag_drop_delegate_ && event) + client_operation = drag_drop_delegate_->OnDragUpdated(*event); return client_operation; } void DesktopDragDropClientOzone::OnDragDrop( std::unique_ptr<ui::OSExchangeData> data) { - // If it doesn't have |os_exchange_data_|, it needs to update it with |data|. - if (!os_exchange_data_) { - DCHECK(data); - os_exchange_data_ = std::move(data); - std::unique_ptr<ui::DropTargetEvent> event = - CreateDropTargetEvent(last_drag_point_); - // Sends the deferred drag events to |drag_drop_delegate_| before handling - // drop. - if (drag_drop_delegate_ && event) { - drag_drop_delegate_->OnDragEntered(*event); - // TODO(jkim): It doesn't use the return value from 'OnDragUpdated' and - // doesn't have a chance to update the expected operation. - // https://crbug.com/875164 + // If we didn't have |data_to_drop_|, then |drag_drop_delegate_| had never + // been updated, and now it needs to receive deferred enter and update events + // before handling the actual drop. + const bool posponed_enter_and_update = !data_to_drop_; + + // If we had |data_to_drop_| already since the drag had entered the window, + // then we don't expect new data to come now, and vice versa. + DCHECK((data_to_drop_ && !data) || (!data_to_drop_ && data)); + if (!data_to_drop_) + data_to_drop_ = std::move(data); + + // This will call the delegate's OnDragEntered if needed. + auto event = UpdateTargetAndCreateDropEvent(last_drag_point_); + if (drag_drop_delegate_ && event) { + if (posponed_enter_and_update) { + // TODO(https://crbug.com/1014860): deal with drop refusals. + // The delegate's OnDragUpdated returns an operation that the delegate + // would accept. Normally the accepted operation would be propagated + // properly, and if the delegate didn't accept it, the drop would never + // be called, but in this scenario of postponed updates we send all events + // at once. Now we just drop, but perhaps we could call OnDragLeave + // and quit? drag_drop_delegate_->OnDragUpdated(*event); } - } else { - // If it has |os_exchange_data_|, it doesn't expect |data| on OnDragDrop. - DCHECK(!data); + drag_operation_ = + drag_drop_delegate_->OnPerformDrop(*event, std::move(data_to_drop_)); } - PerformDrop(); + ResetDragDropTarget(); } void DesktopDragDropClientOzone::OnDragLeave() { - os_exchange_data_.reset(); + data_to_drop_.reset(); ResetDragDropTarget(); } -void DesktopDragDropClientOzone::OnDragSessionClosed(int dnd_action) { - drag_operation_ = dnd_action; - QuitRunLoop(); +void DesktopDragDropClientOzone::OnWindowDestroyed(aura::Window* window) { + DCHECK_EQ(window, current_window_); + + current_window_->RemoveObserver(this); + current_window_ = nullptr; + drag_drop_delegate_ = nullptr; } -void DesktopDragDropClientOzone::DragDropSessionCompleted() { +void DesktopDragDropClientOzone::OnDragLocationChanged( + const gfx::Point& screen_point_px) { + DCHECK(drag_context_); + + if (!drag_context_->widget) + return; + + const bool dispatch_mouse_event = !drag_context_->last_screen_location_px; + drag_context_->last_screen_location_px = screen_point_px; + if (dispatch_mouse_event) { + // Post a task to dispatch mouse movement event when control returns to the + // message loop. This allows smoother dragging since the events are + // dispatched without waiting for the drag widget updates. + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(&DesktopDragDropClientOzone::UpdateDragWidgetLocation, + weak_factory_.GetWeakPtr())); + } +} + +void DesktopDragDropClientOzone::OnDragOperationChanged( + ui::DragDropTypes::DragOperation operation) { aura::client::CursorClient* cursor_client = aura::client::GetCursorClient(root_window_); if (!cursor_client) return; - cursor_client->SetCursor(initial_cursor_); + ui::mojom::CursorType cursor_type = ui::mojom::CursorType::kNull; + switch (operation) { + case ui::DragDropTypes::DRAG_NONE: + cursor_type = ui::mojom::CursorType::kDndNone; + break; + case ui::DragDropTypes::DRAG_MOVE: + cursor_type = ui::mojom::CursorType::kDndMove; + break; + case ui::DragDropTypes::DRAG_COPY: + cursor_type = ui::mojom::CursorType::kDndCopy; + break; + case ui::DragDropTypes::DRAG_LINK: + cursor_type = ui::mojom::CursorType::kDndLink; + break; + } + cursor_client->SetCursor(cursor_manager_->GetInitializedCursor(cursor_type)); +} + +void DesktopDragDropClientOzone::OnDragFinished(int dnd_action) { + drag_operation_ = dnd_action; + QuitRunLoop(); } void DesktopDragDropClientOzone::QuitRunLoop() { - in_move_loop_ = false; - if (quit_closure_.is_null()) + if (!drag_context_->quit_closure) return; - std::move(quit_closure_).Run(); + std::move(drag_context_->quit_closure).Run(); } std::unique_ptr<ui::DropTargetEvent> -DesktopDragDropClientOzone::CreateDropTargetEvent(const gfx::PointF& location) { +DesktopDragDropClientOzone::UpdateTargetAndCreateDropEvent( + const gfx::PointF& location) { const gfx::Point point(location.x(), location.y()); aura::Window* window = GetTargetWindow(root_window_, point); - if (!window) + if (!window) { + ResetDragDropTarget(); + return nullptr; + } + + auto* new_delegate = aura::client::GetDragDropDelegate(window); + const bool delegate_has_changed = (new_delegate != drag_drop_delegate_); + if (delegate_has_changed) { + ResetDragDropTarget(); + drag_drop_delegate_ = new_delegate; + current_window_ = window; + current_window_->AddObserver(this); + } + + if (!drag_drop_delegate_) return nullptr; - UpdateDragDropDelegate(window); gfx::Point root_location(location.x(), location.y()); root_window_->GetHost()->ConvertScreenInPixelsToDIP(&root_location); gfx::PointF target_location(root_location); aura::Window::ConvertPointToTarget(root_window_, window, &target_location); - return std::make_unique<ui::DropTargetEvent>( - *os_exchange_data_, target_location, gfx::PointF(root_location), + auto event = std::make_unique<ui::DropTargetEvent>( + *data_to_drop_, target_location, gfx::PointF(root_location), drag_operation_); + if (delegate_has_changed) + drag_drop_delegate_->OnDragEntered(*event); + return event; } -void DesktopDragDropClientOzone::UpdateDragDropDelegate(aura::Window* window) { - aura::client::DragDropDelegate* delegate = - aura::client::GetDragDropDelegate(window); - - if (drag_drop_delegate_ == delegate) +void DesktopDragDropClientOzone::UpdateDragWidgetLocation() { + if (!drag_context_) return; - ResetDragDropTarget(); - if (delegate) - drag_drop_delegate_ = delegate; -} + float scale_factor = + ui::GetScaleFactorForNativeView(drag_context_->widget->GetNativeWindow()); + gfx::Point scaled_point = gfx::ScaleToRoundedPoint( + *drag_context_->last_screen_location_px, 1.f / scale_factor); + drag_context_->widget->SetBounds( + gfx::Rect(scaled_point - drag_context_->offset, drag_context_->size)); + drag_context_->widget->StackAtTop(); -void DesktopDragDropClientOzone::ResetDragDropTarget() { - if (!drag_drop_delegate_) - return; - drag_drop_delegate_->OnDragExited(); - drag_drop_delegate_ = nullptr; + drag_context_->last_screen_location_px.reset(); } -void DesktopDragDropClientOzone::PerformDrop() { - std::unique_ptr<ui::DropTargetEvent> event = - CreateDropTargetEvent(last_drag_point_); - if (drag_drop_delegate_ && event) - drag_operation_ = drag_drop_delegate_->OnPerformDrop( - *event, std::move(os_exchange_data_)); - DragDropSessionCompleted(); - ResetDragDropTarget(); +void DesktopDragDropClientOzone::ResetDragDropTarget() { + if (drag_drop_delegate_) { + drag_drop_delegate_->OnDragExited(); + drag_drop_delegate_ = nullptr; + } + if (current_window_) { + current_window_->RemoveObserver(this); + current_window_ = nullptr; + } } } // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.h b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.h index 05b2fd24af6..d4e93838750 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.h @@ -8,7 +8,9 @@ #include <memory> #include "base/callback.h" +#include "base/memory/weak_ptr.h" #include "ui/aura/client/drag_drop_client.h" +#include "ui/aura/window_observer.h" #include "ui/base/cursor/cursor.h" #include "ui/base/dragdrop/os_exchange_data.h" #include "ui/gfx/geometry/point_f.h" @@ -29,17 +31,50 @@ class DropTargetEvent; namespace views { class DesktopNativeCursorManager; +class Widget; class VIEWS_EXPORT DesktopDragDropClientOzone : public aura::client::DragDropClient, - public ui::WmDropHandler { + public ui::WmDragHandler::Delegate, + public ui::WmDropHandler, + public aura::WindowObserver { public: DesktopDragDropClientOzone(aura::Window* root_window, views::DesktopNativeCursorManager* cursor_manager, ui::WmDragHandler* drag_handler); ~DesktopDragDropClientOzone() override; - // Overridden from aura::client::DragDropClient: + private: + friend class DesktopDragDropClientOzoneTest; + + // Holds data related to the drag operation started by this client. + struct DragContext { + DragContext(); + ~DragContext(); + + // Exits the drag loop. + base::OnceClosure quit_closure; + + // Widget that the user drags around. May be nullptr. + std::unique_ptr<Widget> widget; + + // The size of drag image. + gfx::Size size; + + // The offset of |drag_widget_| relative to the mouse position. + gfx::Vector2d offset; + + // The last received drag location. The drag widget is moved asynchronously + // so its position is updated when the UI thread has time for that. When + // the first change to the location happens, a call to UpdateDragWidget() + // is posted, and this location is set. The location can be updated a few + // more times until the posted task is executed, but no more than a single + // call to UpdateDragWidget() is scheduled at any time; this optional is set + // means that the task is scheduled. + base::Optional<gfx::Point> last_screen_location_px; + }; + + // aura::client::DragDropClient int StartDragAndDrop(std::unique_ptr<ui::OSExchangeData> data, aura::Window* root_window, aura::Window* source_window, @@ -51,7 +86,7 @@ class VIEWS_EXPORT DesktopDragDropClientOzone void AddObserver(aura::client::DragDropClientObserver* observer) override; void RemoveObserver(aura::client::DragDropClientObserver* observer) override; - // Overridden from void ui::WmDropHandler: + // ui::WmDropHandler void OnDragEnter(const gfx::PointF& point, std::unique_ptr<ui::OSExchangeData> data, int operation) override; @@ -59,24 +94,33 @@ class VIEWS_EXPORT DesktopDragDropClientOzone void OnDragDrop(std::unique_ptr<ui::OSExchangeData> data) override; void OnDragLeave() override; - void OnDragSessionClosed(int operation); + // aura::WindowObserver + void OnWindowDestroyed(aura::Window* window) override; + + // ui::WmDragHandler::Delegate + void OnDragLocationChanged(const gfx::Point& screen_point_px) override; + void OnDragOperationChanged( + ui::DragDropTypes::DragOperation operation) override; + void OnDragFinished(int operation) override; - private: - void DragDropSessionCompleted(); void QuitRunLoop(); - // Returns a DropTargetEvent to be passed to the DragDropDelegate, or null to - // abort the drag. - std::unique_ptr<ui::DropTargetEvent> CreateDropTargetEvent( + // Returns a DropTargetEvent to be passed to the DragDropDelegate. + // Updates the delegate if needed, which in its turn calls their + // OnDragExited/OnDragEntered, so after getting the event the delegate + // is ready to accept OnDragUpdated or OnPerformDrop. Returns nullptr if + // drop is not possible. + std::unique_ptr<ui::DropTargetEvent> UpdateTargetAndCreateDropEvent( const gfx::PointF& point); // Updates |drag_drop_delegate_| along with |window|. void UpdateDragDropDelegate(aura::Window* window); - // Resets |drag_drop_delegate_|. - void ResetDragDropTarget(); + // Updates |drag_widget_| so it is aligned with the last drag location. + void UpdateDragWidgetLocation(); - void PerformDrop(); + // Resets |drag_drop_delegate_|. Calls OnDragExited() before resetting. + void ResetDragDropTarget(); aura::Window* const root_window_; @@ -84,26 +128,24 @@ class VIEWS_EXPORT DesktopDragDropClientOzone ui::WmDragHandler* const drag_handler_; + // Last window under the mouse. + aura::Window* current_window_ = nullptr; // The delegate corresponding to the window located at the mouse position. aura::client::DragDropDelegate* drag_drop_delegate_ = nullptr; // The data to be delivered through the drag and drop. - std::unique_ptr<ui::OSExchangeData> os_exchange_data_ = nullptr; + std::unique_ptr<ui::OSExchangeData> data_to_drop_; - // The most recent native coordinates of a drag. + // The most recent native coordinates of an incoming drag. Updated while + // the mouse is moved, and used at dropping. gfx::PointF last_drag_point_; - // Cursor in use prior to the move loop starting. Restored when the move loop - // quits. - gfx::NativeCursor initial_cursor_; - - base::OnceClosure quit_closure_; - // The operation bitfield. int drag_operation_ = 0; - // The flag that controls whether it has a nested run loop. - bool in_move_loop_ = false; + std::unique_ptr<DragContext> drag_context_; + + base::WeakPtrFactory<DesktopDragDropClientOzone> weak_factory_{this}; DISALLOW_COPY_AND_ASSIGN(DesktopDragDropClientOzone); }; diff --git a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone_unittest.cc b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone_unittest.cc index d159e5130b8..cfb7bcca00b 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone_unittest.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone_unittest.cc @@ -66,8 +66,8 @@ class FakePlatformWindow : public ui::PlatformWindow, public ui::WmDragHandler { void StartDrag(const OSExchangeData& data, int operation, gfx::NativeCursor cursor, - base::OnceCallback<void(int)> callback) override { - drag_closed_callback_ = std::move(callback); + WmDragHandler::Delegate* delegate) override { + drag_handler_delegate_ = delegate; source_data_ = std::make_unique<OSExchangeData>(data.provider().Clone()); base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, @@ -107,7 +107,7 @@ class FakePlatformWindow : public ui::PlatformWindow, public ui::WmDragHandler { } void CloseDrag(uint32_t dnd_action) { - std::move(drag_closed_callback_).Run(dnd_action); + drag_handler_delegate_->OnDragFinished(dnd_action); } void ProcessDrag(std::unique_ptr<OSExchangeData> data, int operation) { @@ -119,7 +119,7 @@ class FakePlatformWindow : public ui::PlatformWindow, public ui::WmDragHandler { } private: - base::OnceCallback<void(int)> drag_closed_callback_; + WmDragHandler::Delegate* drag_handler_delegate_ = nullptr; std::unique_ptr<ui::OSExchangeData> source_data_; DISALLOW_COPY_AND_ASSIGN(FakePlatformWindow); @@ -288,10 +288,77 @@ TEST_F(DesktopDragDropClientOzoneTest, ReceiveDrag) { EXPECT_EQ(sample_data, string_data); EXPECT_EQ(1, dragdrop_delegate_->num_enters()); - EXPECT_EQ(1, dragdrop_delegate_->num_enters()); EXPECT_EQ(1, dragdrop_delegate_->num_updates()); EXPECT_EQ(1, dragdrop_delegate_->num_drops()); EXPECT_EQ(1, dragdrop_delegate_->num_exits()); } +TEST_F(DesktopDragDropClientOzoneTest, TargetDestroyedDuringDrag) { + const int suggested_operation = + ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_MOVE; + + // Set the operation which the destination can accept. + dragdrop_delegate_->SetOperation(ui::DragDropTypes::DRAG_MOVE); + + // Set the data which will be delivered. + const base::string16 sample_data = base::ASCIIToUTF16("ReceiveDrag"); + std::unique_ptr<ui::OSExchangeData> data = + std::make_unique<ui::OSExchangeData>(); + data->SetString(sample_data); + + // Simulate that the drag enter/motion/leave events happen with the + // |suggested_operation| in the main window. + platform_window_->OnDragEnter(gfx::PointF(), std::move(data), + suggested_operation); + platform_window_->OnDragMotion(gfx::PointF(), suggested_operation); + platform_window_->OnDragLeave(); + + // Create another window with its own DnD facility and simulate that the drag + // enters it and then the window is destroyed. + auto another_widget = std::make_unique<Widget>(); + Widget::InitParams params(Widget::InitParams::TYPE_WINDOW); + params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.bounds = gfx::Rect(100, 100); + another_widget->Init(std::move(params)); + another_widget->Show(); + + aura::Window* another_window = another_widget->GetNativeWindow(); + auto another_dragdrop_delegate = std::make_unique<FakeDragDropDelegate>(); + aura::client::SetDragDropDelegate(another_window, + another_dragdrop_delegate.get()); + another_dragdrop_delegate->SetOperation(ui::DragDropTypes::DRAG_COPY); + + auto another_cursor_manager = std::make_unique<DesktopNativeCursorManager>(); + auto another_platform_window = std::make_unique<FakePlatformWindow>(); + ui::WmDragHandler* drag_handler = + ui::GetWmDragHandler(*(another_platform_window)); + auto another_client = std::make_unique<DesktopDragDropClientOzone>( + another_window, another_cursor_manager.get(), drag_handler); + SetWmDropHandler(another_platform_window.get(), another_client.get()); + + std::unique_ptr<ui::OSExchangeData> another_data = + std::make_unique<ui::OSExchangeData>(); + another_data->SetString(sample_data); + another_platform_window->OnDragEnter(gfx::PointF(), std::move(another_data), + suggested_operation); + another_platform_window->OnDragMotion(gfx::PointF(), suggested_operation); + + another_widget->CloseWithReason(Widget::ClosedReason::kUnspecified); + another_widget.reset(); + + // The main window should have the typical record of a drag started and left. + EXPECT_EQ(1, dragdrop_delegate_->num_enters()); + EXPECT_EQ(1, dragdrop_delegate_->num_updates()); + EXPECT_EQ(0, dragdrop_delegate_->num_drops()); + EXPECT_EQ(1, dragdrop_delegate_->num_exits()); + + // As the target window has closed and we have never provided another one, + // the number of exits should be zero despite that the platform window has + // notified the client about leaving the drag. + EXPECT_EQ(1, another_dragdrop_delegate->num_enters()); + EXPECT_EQ(1, another_dragdrop_delegate->num_updates()); + EXPECT_EQ(0, another_dragdrop_delegate->num_drops()); + EXPECT_EQ(0, another_dragdrop_delegate->num_exits()); +} + } // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.cc b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.cc index 806e4a883a0..e3bd6355071 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.cc @@ -6,19 +6,24 @@ #include <memory> -#include "base/metrics/histogram_macros.h" #include "ui/base/dragdrop/drag_drop_types.h" #include "ui/base/dragdrop/drag_source_win.h" #include "ui/base/dragdrop/drop_target_event.h" #include "ui/base/dragdrop/os_exchange_data_provider_win.h" +#include "ui/base/win/event_creation_utils.h" +#include "ui/display/win/screen_win.h" #include "ui/views/widget/desktop_aura/desktop_drop_target_win.h" #include "ui/views/widget/desktop_aura/desktop_window_tree_host_win.h" namespace views { -DesktopDragDropClientWin::DesktopDragDropClientWin(aura::Window* root_window, - HWND window) - : drag_drop_in_progress_(false), drag_operation_(0) { +DesktopDragDropClientWin::DesktopDragDropClientWin( + aura::Window* root_window, + HWND window, + DesktopWindowTreeHostWin* desktop_host) + : drag_drop_in_progress_(false), + drag_operation_(0), + desktop_host_(desktop_host) { drop_target_ = new DesktopDropTargetWin(root_window); drop_target_->Init(window); } @@ -37,24 +42,36 @@ int DesktopDragDropClientWin::StartDragAndDrop( ui::DragDropTypes::DragEventSource source) { drag_drop_in_progress_ = true; drag_operation_ = operation; - + if (source == ui::DragDropTypes::DRAG_EVENT_SOURCE_TOUCH) { + gfx::Point screen_point = display::win::ScreenWin::DIPToScreenPoint( + {screen_location.x(), screen_location.y()}); + // Send a mouse down and mouse move before do drag drop runs its own event + // loop. This is required for ::DoDragDrop to start the drag. + ui::SendMouseEvent(screen_point, + MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_ABSOLUTE); + ui::SendMouseEvent(screen_point, MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE); + desktop_host_->SetInTouchDrag(true); + } base::WeakPtr<DesktopDragDropClientWin> alive(weak_factory_.GetWeakPtr()); drag_source_ = ui::DragSourceWin::Create(); Microsoft::WRL::ComPtr<ui::DragSourceWin> drag_source_copy = drag_source_; drag_source_copy->set_data(data.get()); - ui::OSExchangeDataProviderWin::GetDataObjectImpl(*data.get()) - ->set_in_drag_loop(true); + ui::OSExchangeDataProviderWin::GetDataObjectImpl(*data)->set_in_drag_loop( + true); DWORD effect; - UMA_HISTOGRAM_ENUMERATION("Event.DragDrop.Start", source, - ui::DragDropTypes::DRAG_EVENT_SOURCE_COUNT); - - HRESULT result = DoDragDrop( + HRESULT result = ::DoDragDrop( ui::OSExchangeDataProviderWin::GetIDataObject(*data.get()), drag_source_.Get(), ui::DragDropTypes::DragOperationToDropEffect(operation), &effect); + if (alive && source == ui::DragDropTypes::DRAG_EVENT_SOURCE_TOUCH) { + desktop_host_->SetInTouchDrag(false); + // Gesture state gets left in a state where you can't start + // another drag, unless it's cleaned up. + source_window->CleanupGestureState(); + } drag_source_copy->set_data(nullptr); if (alive) @@ -63,17 +80,7 @@ int DesktopDragDropClientWin::StartDragAndDrop( if (result != DRAGDROP_S_DROP) effect = DROPEFFECT_NONE; - int drag_operation = ui::DragDropTypes::DropEffectToDragOperation(effect); - - if (drag_operation == ui::DragDropTypes::DRAG_NONE) { - UMA_HISTOGRAM_ENUMERATION("Event.DragDrop.Cancel", source, - ui::DragDropTypes::DRAG_EVENT_SOURCE_COUNT); - } else { - UMA_HISTOGRAM_ENUMERATION("Event.DragDrop.Drop", source, - ui::DragDropTypes::DRAG_EVENT_SOURCE_COUNT); - } - - return drag_operation; + return ui::DragDropTypes::DropEffectToDragOperation(effect); } void DesktopDragDropClientWin::DragCancel() { diff --git a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.h b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.h index 74c40adff7e..a55fe963a04 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.h @@ -28,11 +28,14 @@ class DragSourceWin; namespace views { class DesktopDropTargetWin; +class DesktopWindowTreeHostWin; class VIEWS_EXPORT DesktopDragDropClientWin : public aura::client::DragDropClient { public: - DesktopDragDropClientWin(aura::Window* root_window, HWND window); + DesktopDragDropClientWin(aura::Window* root_window, + HWND window, + DesktopWindowTreeHostWin* desktop_host); ~DesktopDragDropClientWin() override; // Overridden from aura::client::DragDropClient: @@ -58,6 +61,11 @@ class VIEWS_EXPORT DesktopDragDropClientWin scoped_refptr<DesktopDropTargetWin> drop_target_; + // |this| will get deleted DesktopNativeWidgetAura is notified that the + // DesktopWindowTreeHost is being destroyed. So desktop_host_ should outlive + // |this|. + DesktopWindowTreeHostWin* desktop_host_ = nullptr; + base::WeakPtrFactory<DesktopDragDropClientWin> weak_factory_{this}; DISALLOW_COPY_AND_ASSIGN(DesktopDragDropClientWin); diff --git a/chromium/ui/views/widget/desktop_aura/desktop_drop_target_win.cc b/chromium/ui/views/widget/desktop_aura/desktop_drop_target_win.cc index a1703ed51c4..eac8953797e 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_drop_target_win.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_drop_target_win.cc @@ -6,7 +6,6 @@ #include <utility> -#include "base/metrics/histogram_macros.h" #include "base/win/win_util.h" #include "ui/aura/client/drag_drop_client.h" #include "ui/aura/client/drag_drop_delegate.h" @@ -80,8 +79,6 @@ DWORD DesktopDropTargetWin::OnDragOver(IDataObject* data_object, if (delegate) drag_operation = delegate->OnDragUpdated(*event); - UMA_HISTOGRAM_BOOLEAN("Event.DragDrop.AcceptDragUpdate", - drag_operation != ui::DragDropTypes::DRAG_NONE); return ui::DragDropTypes::DragOperationToDropEffect(drag_operation); } @@ -98,14 +95,8 @@ DWORD DesktopDropTargetWin::OnDrop(IDataObject* data_object, std::unique_ptr<ui::DropTargetEvent> event; DragDropDelegate* delegate; Translate(data_object, key_state, position, effect, &data, &event, &delegate); - if (delegate) { + if (delegate) drag_operation = delegate->OnPerformDrop(*event, std::move(data)); - DragDropClient* client = aura::client::GetDragDropClient(root_window_); - if (client && !client->IsDragDropInProgress() && - drag_operation != ui::DragDropTypes::DRAG_NONE) { - UMA_HISTOGRAM_COUNTS_1M("Event.DragDrop.ExternalOriginDrop", 1); - } - } if (target_window_) { target_window_->RemoveObserver(this); target_window_ = nullptr; diff --git a/chromium/ui/views/widget/desktop_aura/desktop_screen.cc b/chromium/ui/views/widget/desktop_aura/desktop_screen.cc index b656c14e4f7..50ac4c71dbf 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_screen.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen.cc @@ -17,7 +17,7 @@ void InstallDesktopScreenIfNecessary() { // The screen may have already been set in test initialization. if (!display::Screen::GetScreen()) - display::Screen::SetScreenInstance(CreateDesktopScreen()); + CreateDesktopScreen(); } } // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/desktop_screen_linux.cc b/chromium/ui/views/widget/desktop_aura/desktop_screen_linux.cc new file mode 100644 index 00000000000..6a11abce534 --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_linux.cc @@ -0,0 +1,35 @@ +// Copyright 2020 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 "ui/views/widget/desktop_aura/desktop_screen.h" + +#include "base/notreached.h" + +#if defined(USE_X11) +#include "ui/views/widget/desktop_aura/desktop_screen_x11.h" +#endif + +#if defined(USE_OZONE) +#include "ui/base/ui_base_features.h" +#include "ui/views/widget/desktop_aura/desktop_screen_ozone.h" +#endif + +namespace views { + +display::Screen* CreateDesktopScreen() { +#if defined(USE_OZONE) + if (features::IsUsingOzonePlatform()) + return new DesktopScreenOzone(); +#endif +#if defined(USE_X11) + auto* screen = new DesktopScreenX11(); + screen->Init(); + return screen; +#else + NOTREACHED(); + return nullptr; +#endif +} + +} // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/desktop_screen_ozone.cc b/chromium/ui/views/widget/desktop_aura/desktop_screen_ozone.cc index 6f9d29fbcbb..05fc9cde057 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_screen_ozone.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_ozone.cc @@ -4,6 +4,7 @@ #include "ui/views/widget/desktop_aura/desktop_screen_ozone.h" +#include "build/build_config.h" #include "ui/aura/screen_ozone.h" #include "ui/views/widget/desktop_aura/desktop_screen.h" #include "ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h" @@ -22,8 +23,13 @@ gfx::NativeWindow DesktopScreenOzone::GetNativeWindowFromAcceleratedWidget( widget); } +// To avoid multiple definitions when use_x11 && use_ozone is true, disable this +// factory method for OS_LINUX as Linux has a factory method that decides what +// screen to use based on IsUsingOzonePlatform feature flag. +#if !defined(OS_LINUX) display::Screen* CreateDesktopScreen() { return new DesktopScreenOzone(); } +#endif } // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/desktop_screen_win.cc b/chromium/ui/views/widget/desktop_aura/desktop_screen_win.cc index 963e7053e7a..e9bb45ad238 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_screen_win.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_win.cc @@ -13,7 +13,9 @@ namespace views { DesktopScreenWin::DesktopScreenWin() = default; -DesktopScreenWin::~DesktopScreenWin() = default; +DesktopScreenWin::~DesktopScreenWin() { + display::Screen::SetScreenInstance(old_screen_); +} HWND DesktopScreenWin::GetHWNDFromNativeWindow(gfx::NativeWindow window) const { aura::WindowTreeHost* host = window->GetHost(); diff --git a/chromium/ui/views/widget/desktop_aura/desktop_screen_win.h b/chromium/ui/views/widget/desktop_aura/desktop_screen_win.h index 97442c424d8..52f54f2e6b8 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_screen_win.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_win.h @@ -21,6 +21,8 @@ class VIEWS_EXPORT DesktopScreenWin : public display::win::ScreenWin { // display::win::ScreenWin: HWND GetHWNDFromNativeWindow(gfx::NativeWindow window) const override; gfx::NativeWindow GetNativeWindowFromHWND(HWND hwnd) const override; + + display::Screen* const old_screen_ = display::Screen::SetScreenInstance(this); }; } // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.cc b/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.cc index 4f4a5d1c395..084ba94bd9a 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.cc @@ -32,7 +32,9 @@ DesktopScreenX11::DesktopScreenX11() { display_scale_factor_observer_.Add(LinuxUI::instance()); } -DesktopScreenX11::~DesktopScreenX11() = default; +DesktopScreenX11::~DesktopScreenX11() { + display::Screen::SetScreenInstance(old_screen_); +} void DesktopScreenX11::Init() { if (x11_display_manager_->IsXrandrAvailable() && @@ -60,12 +62,11 @@ bool DesktopScreenX11::IsWindowUnderCursor(gfx::NativeWindow window) { gfx::NativeWindow DesktopScreenX11::GetWindowAtScreenPoint( const gfx::Point& point) { - auto accelerated_widget = - ui::X11TopmostWindowFinder().FindLocalProcessWindowAt( - gfx::ConvertPointToPixel(GetXDisplayScaleFactor(), point), {}); - return accelerated_widget + auto window = ui::X11TopmostWindowFinder().FindLocalProcessWindowAt( + gfx::ConvertPointToPixel(GetXDisplayScaleFactor(), point), {}); + return window != x11::Window::None ? views::DesktopWindowTreeHostPlatform::GetContentWindowForWidget( - static_cast<gfx::AcceleratedWidget>(accelerated_widget)) + window) : nullptr; } @@ -75,13 +76,12 @@ gfx::NativeWindow DesktopScreenX11::GetLocalProcessWindowAtPoint( std::set<gfx::AcceleratedWidget> ignore_widgets; for (auto* const window : ignore) ignore_widgets.emplace(window->GetHost()->GetAcceleratedWidget()); - auto accelerated_widget = - ui::X11TopmostWindowFinder().FindLocalProcessWindowAt( - gfx::ConvertPointToPixel(GetXDisplayScaleFactor(), point), - ignore_widgets); - return accelerated_widget + auto window = ui::X11TopmostWindowFinder().FindLocalProcessWindowAt( + gfx::ConvertPointToPixel(GetXDisplayScaleFactor(), point), + ignore_widgets); + return window != x11::Window::None ? views::DesktopWindowTreeHostPlatform::GetContentWindowForWidget( - static_cast<gfx::AcceleratedWidget>(accelerated_widget)) + window) : nullptr; } @@ -148,7 +148,7 @@ std::string DesktopScreenX11::GetCurrentWorkspace() { return x11_display_manager_->GetCurrentWorkspace(); } -bool DesktopScreenX11::DispatchXEvent(XEvent* event) { +bool DesktopScreenX11::DispatchXEvent(x11::Event* event) { return x11_display_manager_->CanProcessEvent(*event) && x11_display_manager_->ProcessEvent(event); } @@ -176,12 +176,4 @@ float DesktopScreenX11::GetXDisplayScaleFactor() const { : 1.0f; } -//////////////////////////////////////////////////////////////////////////////// - -display::Screen* CreateDesktopScreen() { - auto* screen = new DesktopScreenX11; - screen->Init(); - return screen; -} - } // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.h b/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.h index b391f41caf2..54954d61993 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.h @@ -12,6 +12,7 @@ #include "ui/base/x/x11_display_manager.h" #include "ui/display/screen.h" #include "ui/events/platform/x11/x11_event_source.h" +#include "ui/gfx/x/event.h" #include "ui/views/linux_ui/device_scale_factor_observer.h" #include "ui/views/linux_ui/linux_ui.h" #include "ui/views/views_export.h" @@ -19,10 +20,6 @@ namespace views { class DesktopScreenX11Test; -namespace test { -class DesktopScreenX11TestApi; -} - // Screen implementation that talks to XRandR class VIEWS_EXPORT DesktopScreenX11 : public display::Screen, public ui::XEventDispatcher, @@ -57,7 +54,7 @@ class VIEWS_EXPORT DesktopScreenX11 : public display::Screen, std::string GetCurrentWorkspace() override; // ui::XEventDispatcher: - bool DispatchXEvent(XEvent* event) override; + bool DispatchXEvent(x11::Event* event) override; // DeviceScaleFactorObserver: void OnDeviceScaleFactorChanged() override; @@ -66,12 +63,12 @@ class VIEWS_EXPORT DesktopScreenX11 : public display::Screen, private: friend class DesktopScreenX11Test; - friend class test::DesktopScreenX11TestApi; // ui::XDisplayManager::Delegate: void OnXDisplayListUpdated() override; float GetXDisplayScaleFactor() const override; + display::Screen* const old_screen_ = display::Screen::SetScreenInstance(this); std::unique_ptr<ui::XDisplayManager> x11_display_manager_ = std::make_unique<ui::XDisplayManager>(this); ScopedObserver<LinuxUI, diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.cc b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.cc index 4168fa1a51f..61d8b3a0678 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.cc @@ -184,23 +184,6 @@ Widget::MoveLoopResult DesktopWindowTreeHostLinux::RunMoveLoop( escape_behavior); } -void DesktopWindowTreeHostLinux::OnDisplayMetricsChanged( - const display::Display& display, - uint32_t changed_metrics) { - aura::WindowTreeHost::OnDisplayMetricsChanged(display, changed_metrics); - - if ((changed_metrics & DISPLAY_METRIC_DEVICE_SCALE_FACTOR) && - display::Screen::GetScreen()->GetDisplayNearestWindow(window()).id() == - display.id()) { - // When the scale factor changes, also pretend that a resize - // occurred so that the window layout will be refreshed and a - // compositor redraw will be scheduled. This is weird, but works. - // TODO(thomasanderson): Figure out a more direct way of doing - // this. - OnHostResizedInPixels(GetBoundsInPixels().size()); - } -} - void DesktopWindowTreeHostLinux::DispatchEvent(ui::Event* event) { // The input can be disabled and the widget marked as non-active in case of // opened file-dialogs. diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h index 79bef5fe949..bec4d76adb1 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h @@ -89,12 +89,10 @@ class VIEWS_EXPORT DesktopWindowTreeHostLinux const ui::X11Extension* GetX11Extension() const; private: - friend class DesktopWindowTreeHostX11Test; FRIEND_TEST_ALL_PREFIXES(DesktopWindowTreeHostLinuxTest, HitTest); - - // Overridden from display::DisplayObserver via aura::WindowTreeHost: - void OnDisplayMetricsChanged(const display::Display& display, - uint32_t changed_metrics) override; + FRIEND_TEST_ALL_PREFIXES(DesktopWindowTreeHostLinuxTest, MouseNCEvents); + FRIEND_TEST_ALL_PREFIXES(DesktopWindowTreeHostLinuxHighDPITest, + MouseNCEvents); // DesktopWindowTreeHostPlatform overrides: void AddAdditionalInitProperties( diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_linux_unittest.cc b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_linux_unittest.cc new file mode 100644 index 00000000000..15d8c29db55 --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_linux_unittest.cc @@ -0,0 +1,228 @@ +// Copyright 2020 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 "ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h" + +#include "base/command_line.h" +#include "ui/base/hit_test.h" +#include "ui/display/display_switches.h" +#include "ui/views/test/views_test_base.h" +#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" +#include "ui/views/widget/widget_delegate.h" + +namespace views { + +namespace { +// A NonClientFrameView with a window mask with the bottom right corner cut out. +class ShapedNonClientFrameView : public NonClientFrameView { + public: + ShapedNonClientFrameView() = default; + + ~ShapedNonClientFrameView() override = default; + + // NonClientFrameView: + gfx::Rect GetBoundsForClientView() const override { return bounds(); } + gfx::Rect GetWindowBoundsForClientBounds( + const gfx::Rect& client_bounds) const override { + return client_bounds; + } + int NonClientHitTest(const gfx::Point& point) override { + // Fake bottom for non client event test. + if (point == gfx::Point(500, 500)) + return HTBOTTOM; + return HTNOWHERE; + } + void GetWindowMask(const gfx::Size& size, SkPath* window_mask) override { + int right = size.width(); + int bottom = size.height(); + + window_mask->moveTo(0, 0); + window_mask->lineTo(0, bottom); + window_mask->lineTo(right, bottom); + window_mask->lineTo(right, 10); + window_mask->lineTo(right - 10, 10); + window_mask->lineTo(right - 10, 0); + window_mask->close(); + } + void ResetWindowControls() override {} + void UpdateWindowIcon() override {} + void UpdateWindowTitle() override {} + void SizeConstraintsChanged() override {} + + bool GetAndResetLayoutRequest() { + bool layout_requested = layout_requested_; + layout_requested_ = false; + return layout_requested; + } + + private: + void Layout() override { layout_requested_ = true; } + + bool layout_requested_ = false; + + DISALLOW_COPY_AND_ASSIGN(ShapedNonClientFrameView); +}; + +class ShapedWidgetDelegate : public WidgetDelegateView { + public: + ShapedWidgetDelegate() = default; + + ~ShapedWidgetDelegate() override = default; + + // WidgetDelegateView: + NonClientFrameView* CreateNonClientFrameView(Widget* widget) override { + return new ShapedNonClientFrameView; + } + + private: + DISALLOW_COPY_AND_ASSIGN(ShapedWidgetDelegate); +}; + +class MouseEventRecorder : public ui::EventHandler { + public: + MouseEventRecorder() = default; + ~MouseEventRecorder() override = default; + + void Reset() { mouse_events_.clear(); } + + const std::vector<ui::MouseEvent>& mouse_events() const { + return mouse_events_; + } + + private: + // ui::EventHandler: + void OnMouseEvent(ui::MouseEvent* mouse) override { + mouse_events_.push_back(*mouse); + } + + std::vector<ui::MouseEvent> mouse_events_; + + DISALLOW_COPY_AND_ASSIGN(MouseEventRecorder); +}; + +} // namespace + +class DesktopWindowTreeHostLinuxTest : public ViewsTestBase { + public: + DesktopWindowTreeHostLinuxTest() = default; + DesktopWindowTreeHostLinuxTest(const DesktopWindowTreeHostLinuxTest&) = + delete; + DesktopWindowTreeHostLinuxTest& operator=( + const DesktopWindowTreeHostLinuxTest&) = delete; + ~DesktopWindowTreeHostLinuxTest() override = default; + + void SetUp() override { + set_native_widget_type(NativeWidgetType::kDesktop); + + ViewsTestBase::SetUp(); + } + + protected: + // Creates a widget of size 100x100. + std::unique_ptr<Widget> CreateWidget(WidgetDelegate* delegate) { + std::unique_ptr<Widget> widget(new Widget); + Widget::InitParams params(Widget::InitParams::TYPE_WINDOW); + params.delegate = delegate; + params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.remove_standard_frame = true; + params.bounds = gfx::Rect(100, 100, 100, 100); + widget->Init(std::move(params)); + return widget; + } +}; + +TEST_F(DesktopWindowTreeHostLinuxTest, ChildWindowDestructionDuringTearDown) { + Widget parent_widget; + Widget::InitParams parent_params = + CreateParams(Widget::InitParams::TYPE_WINDOW); + parent_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + parent_widget.Init(std::move(parent_params)); + parent_widget.Show(); + + Widget child_widget; + Widget::InitParams child_params = + CreateParams(Widget::InitParams::TYPE_WINDOW); + child_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + child_params.parent = parent_widget.GetNativeWindow(); + child_widget.Init(std::move(child_params)); + child_widget.Show(); + + // Sanity check that the two widgets each have their own XID. + ASSERT_NE(parent_widget.GetNativeWindow()->GetHost()->GetAcceleratedWidget(), + child_widget.GetNativeWindow()->GetHost()->GetAcceleratedWidget()); + Widget::CloseAllSecondaryWidgets(); + EXPECT_TRUE(DesktopWindowTreeHostLinux::GetAllOpenWindows().empty()); +} + +TEST_F(DesktopWindowTreeHostLinuxTest, MouseNCEvents) { + std::unique_ptr<Widget> widget = CreateWidget(new ShapedWidgetDelegate()); + widget->Show(); + + base::RunLoop().RunUntilIdle(); + + widget->SetBounds(gfx::Rect(100, 100, 501, 501)); + + base::RunLoop().RunUntilIdle(); + + MouseEventRecorder recorder; + widget->GetNativeWindow()->AddPreTargetHandler(&recorder); + + auto* host_linux = static_cast<DesktopWindowTreeHostLinux*>( + widget->GetNativeWindow()->GetHost()); + ASSERT_TRUE(host_linux); + + ui::MouseEvent event(ui::ET_MOUSE_PRESSED, gfx::PointF(500, 500), + gfx::PointF(500, 500), base::TimeTicks::Now(), 0, 0, {}); + host_linux->DispatchEvent(&event); + + ASSERT_EQ(1u, recorder.mouse_events().size()); + EXPECT_EQ(ui::ET_MOUSE_PRESSED, recorder.mouse_events()[0].type()); + EXPECT_TRUE(recorder.mouse_events()[0].flags() & ui::EF_IS_NON_CLIENT); + + widget->GetNativeWindow()->RemovePreTargetHandler(&recorder); +} + +class DesktopWindowTreeHostLinuxHighDPITest + : public DesktopWindowTreeHostLinuxTest { + public: + DesktopWindowTreeHostLinuxHighDPITest() = default; + ~DesktopWindowTreeHostLinuxHighDPITest() override = default; + + private: + void SetUp() override { + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + command_line->AppendSwitchASCII(switches::kForceDeviceScaleFactor, "2"); + + DesktopWindowTreeHostLinuxTest::SetUp(); + } +}; + +TEST_F(DesktopWindowTreeHostLinuxHighDPITest, MouseNCEvents) { + std::unique_ptr<Widget> widget = CreateWidget(new ShapedWidgetDelegate()); + widget->Show(); + + widget->SetBounds(gfx::Rect(100, 100, 1000, 1000)); + base::RunLoop().RunUntilIdle(); + + MouseEventRecorder recorder; + widget->GetNativeWindow()->AddPreTargetHandler(&recorder); + + auto* host_linux = static_cast<DesktopWindowTreeHostLinux*>( + widget->GetNativeWindow()->GetHost()); + ASSERT_TRUE(host_linux); + + ui::MouseEvent event(ui::ET_MOUSE_PRESSED, gfx::PointF(1001, 1001), + gfx::PointF(1001, 1001), base::TimeTicks::Now(), 0, 0, + {}); + host_linux->DispatchEvent(&event); + + EXPECT_EQ(1u, recorder.mouse_events().size()); + EXPECT_EQ(gfx::Point(500, 500), recorder.mouse_events()[0].location()); + EXPECT_EQ(ui::ET_MOUSE_PRESSED, recorder.mouse_events()[0].type()); + EXPECT_TRUE(recorder.mouse_events()[0].flags() & ui::EF_IS_NON_CLIENT); + + widget->GetNativeWindow()->RemovePreTargetHandler(&recorder); +} + +} // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_platform_unittest.cc b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_platform_unittest.cc index 62e2a8fd5ab..c9119b8606c 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_platform_unittest.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_platform_unittest.cc @@ -143,4 +143,68 @@ TEST_F(DesktopWindowTreeHostPlatformTest, CallOnNativeWidgetVisibilityChanged) { EXPECT_TRUE(observer.visible()); } +// Tests that the minimization information is propagated to the content window. +TEST_F(DesktopWindowTreeHostPlatformTest, + ToggleMinimizePropogateToContentWindow) { + std::unique_ptr<Widget> widget = CreateWidgetWithNativeWidget(); + widget->Show(); + + auto* host_platform = DesktopWindowTreeHostPlatform::GetHostForWidget( + widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget()); + ASSERT_TRUE(host_platform); + + EXPECT_TRUE(widget->GetNativeWindow()->IsVisible()); + + // Pretend a PlatformWindow enters the minimized state. + host_platform->OnWindowStateChanged(ui::PlatformWindowState::kMinimized); + + EXPECT_FALSE(widget->GetNativeWindow()->IsVisible()); + + // Pretend a PlatformWindow exits the minimized state. + host_platform->OnWindowStateChanged(ui::PlatformWindowState::kNormal); + EXPECT_TRUE(widget->GetNativeWindow()->IsVisible()); +} + +// A Widget that allows setting the min/max size for the widget. +class CustomSizeWidget : public Widget { + public: + CustomSizeWidget() = default; + ~CustomSizeWidget() override = default; + + void set_min_size(const gfx::Size& size) { min_size_ = size; } + void set_max_size(const gfx::Size& size) { max_size_ = size; } + + // Widget: + gfx::Size GetMinimumSize() const override { return min_size_; } + gfx::Size GetMaximumSize() const override { return max_size_; } + + private: + gfx::Size min_size_; + gfx::Size max_size_; + + DISALLOW_COPY_AND_ASSIGN(CustomSizeWidget); +}; + +TEST_F(DesktopWindowTreeHostPlatformTest, SetBoundsWithMinMax) { + CustomSizeWidget widget; + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); + params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.bounds = gfx::Rect(200, 100); + widget.Init(std::move(params)); + widget.Show(); + + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(gfx::Size(200, 100).ToString(), + widget.GetWindowBoundsInScreen().size().ToString()); + widget.SetBounds(gfx::Rect(300, 200)); + EXPECT_EQ(gfx::Size(300, 200).ToString(), + widget.GetWindowBoundsInScreen().size().ToString()); + + widget.set_min_size(gfx::Size(100, 100)); + widget.SetBounds(gfx::Rect(50, 500)); + EXPECT_EQ(gfx::Size(100, 500).ToString(), + widget.GetWindowBoundsInScreen().size().ToString()); +} + } // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc index 6051aadee62..b5430b871cf 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc @@ -25,6 +25,7 @@ #include "ui/base/cursor/cursor_loader_win.h" #include "ui/base/ime/input_method.h" #include "ui/base/ui_base_features.h" +#include "ui/base/win/event_creation_utils.h" #include "ui/base/win/shell.h" #include "ui/compositor/paint_context.h" #include "ui/display/win/dpi.h" @@ -32,6 +33,7 @@ #include "ui/events/keyboard_hook.h" #include "ui/events/keycodes/dom/dom_code.h" #include "ui/events/keycodes/dom/dom_keyboard_layout_map.h" +#include "ui/events/platform/platform_event_source.h" #include "ui/gfx/geometry/insets.h" #include "ui/gfx/geometry/vector2d.h" #include "ui/gfx/native_widget_types.h" @@ -118,6 +120,10 @@ aura::Window* DesktopWindowTreeHostWin::GetContentWindowForHWND(HWND hwnd) { return host ? host->window()->GetProperty(kContentWindowForRootWindow) : NULL; } +void DesktopWindowTreeHostWin::SetInTouchDrag(bool in_touch_drag) { + in_touch_drag_ = in_touch_drag; +} + //////////////////////////////////////////////////////////////////////////////// // DesktopWindowTreeHostWin, DesktopWindowTreeHost implementation: @@ -185,7 +191,7 @@ std::unique_ptr<corewm::Tooltip> DesktopWindowTreeHostWin::CreateTooltip() { std::unique_ptr<aura::client::DragDropClient> DesktopWindowTreeHostWin::CreateDragDropClient( DesktopNativeCursorManager* cursor_manager) { - drag_drop_client_ = new DesktopDragDropClientWin(window(), GetHWND()); + drag_drop_client_ = new DesktopDragDropClientWin(window(), GetHWND(), this); return base::WrapUnique(drag_drop_client_); } @@ -920,6 +926,9 @@ void DesktopWindowTreeHostWin::HandleNativeBlur(HWND focused_window) { } bool DesktopWindowTreeHostWin::HandleMouseEvent(ui::MouseEvent* event) { + // Ignore native platform events for test purposes + if (ui::PlatformEventSource::ShouldIgnoreNativePlatformEvents()) + return true; // Mouse events in occluded windows should be very rare. If this stat isn't // very close to 0, that would indicate that windows are incorrectly getting // marked occluded, or getting stuck in the occluded state. Event can cause @@ -967,6 +976,21 @@ void DesktopWindowTreeHostWin::HandleTouchEvent(ui::TouchEvent* event) { if (!GetWidget()->GetNativeView()) return; + if (in_touch_drag_) { + POINT event_point; + event_point.x = event->location().x(); + event_point.y = event->location().y(); + ::ClientToScreen(GetHWND(), &event_point); + gfx::Point screen_point(event_point); + // Send equivalent mouse events, because Ole32 drag drop doesn't seem to + // handle pointer events. + if (event->type() == ui::ET_TOUCH_MOVED) { + ui::SendMouseEvent(screen_point, MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE); + } else if (event->type() == ui::ET_TOUCH_RELEASED) { + ui::SendMouseEvent(screen_point, + MOUSEEVENTF_RIGHTUP | MOUSEEVENTF_ABSOLUTE); + } + } // Currently we assume the window that has capture gets touch events too. aura::WindowTreeHost* host = aura::WindowTreeHost::GetForAcceleratedWidget(GetCapture()); diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h index 75ca9e99f2c..c0f00b6d86d 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h @@ -59,6 +59,12 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin // A way of converting an HWND into a content window. static aura::Window* GetContentWindowForHWND(HWND hwnd); + // Set to true when DesktopDragDropClientWin starts a touch-initiated drag + // drop and false when it finishes. While in touch drag, if pointer events are + // received, the equivalent mouse events are generated, because ole32 + // ::DoDragDrop does not seem to handle pointer events. + void SetInTouchDrag(bool in_touch_drag); + protected: // Overridden from DesktopWindowTreeHost: void Init(const Widget::InitParams& params) override; @@ -312,6 +318,8 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin // when that stat is no longer tracked. gfx::Point occluded_window_mouse_event_loc_; + bool in_touch_drag_ = false; + // The z-order level of the window; the window exhibits "always on top" // behavior if > 0. ui::ZOrderLevel z_order_ = ui::ZOrderLevel::kNormal; diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc index 44e9eef3492..af81a254b0c 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc @@ -52,6 +52,7 @@ #include "ui/gfx/x/x11.h" #include "ui/gfx/x/x11_atom_cache.h" #include "ui/gfx/x/x11_path.h" +#include "ui/gfx/x/xproto.h" #include "ui/views/linux_ui/linux_ui.h" #include "ui/views/views_switches.h" #include "ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h" @@ -82,9 +83,9 @@ void DesktopWindowTreeHostX11::Init(const Widget::InitParams& params) { // Set XEventDelegate to receive selection, drag&drop and raw key events. // // TODO(https://crbug.com/990756): There are two cases of this delegate: - // XEvents for DragAndDrop client and raw key events. DragAndDrop could be - // unified so that DragAndrDropClientOzone is used and XEvent are handled on - // platform level. + // x11::Events for DragAndDrop client and raw key events. DragAndDrop could be + // unified so that DragAndrDropClientOzone is used and x11::Event are handled + // on platform level. static_cast<ui::X11Window*>(platform_window())->SetXEventDelegate(this); } @@ -92,7 +93,6 @@ std::unique_ptr<aura::client::DragDropClient> DesktopWindowTreeHostX11::CreateDragDropClient( DesktopNativeCursorManager* cursor_manager) { drag_drop_client_ = new DesktopDragDropClientAuraX11(window(), cursor_manager, - GetXWindow()->display(), GetXWindow()->window()); drag_drop_client_->Init(); return base::WrapUnique(drag_drop_client_); @@ -101,16 +101,16 @@ DesktopWindowTreeHostX11::CreateDragDropClient( //////////////////////////////////////////////////////////////////////////////// // DesktopWindowTreeHostX11 implementation: -void DesktopWindowTreeHostX11::OnXWindowSelectionEvent(XEvent* xev) { +void DesktopWindowTreeHostX11::OnXWindowSelectionEvent(x11::Event* xev) { DCHECK(xev); DCHECK(drag_drop_client_); - drag_drop_client_->OnSelectionNotify(xev->xselection); + drag_drop_client_->OnSelectionNotify(*xev->As<x11::SelectionNotifyEvent>()); } -void DesktopWindowTreeHostX11::OnXWindowDragDropEvent(XEvent* xev) { +void DesktopWindowTreeHostX11::OnXWindowDragDropEvent(x11::Event* xev) { DCHECK(xev); DCHECK(drag_drop_client_); - drag_drop_client_->HandleXdndEvent(xev->xclient); + drag_drop_client_->HandleXdndEvent(*xev->As<x11::ClientMessageEvent>()); } const ui::XWindow* DesktopWindowTreeHostX11::GetXWindow() const { diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h index 8cd6b80ee1e..69501945464 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h @@ -8,6 +8,7 @@ #include <memory> #include "base/macros.h" +#include "ui/gfx/x/event.h" #include "ui/gfx/x/x11_types.h" #include "ui/platform_window/platform_window_delegate.h" #include "ui/platform_window/x11/x11_window.h" @@ -40,8 +41,8 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11 : public DesktopWindowTreeHostLinux, friend class DesktopWindowTreeHostX11HighDPITest; // Overridden from ui::XEventDelegate. - void OnXWindowSelectionEvent(XEvent* xev) override; - void OnXWindowDragDropEvent(XEvent* xev) override; + void OnXWindowSelectionEvent(x11::Event* xev) override; + void OnXWindowDragDropEvent(x11::Event* xev) override; // Casts PlatformWindow into XWindow and returns the result. This is a temp // solution to access XWindow, which is subclassed by the X11Window, which is diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_interactive_uitest.cc b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_interactive_uitest.cc index c63bfe26350..b7c96511462 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_interactive_uitest.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_interactive_uitest.cc @@ -4,6 +4,8 @@ #include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h" +#include <xcb/xproto.h> + #include <memory> #include "base/macros.h" @@ -17,6 +19,7 @@ #include "ui/events/platform/x11/x11_event_source.h" #include "ui/events/x/x11_event_translation.h" #include "ui/gfx/geometry/rect.h" +#include "ui/gfx/x/event.h" #include "ui/gfx/x/x11.h" #include "ui/gfx/x/x11_atom_cache.h" #include "ui/views/controls/textfield/textfield.h" @@ -30,7 +33,7 @@ namespace { // Blocks till |window| gets activated. class ActivationWaiter : public ui::X11PropertyChangeWaiter { public: - explicit ActivationWaiter(XID window) + explicit ActivationWaiter(x11::Window window) : ui::X11PropertyChangeWaiter(ui::GetX11RootWindow(), "_NET_ACTIVE_WINDOW"), window_(window) {} @@ -39,13 +42,14 @@ class ActivationWaiter : public ui::X11PropertyChangeWaiter { private: // ui::X11PropertyChangeWaiter: - bool ShouldKeepOnWaiting(XEvent* event) override { - XID xid = 0; - ui::GetXIDProperty(ui::GetX11RootWindow(), "_NET_ACTIVE_WINDOW", &xid); - return xid != window_; + bool ShouldKeepOnWaiting(x11::Event* event) override { + x11::Window window = x11::Window::None; + ui::GetProperty(ui::GetX11RootWindow(), gfx::GetAtom("_NET_ACTIVE_WINDOW"), + &window); + return window != window_; } - XID window_; + x11::Window window_; DISALLOW_COPY_AND_ASSIGN(ActivationWaiter); }; @@ -88,23 +92,25 @@ void DispatchMouseMotionEvent(DesktopWindowTreeHostX11* desktop_host, const gfx::Point& point_in_screen) { gfx::Rect bounds_in_screen = desktop_host->window()->GetBoundsInScreen(); - Display* display = gfx::GetXDisplay(); - XEvent xev; - xev.xmotion.type = MotionNotify; - xev.xmotion.display = display; - xev.xmotion.window = desktop_host->GetAcceleratedWidget(); - xev.xmotion.root = DefaultRootWindow(display); - xev.xmotion.subwindow = 0; - xev.xmotion.time = x11::CurrentTime; - xev.xmotion.x = point_in_screen.x() - bounds_in_screen.x(); - xev.xmotion.y = point_in_screen.y() - bounds_in_screen.y(); - xev.xmotion.x_root = point_in_screen.x(); - xev.xmotion.y_root = point_in_screen.y(); - xev.xmotion.state = 0; - xev.xmotion.is_hint = NotifyNormal; - xev.xmotion.same_screen = x11::True; - - ui::X11EventSource::GetInstance()->ProcessXEvent(&xev); + auto* connection = x11::Connection::Get(); + xcb_generic_event_t ge; + memset(&ge, 0, sizeof(ge)); + auto* xev = reinterpret_cast<xcb_motion_notify_event_t*>(&ge); + xev->response_type = MotionNotify; + xev->event = static_cast<uint32_t>(desktop_host->GetAcceleratedWidget()); + xev->root = static_cast<uint32_t>(connection->default_screen().root); + xev->child = 0; + xev->time = x11::CurrentTime; + xev->event_x = point_in_screen.x() - bounds_in_screen.x(); + xev->event_y = point_in_screen.y() - bounds_in_screen.y(); + xev->root_x = point_in_screen.x(); + xev->root_y = point_in_screen.y(); + xev->state = 0; + xev->detail = NotifyNormal; + xev->same_screen = x11::True; + + x11::Event x11_event(&ge, connection); + ui::X11EventSource::GetInstance()->ProcessXEvent(&x11_event); } } // namespace diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc deleted file mode 100644 index 1f4b3a5244c..00000000000 --- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc +++ /dev/null @@ -1,675 +0,0 @@ -// Copyright 2014 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 <stddef.h> - -#include <memory> -#include <utility> -#include <vector> - -#include "base/command_line.h" -#include "base/macros.h" -#include "base/run_loop.h" -#include "base/stl_util.h" -#include "third_party/skia/include/core/SkPath.h" -#include "ui/aura/window.h" -#include "ui/aura/window_tree_host.h" -#include "ui/base/hit_test.h" -#include "ui/base/x/test/x11_property_change_waiter.h" -#include "ui/base/x/x11_util.h" -#include "ui/display/display_switches.h" -#include "ui/events/devices/x11/touch_factory_x11.h" -#include "ui/events/platform/x11/x11_event_source.h" -#include "ui/events/test/events_test_utils_x11.h" -#include "ui/events/test/platform_event_source_test_api.h" -#include "ui/events/x/x11_event_translation.h" -#include "ui/gfx/geometry/point.h" -#include "ui/gfx/geometry/rect.h" -#include "ui/gfx/x/x11.h" -#include "ui/gfx/x/x11_atom_cache.h" -#include "ui/views/test/views_test_base.h" -#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" -#include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h" -#include "ui/views/widget/widget_delegate.h" -#include "ui/views/window/non_client_view.h" - -namespace views { - -namespace { - -const int kPointerDeviceId = 1; - -// Blocks till the window state hint, |hint|, is set or unset. -class WMStateWaiter : public ui::X11PropertyChangeWaiter { - public: - WMStateWaiter(XID window, const char* hint, bool wait_till_set) - : ui::X11PropertyChangeWaiter(window, "_NET_WM_STATE"), - hint_(hint), - wait_till_set_(wait_till_set) {} - - ~WMStateWaiter() override = default; - - private: - // X11PropertyChangeWaiter: - bool ShouldKeepOnWaiting(XEvent* event) override { - std::vector<Atom> hints; - if (ui::GetAtomArrayProperty(xwindow(), "_NET_WM_STATE", &hints)) - return base::Contains(hints, gfx::GetAtom(hint_)) != wait_till_set_; - return true; - } - - // The name of the hint to wait to get set or unset. - const char* hint_; - - // Whether we are waiting for |hint| to be set or unset. - bool wait_till_set_; - - DISALLOW_COPY_AND_ASSIGN(WMStateWaiter); -}; - -// A NonClientFrameView with a window mask with the bottom right corner cut out. -class ShapedNonClientFrameView : public NonClientFrameView { - public: - ShapedNonClientFrameView() = default; - - ~ShapedNonClientFrameView() override = default; - - // NonClientFrameView: - gfx::Rect GetBoundsForClientView() const override { return bounds(); } - gfx::Rect GetWindowBoundsForClientBounds( - const gfx::Rect& client_bounds) const override { - return client_bounds; - } - int NonClientHitTest(const gfx::Point& point) override { - // Fake bottom for non client event test. - if (point == gfx::Point(500, 500)) - return HTBOTTOM; - return HTNOWHERE; - } - void GetWindowMask(const gfx::Size& size, SkPath* window_mask) override { - int right = size.width(); - int bottom = size.height(); - - window_mask->moveTo(0, 0); - window_mask->lineTo(0, bottom); - window_mask->lineTo(right, bottom); - window_mask->lineTo(right, 10); - window_mask->lineTo(right - 10, 10); - window_mask->lineTo(right - 10, 0); - window_mask->close(); - } - void ResetWindowControls() override {} - void UpdateWindowIcon() override {} - void UpdateWindowTitle() override {} - void SizeConstraintsChanged() override {} - - bool GetAndResetLayoutRequest() { - bool layout_requested = layout_requested_; - layout_requested_ = false; - return layout_requested; - } - - private: - void Layout() override { layout_requested_ = true; } - - bool layout_requested_ = false; - - DISALLOW_COPY_AND_ASSIGN(ShapedNonClientFrameView); -}; - -class ShapedWidgetDelegate : public WidgetDelegateView { - public: - ShapedWidgetDelegate() = default; - - ~ShapedWidgetDelegate() override = default; - - // WidgetDelegateView: - NonClientFrameView* CreateNonClientFrameView(Widget* widget) override { - return new ShapedNonClientFrameView; - } - - private: - DISALLOW_COPY_AND_ASSIGN(ShapedWidgetDelegate); -}; - -// Creates a widget of size 100x100. -std::unique_ptr<Widget> CreateWidget(WidgetDelegate* delegate) { - std::unique_ptr<Widget> widget(new Widget); - Widget::InitParams params(Widget::InitParams::TYPE_WINDOW); - params.delegate = delegate; - params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - params.remove_standard_frame = true; - params.bounds = gfx::Rect(100, 100, 100, 100); - widget->Init(std::move(params)); - return widget; -} - -// Returns the list of rectangles which describe |xid|'s bounding region via the -// X shape extension. -std::vector<gfx::Rect> GetShapeRects(XID xid) { - int dummy; - int shape_rects_size; - gfx::XScopedPtr<XRectangle[]> shape_rects(XShapeGetRectangles( - gfx::GetXDisplay(), xid, ShapeBounding, &shape_rects_size, &dummy)); - - std::vector<gfx::Rect> shape_vector; - for (int i = 0; i < shape_rects_size; ++i) { - const XRectangle& rect = shape_rects[i]; - shape_vector.emplace_back(rect.x, rect.y, rect.width, rect.height); - } - return shape_vector; -} - -// Returns true if one of |rects| contains point (x,y). -bool ShapeRectContainsPoint(const std::vector<gfx::Rect>& shape_rects, - int x, - int y) { - gfx::Point point(x, y); - return std::any_of( - shape_rects.cbegin(), shape_rects.cend(), - [&point](const auto& rect) { return rect.Contains(point); }); -} - -} // namespace - -class DesktopWindowTreeHostX11Test : public ViewsTestBase { - public: - DesktopWindowTreeHostX11Test() - : event_source_(ui::X11EventSource::GetInstance()) {} - ~DesktopWindowTreeHostX11Test() override = default; - - void SetUp() override { - set_native_widget_type(NativeWidgetType::kDesktop); - - std::vector<int> pointer_devices; - pointer_devices.push_back(kPointerDeviceId); - ui::TouchFactory::GetInstance()->SetPointerDeviceForTest(pointer_devices); - - ViewsTestBase::SetUp(); - - // Make X11 synchronous for our display connection. This does not force the - // window manager to behave synchronously. - XSynchronize(gfx::GetXDisplay(), x11::True); - } - - void TearDown() override { - XSynchronize(gfx::GetXDisplay(), x11::False); - ViewsTestBase::TearDown(); - } - - void DispatchSingleEventToWidget(XEvent* xev, Widget* widget) { - DCHECK_EQ(GenericEvent, xev->type); - XIDeviceEvent* device_event = - static_cast<XIDeviceEvent*>(xev->xcookie.data); - device_event->event = - widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget(); - event_source_->ProcessXEvent(xev); - } - - private: - ui::X11EventSource* event_source_; - DISALLOW_COPY_AND_ASSIGN(DesktopWindowTreeHostX11Test); -}; - -// https://crbug.com/898742: Test is flaky. -// Tests that the shape is properly set on the x window. -TEST_F(DesktopWindowTreeHostX11Test, DISABLED_Shape) { - if (!ui::IsShapeExtensionAvailable()) - return; - - // 1) Test setting the window shape via the NonClientFrameView. This technique - // is used to get rounded corners on Chrome windows when not using the native - // window frame. - std::unique_ptr<Widget> widget1 = CreateWidget(new ShapedWidgetDelegate()); - widget1->Show(); - ui::X11EventSource::GetInstance()->DispatchXEvents(); - - XID xid1 = widget1->GetNativeWindow()->GetHost()->GetAcceleratedWidget(); - std::vector<gfx::Rect> shape_rects = GetShapeRects(xid1); - ASSERT_FALSE(shape_rects.empty()); - - // The widget was supposed to be 100x100, but the WM might have ignored this - // suggestion. - int widget_width = widget1->GetWindowBoundsInScreen().width(); - EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, widget_width - 15, 5)); - EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, widget_width - 5, 5)); - EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, widget_width - 5, 15)); - EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, widget_width + 5, 15)); - - // Changing widget's size should update the shape. - widget1->SetBounds(gfx::Rect(100, 100, 200, 200)); - ui::X11EventSource::GetInstance()->DispatchXEvents(); - - if (widget1->GetWindowBoundsInScreen().width() == 200) { - shape_rects = GetShapeRects(xid1); - ASSERT_FALSE(shape_rects.empty()); - EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 85, 5)); - EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 95, 5)); - EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 185, 5)); - EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 195, 5)); - EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 195, 15)); - EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 205, 15)); - } - - if (ui::WmSupportsHint(gfx::GetAtom("_NET_WM_STATE_MAXIMIZED_VERT"))) { - // The shape should be changed to a rectangle which fills the entire screen - // when |widget1| is maximized. - { - WMStateWaiter waiter(xid1, "_NET_WM_STATE_MAXIMIZED_VERT", true); - widget1->Maximize(); - waiter.Wait(); - } - - // Ensure that the task which is posted when a window is resized is run. - base::RunLoop().RunUntilIdle(); - - // xvfb does not support Xrandr so we cannot check the maximized window's - // bounds. - gfx::Rect maximized_bounds; - ui::GetOuterWindowBounds(xid1, &maximized_bounds); - - shape_rects = GetShapeRects(xid1); - ASSERT_FALSE(shape_rects.empty()); - EXPECT_TRUE( - ShapeRectContainsPoint(shape_rects, maximized_bounds.width() - 1, 5)); - EXPECT_TRUE( - ShapeRectContainsPoint(shape_rects, maximized_bounds.width() - 1, 15)); - } - - // 2) Test setting the window shape via Widget::SetShape(). - auto shape_region = std::make_unique<Widget::ShapeRects>(); - shape_region->emplace_back(10, 0, 90, 10); - shape_region->emplace_back(0, 10, 10, 90); - shape_region->emplace_back(10, 10, 90, 90); - - std::unique_ptr<Widget> widget2(CreateWidget(nullptr)); - widget2->Show(); - widget2->SetShape(std::move(shape_region)); - ui::X11EventSource::GetInstance()->DispatchXEvents(); - - XID xid2 = widget2->GetNativeWindow()->GetHost()->GetAcceleratedWidget(); - shape_rects = GetShapeRects(xid2); - ASSERT_FALSE(shape_rects.empty()); - EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 5, 5)); - EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 15, 5)); - EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 95, 15)); - EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 105, 15)); - - // Changing the widget's size should not affect the shape. - widget2->SetBounds(gfx::Rect(100, 100, 200, 200)); - shape_rects = GetShapeRects(xid2); - ASSERT_FALSE(shape_rects.empty()); - EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 5, 5)); - EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 15, 5)); - EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 95, 15)); - EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 105, 15)); - - // Setting the shape to NULL resets the shape back to the entire - // window bounds. - widget2->SetShape(nullptr); - shape_rects = GetShapeRects(xid2); - ASSERT_FALSE(shape_rects.empty()); - EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 5, 5)); - EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 15, 5)); - EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 95, 15)); - EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 105, 15)); - EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 500, 500)); -} - -// Test that the widget reacts on changes in fullscreen state initiated by the -// window manager (e.g. via a window manager accelerator key). -TEST_F(DesktopWindowTreeHostX11Test, WindowManagerTogglesFullscreen) { - if (!ui::WmSupportsHint(gfx::GetAtom("_NET_WM_STATE_FULLSCREEN"))) - return; - - Display* display = gfx::GetXDisplay(); - - std::unique_ptr<Widget> widget = CreateWidget(new ShapedWidgetDelegate()); - auto* non_client_view = static_cast<ShapedNonClientFrameView*>( - widget->non_client_view()->frame_view()); - ASSERT_TRUE(non_client_view); - XID xid = widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget(); - widget->Show(); - ui::X11EventSource::GetInstance()->DispatchXEvents(); - - gfx::Rect initial_bounds = widget->GetWindowBoundsInScreen(); - { - WMStateWaiter waiter(xid, "_NET_WM_STATE_FULLSCREEN", true); - widget->SetFullscreen(true); - waiter.Wait(); - } - EXPECT_TRUE(widget->IsFullscreen()); - - // After the fullscreen state has been set, there must be a relayout request - EXPECT_TRUE(non_client_view->GetAndResetLayoutRequest()); - - // Ensure there is not request before we proceed. - EXPECT_FALSE(non_client_view->GetAndResetLayoutRequest()); - - // Emulate the window manager exiting fullscreen via a window manager - // accelerator key. - { - XEvent xclient; - memset(&xclient, 0, sizeof(xclient)); - xclient.type = ClientMessage; - xclient.xclient.window = xid; - xclient.xclient.message_type = gfx::GetAtom("_NET_WM_STATE"); - xclient.xclient.format = 32; - xclient.xclient.data.l[0] = 0; - xclient.xclient.data.l[1] = gfx::GetAtom("_NET_WM_STATE_FULLSCREEN"); - xclient.xclient.data.l[2] = 0; - xclient.xclient.data.l[3] = 1; - xclient.xclient.data.l[4] = 0; - XSendEvent(display, DefaultRootWindow(display), x11::False, - SubstructureRedirectMask | SubstructureNotifyMask, &xclient); - - WMStateWaiter waiter(xid, "_NET_WM_STATE_FULLSCREEN", false); - waiter.Wait(); - } - // Ensure it continues in browser fullscreen mode and bounds are restored to - // |initial_bounds|. - EXPECT_TRUE(widget->IsFullscreen()); - EXPECT_EQ(initial_bounds.ToString(), - widget->GetWindowBoundsInScreen().ToString()); - - // Emulate window resize (through X11 configure events) while in browser - // fullscreen mode and ensure bounds are tracked correctly. - initial_bounds.set_size({400, 400}); - { - XWindowChanges changes = {0}; - changes.width = initial_bounds.width(); - changes.height = initial_bounds.height(); - XConfigureWindow(display, xid, CWHeight | CWWidth, &changes); - // Ensure that the task which is posted when a window is resized is run. - base::RunLoop().RunUntilIdle(); - } - EXPECT_TRUE(widget->IsFullscreen()); - EXPECT_EQ(initial_bounds.ToString(), - widget->GetWindowBoundsInScreen().ToString()); - - // Calling Widget::SetFullscreen(false) should clear the widget's fullscreen - // state and clean things up. - widget->SetFullscreen(false); - EXPECT_FALSE(widget->IsFullscreen()); - EXPECT_EQ(initial_bounds.ToString(), - widget->GetWindowBoundsInScreen().ToString()); - - // Even though the unfullscreen request came from the window manager, we must - // still react and relayout. - EXPECT_TRUE(non_client_view->GetAndResetLayoutRequest()); -} - -// Tests that the minimization information is propagated to the content window. -TEST_F(DesktopWindowTreeHostX11Test, ToggleMinimizePropogateToContentWindow) { - Widget widget; - Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); - params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - widget.Init(std::move(params)); - widget.Show(); - ui::X11EventSource::GetInstance()->DispatchXEvents(); - - XID xid = widget.GetNativeWindow()->GetHost()->GetAcceleratedWidget(); - Display* display = gfx::GetXDisplay(); - - // Minimize by sending _NET_WM_STATE_HIDDEN - { - std::vector<::Atom> atom_list; - atom_list.push_back(gfx::GetAtom("_NET_WM_STATE_HIDDEN")); - ui::SetAtomArrayProperty(xid, "_NET_WM_STATE", "ATOM", atom_list); - - XEvent xevent; - memset(&xevent, 0, sizeof(xevent)); - xevent.type = PropertyNotify; - xevent.xproperty.type = PropertyNotify; - xevent.xproperty.send_event = 1; - xevent.xproperty.display = display; - xevent.xproperty.window = xid; - xevent.xproperty.atom = gfx::GetAtom("_NET_WM_STATE"); - xevent.xproperty.state = 0; - XSendEvent(display, DefaultRootWindow(display), x11::False, - SubstructureRedirectMask | SubstructureNotifyMask, &xevent); - - WMStateWaiter waiter(xid, "_NET_WM_STATE_HIDDEN", true); - waiter.Wait(); - } - EXPECT_FALSE(widget.GetNativeWindow()->IsVisible()); - - // Show from minimized by sending _NET_WM_STATE_FOCUSED - { - std::vector<::Atom> atom_list; - atom_list.push_back(gfx::GetAtom("_NET_WM_STATE_FOCUSED")); - ui::SetAtomArrayProperty(xid, "_NET_WM_STATE", "ATOM", atom_list); - - XEvent xevent; - memset(&xevent, 0, sizeof(xevent)); - xevent.type = PropertyNotify; - xevent.xproperty.type = PropertyNotify; - xevent.xproperty.send_event = 1; - xevent.xproperty.display = display; - xevent.xproperty.window = xid; - xevent.xproperty.atom = gfx::GetAtom("_NET_WM_STATE"); - xevent.xproperty.state = 0; - XSendEvent(display, DefaultRootWindow(display), x11::False, - SubstructureRedirectMask | SubstructureNotifyMask, &xevent); - - WMStateWaiter waiter(xid, "_NET_WM_STATE_FOCUSED", true); - waiter.Wait(); - } - EXPECT_TRUE(widget.GetNativeWindow()->IsVisible()); -} - -TEST_F(DesktopWindowTreeHostX11Test, ChildWindowDestructionDuringTearDown) { - Widget parent_widget; - Widget::InitParams parent_params = - CreateParams(Widget::InitParams::TYPE_WINDOW); - parent_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - parent_widget.Init(std::move(parent_params)); - parent_widget.Show(); - ui::X11EventSource::GetInstance()->DispatchXEvents(); - - Widget child_widget; - Widget::InitParams child_params = - CreateParams(Widget::InitParams::TYPE_WINDOW); - child_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - child_params.parent = parent_widget.GetNativeWindow(); - child_widget.Init(std::move(child_params)); - child_widget.Show(); - ui::X11EventSource::GetInstance()->DispatchXEvents(); - - // Sanity check that the two widgets each have their own XID. - ASSERT_NE(parent_widget.GetNativeWindow()->GetHost()->GetAcceleratedWidget(), - child_widget.GetNativeWindow()->GetHost()->GetAcceleratedWidget()); - Widget::CloseAllSecondaryWidgets(); - EXPECT_TRUE(DesktopWindowTreeHostLinux::GetAllOpenWindows().empty()); -} - -// A Widget that allows setting the min/max size for the widget. -class CustomSizeWidget : public Widget { - public: - CustomSizeWidget() = default; - ~CustomSizeWidget() override = default; - - void set_min_size(const gfx::Size& size) { min_size_ = size; } - void set_max_size(const gfx::Size& size) { max_size_ = size; } - - // Widget: - gfx::Size GetMinimumSize() const override { return min_size_; } - gfx::Size GetMaximumSize() const override { return max_size_; } - - private: - gfx::Size min_size_; - gfx::Size max_size_; - - DISALLOW_COPY_AND_ASSIGN(CustomSizeWidget); -}; - -TEST_F(DesktopWindowTreeHostX11Test, SetBoundsWithMinMax) { - CustomSizeWidget widget; - Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); - params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - params.bounds = gfx::Rect(200, 100); - widget.Init(std::move(params)); - widget.Show(); - ui::X11EventSource::GetInstance()->DispatchXEvents(); - - EXPECT_EQ(gfx::Size(200, 100).ToString(), - widget.GetWindowBoundsInScreen().size().ToString()); - widget.SetBounds(gfx::Rect(300, 200)); - EXPECT_EQ(gfx::Size(300, 200).ToString(), - widget.GetWindowBoundsInScreen().size().ToString()); - - widget.set_min_size(gfx::Size(100, 100)); - widget.SetBounds(gfx::Rect(50, 500)); - EXPECT_EQ(gfx::Size(100, 500).ToString(), - widget.GetWindowBoundsInScreen().size().ToString()); -} - -class MouseEventRecorder : public ui::EventHandler { - public: - MouseEventRecorder() = default; - ~MouseEventRecorder() override = default; - - void Reset() { mouse_events_.clear(); } - - const std::vector<ui::MouseEvent>& mouse_events() const { - return mouse_events_; - } - - private: - // ui::EventHandler: - void OnMouseEvent(ui::MouseEvent* mouse) override { - mouse_events_.push_back(*mouse); - } - - std::vector<ui::MouseEvent> mouse_events_; - - DISALLOW_COPY_AND_ASSIGN(MouseEventRecorder); -}; - -class DesktopWindowTreeHostX11HighDPITest - : public DesktopWindowTreeHostX11Test { - public: - DesktopWindowTreeHostX11HighDPITest() = default; - ~DesktopWindowTreeHostX11HighDPITest() override = default; - - void PretendCapture(views::Widget* capture_widget) { - if (capture_widget) - capture_widget->GetNativeWindow()->SetCapture(); - } - - private: - void SetUp() override { - base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - command_line->AppendSwitchASCII(switches::kForceDeviceScaleFactor, "2"); - - DesktopWindowTreeHostX11Test::SetUp(); - } - - DISALLOW_COPY_AND_ASSIGN(DesktopWindowTreeHostX11HighDPITest); -}; - -// https://crbug.com/702687 -TEST_F(DesktopWindowTreeHostX11HighDPITest, - DISABLED_LocatedEventDispatchWithCapture) { - Widget first; - Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); - params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - params.bounds = gfx::Rect(0, 0, 50, 50); - first.Init(std::move(params)); - first.Show(); - - Widget second; - params = CreateParams(Widget::InitParams::TYPE_WINDOW); - params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - params.bounds = gfx::Rect(50, 50, 50, 50); - second.Init(std::move(params)); - second.Show(); - - ui::X11EventSource::GetInstance()->DispatchXEvents(); - - MouseEventRecorder first_recorder, second_recorder; - first.GetNativeWindow()->AddPreTargetHandler(&first_recorder); - second.GetNativeWindow()->AddPreTargetHandler(&second_recorder); - - // Dispatch an event on |first|. Verify it gets the event. - ui::ScopedXI2Event event; - event.InitGenericButtonEvent(kPointerDeviceId, ui::ET_MOUSEWHEEL, - gfx::Point(50, 50), ui::EF_NONE); - DispatchSingleEventToWidget(event, &first); - ASSERT_EQ(1u, first_recorder.mouse_events().size()); - EXPECT_EQ(ui::ET_MOUSEWHEEL, first_recorder.mouse_events()[0].type()); - EXPECT_EQ(gfx::Point(25, 25).ToString(), - first_recorder.mouse_events()[0].location().ToString()); - ASSERT_EQ(0u, second_recorder.mouse_events().size()); - - first_recorder.Reset(); - second_recorder.Reset(); - - // Set a capture on |second|, and dispatch the same event to |first|. This - // event should reach |second| instead. - PretendCapture(&second); - event.InitGenericButtonEvent(kPointerDeviceId, ui::ET_MOUSEWHEEL, - gfx::Point(50, 50), ui::EF_NONE); - DispatchSingleEventToWidget(event, &first); - - ASSERT_EQ(0u, first_recorder.mouse_events().size()); - ASSERT_EQ(1u, second_recorder.mouse_events().size()); - EXPECT_EQ(ui::ET_MOUSEWHEEL, second_recorder.mouse_events()[0].type()); - EXPECT_EQ(gfx::Point(-25, -25).ToString(), - second_recorder.mouse_events()[0].location().ToString()); - - PretendCapture(nullptr); - first.GetNativeWindow()->RemovePreTargetHandler(&first_recorder); - second.GetNativeWindow()->RemovePreTargetHandler(&second_recorder); -} - -TEST_F(DesktopWindowTreeHostX11Test, MouseNCEvents) { - std::unique_ptr<Widget> widget = CreateWidget(new ShapedWidgetDelegate()); - widget->Show(); - - ui::X11EventSource::GetInstance()->DispatchXEvents(); - - widget->SetBounds(gfx::Rect(100, 100, 501, 501)); - ui::X11EventSource::GetInstance()->DispatchXEvents(); - - MouseEventRecorder recorder; - widget->GetNativeWindow()->AddPreTargetHandler(&recorder); - - ui::ScopedXI2Event event; - event.InitGenericButtonEvent(kPointerDeviceId, ui::ET_MOUSE_PRESSED, - gfx::Point(500, 500), ui::EF_LEFT_MOUSE_BUTTON); - - DispatchSingleEventToWidget(event, widget.get()); - ASSERT_EQ(1u, recorder.mouse_events().size()); - EXPECT_EQ(ui::ET_MOUSE_PRESSED, recorder.mouse_events()[0].type()); - EXPECT_TRUE(recorder.mouse_events()[0].flags() & ui::EF_IS_NON_CLIENT); - - widget->GetNativeWindow()->RemovePreTargetHandler(&recorder); -} - -TEST_F(DesktopWindowTreeHostX11HighDPITest, MouseNCEvents) { - std::unique_ptr<Widget> widget = CreateWidget(new ShapedWidgetDelegate()); - widget->Show(); - - ui::X11EventSource::GetInstance()->DispatchXEvents(); - - widget->SetBounds(gfx::Rect(100, 100, 1000, 1000)); - ui::X11EventSource::GetInstance()->DispatchXEvents(); - - MouseEventRecorder recorder; - widget->GetNativeWindow()->AddPreTargetHandler(&recorder); - - ui::ScopedXI2Event event; - event.InitGenericButtonEvent(kPointerDeviceId, ui::ET_MOUSE_PRESSED, - gfx::Point(1001, 1001), - ui::EF_LEFT_MOUSE_BUTTON); - DispatchSingleEventToWidget(event, widget.get()); - ASSERT_EQ(1u, recorder.mouse_events().size()); - EXPECT_EQ(ui::ET_MOUSE_PRESSED, recorder.mouse_events()[0].type()); - EXPECT_TRUE(recorder.mouse_events()[0].flags() & ui::EF_IS_NON_CLIENT); - - widget->GetNativeWindow()->RemovePreTargetHandler(&recorder); -} - -} // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/x11_drag_drop_client_unittest.cc b/chromium/ui/views/widget/desktop_aura/x11_drag_drop_client_unittest.cc new file mode 100644 index 00000000000..c9946d29596 --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/x11_drag_drop_client_unittest.cc @@ -0,0 +1,827 @@ +// Copyright 2014 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 "ui/base/x/x11_drag_drop_client.h" + +#include <map> +#include <memory> +#include <utility> +#include <vector> + +#include "base/bind.h" +#include "base/location.h" +#include "base/macros.h" +#include "base/memory/ptr_util.h" +#include "base/run_loop.h" +#include "base/single_thread_task_runner.h" +#include "base/strings/utf_string_conversions.h" +#include "base/threading/thread_task_runner_handle.h" +#include "ui/aura/client/drag_drop_client.h" +#include "ui/aura/client/drag_drop_delegate.h" +#include "ui/aura/test/test_screen.h" +#include "ui/aura/window.h" +#include "ui/aura/window_tree_host.h" +#include "ui/base/dragdrop/os_exchange_data.h" +#include "ui/base/x/x11_cursor.h" +#include "ui/base/x/x11_move_loop.h" +#include "ui/base/x/x11_move_loop_delegate.h" +#include "ui/base/x/x11_os_exchange_data_provider.h" +#include "ui/base/x/x11_util.h" +#include "ui/events/event_utils.h" +#include "ui/gfx/x/x11.h" +#include "ui/gfx/x/x11_atom_cache.h" +#include "ui/gfx/x/x11_types.h" +#include "ui/gfx/x/xproto.h" +#include "ui/views/test/views_test_base.h" +#include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h" +#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" +#include "ui/views/widget/widget.h" + +namespace views { + +namespace { + +class TestDragDropClient; + +// Collects messages which would otherwise be sent to |window_| via +// SendXClientEvent(). +class ClientMessageEventCollector { + public: + ClientMessageEventCollector(x11::Window window, TestDragDropClient* client); + virtual ~ClientMessageEventCollector(); + + // Returns true if |events_| is non-empty. + bool HasEvents() const { return !events_.empty(); } + + // Pops all of |events_| and returns the popped events in the order that they + // were on the stack + std::vector<x11::ClientMessageEvent> PopAllEvents(); + + // Adds |event| to the stack. + void RecordEvent(const x11::ClientMessageEvent& event); + + private: + x11::Window window_; + + // Not owned. + TestDragDropClient* client_; + + std::vector<x11::ClientMessageEvent> events_; + + DISALLOW_COPY_AND_ASSIGN(ClientMessageEventCollector); +}; + +// An implementation of ui::X11MoveLoop where RunMoveLoop() always starts the +// move loop. +class TestMoveLoop : public ui::X11MoveLoop { + public: + explicit TestMoveLoop(ui::X11MoveLoopDelegate* delegate); + ~TestMoveLoop() override; + + // Returns true if the move loop is running. + bool IsRunning() const; + + // ui::X11MoveLoop: + bool RunMoveLoop(bool can_grab_pointer, + ::Cursor old_cursor, + ::Cursor new_cursor) override; + void UpdateCursor(::Cursor cursor) override; + void EndMoveLoop() override; + + private: + // Not owned. + ui::X11MoveLoopDelegate* delegate_; + + // Ends the move loop. + base::OnceClosure quit_closure_; + + bool is_running_ = false; +}; + +// Implementation of XDragDropClient which short circuits FindWindowFor(). +class SimpleTestDragDropClient : public aura::client::DragDropClient, + public ui::XDragDropClient, + public ui::XDragDropClient::Delegate, + public ui::X11MoveLoopDelegate { + public: + SimpleTestDragDropClient(aura::Window*, + DesktopNativeCursorManager* cursor_manager); + ~SimpleTestDragDropClient() override; + + // Sets |window| as the topmost window for all mouse positions. + void SetTopmostXWindow(x11::Window window); + + // Returns true if the move loop is running. + bool IsMoveLoopRunning(); + + // aura::client::DragDropClient: + int StartDragAndDrop(std::unique_ptr<ui::OSExchangeData> data, + aura::Window* root_window, + aura::Window* source_window, + const gfx::Point& screen_location, + int operation, + ui::DragDropTypes::DragEventSource source) override; + void DragCancel() override; + bool IsDragDropInProgress() override; + void AddObserver(aura::client::DragDropClientObserver* observer) override; + void RemoveObserver(aura::client::DragDropClientObserver* observer) override; + + private: + // ui::XDragDropClient::Delegate: + std::unique_ptr<ui::XTopmostWindowFinder> CreateWindowFinder() override; + int UpdateDrag(const gfx::Point& screen_point) override; + void UpdateCursor( + ui::DragDropTypes::DragOperation negotiated_operation) override; + void OnBeginForeignDrag(x11::Window window) override; + void OnEndForeignDrag() override; + void OnBeforeDragLeave() override; + int PerformDrop() override; + void EndDragLoop() override; + + // XDragDropClient: + x11::Window FindWindowFor(const gfx::Point& screen_point) override; + + // ui::X11MoveLoopDelegate: + void OnMouseMovement(const gfx::Point& screen_point, + int flags, + base::TimeTicks event_time) override; + void OnMouseReleased() override; + void OnMoveLoopEnded() override; + + std::unique_ptr<ui::X11MoveLoop> CreateMoveLoop( + ui::X11MoveLoopDelegate* delegate); + + // The x11::Window of the window which is simulated to be the topmost window. + x11::Window target_window_ = x11::Window::None; + + // The move loop. Not owned. + TestMoveLoop* loop_ = nullptr; + + base::OnceClosure quit_closure_; + + DISALLOW_COPY_AND_ASSIGN(SimpleTestDragDropClient); +}; + +// Implementation of XDragDropClient which works with a fake +// |XDragDropClient::source_current_window_|. +class TestDragDropClient : public SimpleTestDragDropClient { + public: + // The location in screen coordinates used for the synthetic mouse moves + // generated in SetTopmostXWindowAndMoveMouse(). + static constexpr int kMouseMoveX = 100; + static constexpr int kMouseMoveY = 200; + + TestDragDropClient(aura::Window* window, + DesktopNativeCursorManager* cursor_manager); + ~TestDragDropClient() override; + + // Returns the x11::Window of the window which initiated the drag. + x11::Window source_xwindow() { return source_window_; } + + // Returns the Atom with |name|. + x11::Atom GetAtom(const char* name); + + // Returns true if the event's message has |type|. + bool MessageHasType(const x11::ClientMessageEvent& event, const char* type); + + // Sets |collector| to collect x11::ClientMessageEvents which would otherwise + // have been sent to the drop target window. + void SetEventCollectorFor(x11::Window window, + ClientMessageEventCollector* collector); + + // Builds an XdndStatus message and sends it to + // XDragDropClient. + void OnStatus(x11::Window target_window, + bool will_accept_drop, + x11::Atom accepted_action); + + // Builds an XdndFinished message and sends it to + // XDragDropClient. + void OnFinished(x11::Window target_window, + bool accepted_drop, + x11::Atom performed_action); + + // Sets |window| as the topmost window at the current mouse position and + // generates a synthetic mouse move. + void SetTopmostXWindowAndMoveMouse(x11::Window window); + + private: + // XDragDropClient: + void SendXClientEvent(x11::Window window, + const x11::ClientMessageEvent& event) override; + + // The x11::Window of the window which initiated the drag. + x11::Window source_window_; + + // Map of x11::Windows to the collector which intercepts + // x11::ClientMessageEvents for that window. + std::map<x11::Window, ClientMessageEventCollector*> collectors_; + + DISALLOW_COPY_AND_ASSIGN(TestDragDropClient); +}; + +/////////////////////////////////////////////////////////////////////////////// +// ClientMessageEventCollector + +ClientMessageEventCollector::ClientMessageEventCollector( + x11::Window window, + TestDragDropClient* client) + : window_(window), client_(client) { + client->SetEventCollectorFor(window, this); +} + +ClientMessageEventCollector::~ClientMessageEventCollector() { + client_->SetEventCollectorFor(window_, nullptr); +} + +std::vector<x11::ClientMessageEvent> +ClientMessageEventCollector::PopAllEvents() { + std::vector<x11::ClientMessageEvent> to_return; + to_return.swap(events_); + return to_return; +} + +void ClientMessageEventCollector::RecordEvent( + const x11::ClientMessageEvent& event) { + events_.push_back(event); +} + +/////////////////////////////////////////////////////////////////////////////// +// TestMoveLoop + +TestMoveLoop::TestMoveLoop(ui::X11MoveLoopDelegate* delegate) + : delegate_(delegate) {} + +TestMoveLoop::~TestMoveLoop() = default; + +bool TestMoveLoop::IsRunning() const { + return is_running_; +} + +bool TestMoveLoop::RunMoveLoop(bool can_grab_pointer, + ::Cursor old_cursor, + ::Cursor new_cursor) { + is_running_ = true; + base::RunLoop run_loop; + quit_closure_ = run_loop.QuitClosure(); + run_loop.Run(); + return true; +} + +void TestMoveLoop::UpdateCursor(::Cursor cursor) {} + +void TestMoveLoop::EndMoveLoop() { + if (is_running_) { + delegate_->OnMoveLoopEnded(); + is_running_ = false; + std::move(quit_closure_).Run(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// SimpleTestDragDropClient + +SimpleTestDragDropClient::SimpleTestDragDropClient( + aura::Window* window, + DesktopNativeCursorManager* cursor_manager) + : ui::XDragDropClient(this, window->GetHost()->GetAcceleratedWidget()) {} + +SimpleTestDragDropClient::~SimpleTestDragDropClient() = default; + +void SimpleTestDragDropClient::SetTopmostXWindow(x11::Window window) { + target_window_ = window; +} + +bool SimpleTestDragDropClient::IsMoveLoopRunning() { + return loop_->IsRunning(); +} + +std::unique_ptr<ui::X11MoveLoop> SimpleTestDragDropClient::CreateMoveLoop( + ui::X11MoveLoopDelegate* delegate) { + loop_ = new TestMoveLoop(delegate); + return base::WrapUnique(loop_); +} + +int SimpleTestDragDropClient::StartDragAndDrop( + std::unique_ptr<ui::OSExchangeData> data, + aura::Window* root_window, + aura::Window* source_window, + const gfx::Point& screen_location, + int operation, + ui::DragDropTypes::DragEventSource source) { + InitDrag(operation, data.get()); + + auto loop = CreateMoveLoop(this); + + // Windows has a specific method, DoDragDrop(), which performs the entire + // drag. We have to emulate this, so we spin off a nested runloop which will + // track all cursor movement and reroute events to a specific handler. + auto cursor_manager_ = std::make_unique<DesktopNativeCursorManager>(); + auto* last_cursor = static_cast<ui::X11Cursor*>( + source_window->GetHost()->last_cursor().platform()); + loop_->RunMoveLoop( + !source_window->HasCapture(), + last_cursor ? last_cursor->xcursor() : x11::None, + static_cast<ui::X11Cursor*>( + cursor_manager_ + ->GetInitializedCursor(ui::mojom::CursorType::kGrabbing) + .platform()) + ->xcursor()); + + auto resulting_operation = negotiated_operation(); + CleanupDrag(); + return resulting_operation; +} + +void SimpleTestDragDropClient::DragCancel() {} +bool SimpleTestDragDropClient::IsDragDropInProgress() { + return false; +} +void SimpleTestDragDropClient::AddObserver( + aura::client::DragDropClientObserver* observer) {} +void SimpleTestDragDropClient::RemoveObserver( + aura::client::DragDropClientObserver* observer) {} + +int SimpleTestDragDropClient::UpdateDrag(const gfx::Point& screen_point) { + return 0; +} + +std::unique_ptr<ui::XTopmostWindowFinder> +SimpleTestDragDropClient::CreateWindowFinder() { + return {}; +} +void SimpleTestDragDropClient::UpdateCursor( + ui::DragDropTypes::DragOperation negotiated_operation) {} +void SimpleTestDragDropClient::OnBeginForeignDrag(x11::Window window) {} +void SimpleTestDragDropClient::OnEndForeignDrag() {} +void SimpleTestDragDropClient::OnBeforeDragLeave() {} +int SimpleTestDragDropClient::PerformDrop() { + return 0; +} +void SimpleTestDragDropClient::EndDragLoop() { + // std::move(quit_closure_).Run(); + loop_->EndMoveLoop(); +} + +x11::Window SimpleTestDragDropClient::FindWindowFor( + const gfx::Point& screen_point) { + return target_window_; +} + +void SimpleTestDragDropClient::OnMouseMovement(const gfx::Point& screen_point, + int flags, + base::TimeTicks event_time) { + HandleMouseMovement(screen_point, flags, event_time); +} + +void SimpleTestDragDropClient::OnMouseReleased() { + HandleMouseReleased(); +} + +void SimpleTestDragDropClient::OnMoveLoopEnded() { + HandleMoveLoopEnded(); +} + +/////////////////////////////////////////////////////////////////////////////// +// TestDragDropClient + +TestDragDropClient::TestDragDropClient( + aura::Window* window, + DesktopNativeCursorManager* cursor_manager) + : SimpleTestDragDropClient(window, cursor_manager), + source_window_(window->GetHost()->GetAcceleratedWidget()) {} + +TestDragDropClient::~TestDragDropClient() = default; + +x11::Atom TestDragDropClient::GetAtom(const char* name) { + return gfx::GetAtom(name); +} + +bool TestDragDropClient::MessageHasType(const x11::ClientMessageEvent& event, + const char* type) { + return event.type == GetAtom(type); +} + +void TestDragDropClient::SetEventCollectorFor( + x11::Window window, + ClientMessageEventCollector* collector) { + if (collector) + collectors_[window] = collector; + else + collectors_.erase(window); +} + +void TestDragDropClient::OnStatus(x11::Window target_window, + bool will_accept_drop, + x11::Atom accepted_action) { + x11::ClientMessageEvent event; + event.type = GetAtom("XdndStatus"); + event.format = 32; + event.window = source_window_; + event.data.data32[0] = static_cast<uint32_t>(target_window); + event.data.data32[1] = will_accept_drop ? 1 : 0; + event.data.data32[2] = 0; + event.data.data32[3] = 0; + event.data.data32[4] = static_cast<uint32_t>(accepted_action); + HandleXdndEvent(event); +} + +void TestDragDropClient::OnFinished(x11::Window target_window, + bool accepted_drop, + x11::Atom performed_action) { + x11::ClientMessageEvent event; + event.type = GetAtom("XdndFinished"); + event.format = 32; + event.window = source_window_; + event.data.data32[0] = static_cast<uint32_t>(target_window); + event.data.data32[1] = accepted_drop ? 1 : 0; + event.data.data32[2] = static_cast<uint32_t>(performed_action); + event.data.data32[3] = 0; + event.data.data32[4] = 0; + HandleXdndEvent(event); +} + +void TestDragDropClient::SetTopmostXWindowAndMoveMouse(x11::Window window) { + SetTopmostXWindow(window); + HandleMouseMovement(gfx::Point(kMouseMoveX, kMouseMoveY), ui::EF_NONE, + ui::EventTimeForNow()); +} + +void TestDragDropClient::SendXClientEvent( + x11::Window window, + const x11::ClientMessageEvent& event) { + auto it = collectors_.find(window); + if (it != collectors_.end()) + it->second->RecordEvent(event); +} + +} // namespace + +class X11DragDropClientTest : public ViewsTestBase { + public: + X11DragDropClientTest() = default; + ~X11DragDropClientTest() override = default; + + int StartDragAndDrop() { + auto data(std::make_unique<ui::OSExchangeData>()); + data->SetString(base::ASCIIToUTF16("Test")); + SkBitmap drag_bitmap; + drag_bitmap.allocN32Pixels(10, 10); + drag_bitmap.eraseARGB(0xFF, 0, 0, 0); + gfx::ImageSkia drag_image(gfx::ImageSkia::CreateFrom1xBitmap(drag_bitmap)); + data->provider().SetDragImage(drag_image, gfx::Vector2d()); + + return client_->StartDragAndDrop( + std::move(data), widget_->GetNativeWindow()->GetRootWindow(), + widget_->GetNativeWindow(), gfx::Point(), ui::DragDropTypes::DRAG_COPY, + ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE); + } + + // ViewsTestBase: + void SetUp() override { + set_native_widget_type(NativeWidgetType::kDesktop); + + ViewsTestBase::SetUp(); + + // Create widget to initiate the drags. + widget_ = std::make_unique<Widget>(); + Widget::InitParams params(Widget::InitParams::TYPE_WINDOW); + params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.bounds = gfx::Rect(100, 100); + widget_->Init(std::move(params)); + widget_->Show(); + + cursor_manager_ = std::make_unique<DesktopNativeCursorManager>(); + + client_ = std::make_unique<TestDragDropClient>(widget_->GetNativeWindow(), + cursor_manager_.get()); + // client_->Init(); + } + + void TearDown() override { + client_.reset(); + cursor_manager_.reset(); + widget_.reset(); + ViewsTestBase::TearDown(); + } + + TestDragDropClient* client() { return client_.get(); } + + private: + std::unique_ptr<TestDragDropClient> client_; + std::unique_ptr<DesktopNativeCursorManager> cursor_manager_; + + // The widget used to initiate drags. + std::unique_ptr<Widget> widget_; + + DISALLOW_COPY_AND_ASSIGN(X11DragDropClientTest); +}; + +namespace { + +void BasicStep2(TestDragDropClient* client, x11::Window toplevel) { + EXPECT_TRUE(client->IsMoveLoopRunning()); + + ClientMessageEventCollector collector(toplevel, client); + client->SetTopmostXWindowAndMoveMouse(toplevel); + + // XdndEnter should have been sent to |toplevel| before the XdndPosition + // message. + std::vector<x11::ClientMessageEvent> events = collector.PopAllEvents(); + ASSERT_EQ(2u, events.size()); + + EXPECT_TRUE(client->MessageHasType(events[0], "XdndEnter")); + EXPECT_EQ(client->source_xwindow(), + static_cast<x11::Window>(events[0].data.data32[0])); + EXPECT_EQ(1u, events[0].data.data32[1] & 1); + std::vector<x11::Atom> targets; + ui::GetAtomArrayProperty(client->source_xwindow(), "XdndTypeList", &targets); + EXPECT_FALSE(targets.empty()); + + EXPECT_TRUE(client->MessageHasType(events[1], "XdndPosition")); + EXPECT_EQ(client->source_xwindow(), + static_cast<x11::Window>(events[0].data.data32[0])); + const uint32_t kCoords = + TestDragDropClient::kMouseMoveX << 16 | TestDragDropClient::kMouseMoveY; + EXPECT_EQ(kCoords, events[1].data.data32[2]); + EXPECT_EQ(client->GetAtom("XdndActionCopy"), + static_cast<x11::Atom>(events[1].data.data32[4])); + + client->OnStatus(toplevel, true, client->GetAtom("XdndActionCopy")); + + // Because there is no unprocessed XdndPosition, the drag drop client should + // send XdndDrop immediately after the mouse is released. + client->HandleMouseReleased(); + + events = collector.PopAllEvents(); + ASSERT_EQ(1u, events.size()); + EXPECT_TRUE(client->MessageHasType(events[0], "XdndDrop")); + EXPECT_EQ(client->source_xwindow(), + static_cast<x11::Window>(events[0].data.data32[0])); + + // Send XdndFinished to indicate that the drag drop client can cleanup any + // data related to this drag. The move loop should end only after the + // XdndFinished message was received. + EXPECT_TRUE(client->IsMoveLoopRunning()); + client->OnFinished(toplevel, true, client->GetAtom("XdndActionCopy")); + EXPECT_FALSE(client->IsMoveLoopRunning()); +} + +void BasicStep3(TestDragDropClient* client, x11::Window toplevel) { + EXPECT_TRUE(client->IsMoveLoopRunning()); + + ClientMessageEventCollector collector(toplevel, client); + client->SetTopmostXWindowAndMoveMouse(toplevel); + + std::vector<x11::ClientMessageEvent> events = collector.PopAllEvents(); + ASSERT_EQ(2u, events.size()); + EXPECT_TRUE(client->MessageHasType(events[0], "XdndEnter")); + EXPECT_TRUE(client->MessageHasType(events[1], "XdndPosition")); + + client->OnStatus(toplevel, true, client->GetAtom("XdndActionCopy")); + client->SetTopmostXWindowAndMoveMouse(toplevel); + events = collector.PopAllEvents(); + ASSERT_EQ(1u, events.size()); + EXPECT_TRUE(client->MessageHasType(events[0], "XdndPosition")); + + // We have not received an XdndStatus ack for the second XdndPosition message. + // Test that sending XdndDrop is delayed till the XdndStatus ack is received. + client->HandleMouseReleased(); + EXPECT_FALSE(collector.HasEvents()); + + client->OnStatus(toplevel, true, client->GetAtom("XdndActionCopy")); + events = collector.PopAllEvents(); + ASSERT_EQ(1u, events.size()); + EXPECT_TRUE(client->MessageHasType(events[0], "XdndDrop")); + + EXPECT_TRUE(client->IsMoveLoopRunning()); + client->OnFinished(toplevel, true, client->GetAtom("XdndActionCopy")); + EXPECT_FALSE(client->IsMoveLoopRunning()); +} + +} // namespace + +TEST_F(X11DragDropClientTest, Basic) { + x11::Window toplevel = static_cast<x11::Window>(1); + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&BasicStep2, client(), toplevel)); + int result = StartDragAndDrop(); + EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, result); + + // Do another drag and drop to test that the data is properly cleaned up as a + // result of the XdndFinished message. + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&BasicStep3, client(), toplevel)); + result = StartDragAndDrop(); + EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, result); +} + +namespace { + +void TargetDoesNotRespondStep2(TestDragDropClient* client) { + EXPECT_TRUE(client->IsMoveLoopRunning()); + + x11::Window toplevel = static_cast<x11::Window>(1); + ClientMessageEventCollector collector(toplevel, client); + client->SetTopmostXWindowAndMoveMouse(toplevel); + + std::vector<x11::ClientMessageEvent> events = collector.PopAllEvents(); + ASSERT_EQ(2u, events.size()); + EXPECT_TRUE(client->MessageHasType(events[0], "XdndEnter")); + EXPECT_TRUE(client->MessageHasType(events[1], "XdndPosition")); + + client->HandleMouseReleased(); + events = collector.PopAllEvents(); + ASSERT_EQ(1u, events.size()); + EXPECT_TRUE(client->MessageHasType(events[0], "XdndLeave")); + EXPECT_FALSE(client->IsMoveLoopRunning()); +} + +} // namespace + +// Test that we do not wait for the target to send XdndStatus if we have not +// received any XdndStatus messages at all from the target. The Unity +// DNDCollectionWindow is an example of an XdndAware target which does not +// respond to XdndPosition messages at all. +TEST_F(X11DragDropClientTest, TargetDoesNotRespond) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&TargetDoesNotRespondStep2, client())); + int result = StartDragAndDrop(); + EXPECT_EQ(ui::DragDropTypes::DRAG_NONE, result); +} + +namespace { + +void QueuePositionStep2(TestDragDropClient* client) { + EXPECT_TRUE(client->IsMoveLoopRunning()); + + x11::Window toplevel = static_cast<x11::Window>(1); + ClientMessageEventCollector collector(toplevel, client); + client->SetTopmostXWindowAndMoveMouse(toplevel); + client->SetTopmostXWindowAndMoveMouse(toplevel); + client->SetTopmostXWindowAndMoveMouse(toplevel); + + std::vector<x11::ClientMessageEvent> events = collector.PopAllEvents(); + ASSERT_EQ(2u, events.size()); + EXPECT_TRUE(client->MessageHasType(events[0], "XdndEnter")); + EXPECT_TRUE(client->MessageHasType(events[1], "XdndPosition")); + + client->OnStatus(toplevel, true, client->GetAtom("XdndActionCopy")); + events = collector.PopAllEvents(); + ASSERT_EQ(1u, events.size()); + EXPECT_TRUE(client->MessageHasType(events[0], "XdndPosition")); + + client->OnStatus(toplevel, true, client->GetAtom("XdndActionCopy")); + EXPECT_FALSE(collector.HasEvents()); + + client->HandleMouseReleased(); + events = collector.PopAllEvents(); + ASSERT_EQ(1u, events.size()); + EXPECT_TRUE(client->MessageHasType(events[0], "XdndDrop")); + + EXPECT_TRUE(client->IsMoveLoopRunning()); + client->OnFinished(toplevel, true, client->GetAtom("XdndActionCopy")); + EXPECT_FALSE(client->IsMoveLoopRunning()); +} + +} // namespace + +// Test that XdndPosition messages are queued till the pending XdndPosition +// message is acked via an XdndStatus message. +TEST_F(X11DragDropClientTest, QueuePosition) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&QueuePositionStep2, client())); + int result = StartDragAndDrop(); + EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, result); +} + +namespace { + +void TargetChangesStep2(TestDragDropClient* client) { + EXPECT_TRUE(client->IsMoveLoopRunning()); + + x11::Window toplevel1 = static_cast<x11::Window>(1); + ClientMessageEventCollector collector1(toplevel1, client); + client->SetTopmostXWindowAndMoveMouse(toplevel1); + + std::vector<x11::ClientMessageEvent> events1 = collector1.PopAllEvents(); + ASSERT_EQ(2u, events1.size()); + EXPECT_TRUE(client->MessageHasType(events1[0], "XdndEnter")); + EXPECT_TRUE(client->MessageHasType(events1[1], "XdndPosition")); + + x11::Window toplevel2 = static_cast<x11::Window>(2); + ClientMessageEventCollector collector2(toplevel2, client); + client->SetTopmostXWindowAndMoveMouse(toplevel2); + + // It is possible for |toplevel1| to send XdndStatus after the source has sent + // XdndLeave but before |toplevel1| has received the XdndLeave message. The + // XdndStatus message should be ignored. + client->OnStatus(toplevel1, true, client->GetAtom("XdndActionCopy")); + events1 = collector1.PopAllEvents(); + ASSERT_EQ(1u, events1.size()); + EXPECT_TRUE(client->MessageHasType(events1[0], "XdndLeave")); + + std::vector<x11::ClientMessageEvent> events2 = collector2.PopAllEvents(); + ASSERT_EQ(2u, events2.size()); + EXPECT_TRUE(client->MessageHasType(events2[0], "XdndEnter")); + EXPECT_TRUE(client->MessageHasType(events2[1], "XdndPosition")); + + client->OnStatus(toplevel2, true, client->GetAtom("XdndActionCopy")); + client->HandleMouseReleased(); + events2 = collector2.PopAllEvents(); + ASSERT_EQ(1u, events2.size()); + EXPECT_TRUE(client->MessageHasType(events2[0], "XdndDrop")); + + EXPECT_TRUE(client->IsMoveLoopRunning()); + client->OnFinished(toplevel2, true, client->GetAtom("XdndActionCopy")); + EXPECT_FALSE(client->IsMoveLoopRunning()); +} + +} // namespace + +// Test the behavior when the target changes during a drag. +TEST_F(X11DragDropClientTest, TargetChanges) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&TargetChangesStep2, client())); + int result = StartDragAndDrop(); + EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, result); +} + +namespace { + +void RejectAfterMouseReleaseStep2(TestDragDropClient* client) { + EXPECT_TRUE(client->IsMoveLoopRunning()); + + x11::Window toplevel = static_cast<x11::Window>(1); + ClientMessageEventCollector collector(toplevel, client); + client->SetTopmostXWindowAndMoveMouse(toplevel); + + std::vector<x11::ClientMessageEvent> events = collector.PopAllEvents(); + ASSERT_EQ(2u, events.size()); + EXPECT_TRUE(client->MessageHasType(events[0], "XdndEnter")); + EXPECT_TRUE(client->MessageHasType(events[1], "XdndPosition")); + + client->OnStatus(toplevel, true, client->GetAtom("XdndActionCopy")); + EXPECT_FALSE(collector.HasEvents()); + + // Send another mouse move such that there is a pending XdndPosition. + client->SetTopmostXWindowAndMoveMouse(toplevel); + events = collector.PopAllEvents(); + ASSERT_EQ(1u, events.size()); + EXPECT_TRUE(client->MessageHasType(events[0], "XdndPosition")); + + client->HandleMouseReleased(); + // Reject the drop. + client->OnStatus(toplevel, false, x11::Atom::None); + + events = collector.PopAllEvents(); + ASSERT_EQ(1u, events.size()); + EXPECT_TRUE(client->MessageHasType(events[0], "XdndLeave")); + EXPECT_FALSE(client->IsMoveLoopRunning()); +} + +void RejectAfterMouseReleaseStep3(TestDragDropClient* client) { + EXPECT_TRUE(client->IsMoveLoopRunning()); + + x11::Window toplevel = static_cast<x11::Window>(2); + ClientMessageEventCollector collector(toplevel, client); + client->SetTopmostXWindowAndMoveMouse(toplevel); + + std::vector<x11::ClientMessageEvent> events = collector.PopAllEvents(); + ASSERT_EQ(2u, events.size()); + EXPECT_TRUE(client->MessageHasType(events[0], "XdndEnter")); + EXPECT_TRUE(client->MessageHasType(events[1], "XdndPosition")); + + client->OnStatus(toplevel, true, client->GetAtom("XdndActionCopy")); + EXPECT_FALSE(collector.HasEvents()); + + client->HandleMouseReleased(); + events = collector.PopAllEvents(); + ASSERT_EQ(1u, events.size()); + EXPECT_TRUE(client->MessageHasType(events[0], "XdndDrop")); + + EXPECT_TRUE(client->IsMoveLoopRunning()); + client->OnFinished(toplevel, false, x11::Atom::None); + EXPECT_FALSE(client->IsMoveLoopRunning()); +} + +} // namespace + +// Test that the source sends XdndLeave instead of XdndDrop if the drag +// operation is rejected after the mouse is released. +TEST_F(X11DragDropClientTest, RejectAfterMouseRelease) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&RejectAfterMouseReleaseStep2, client())); + int result = StartDragAndDrop(); + EXPECT_EQ(ui::DragDropTypes::DRAG_NONE, result); + + // Repeat the test but reject the drop in the XdndFinished message instead. + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&RejectAfterMouseReleaseStep3, client())); + result = StartDragAndDrop(); + EXPECT_EQ(ui::DragDropTypes::DRAG_NONE, result); +} + +} // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/x11_topmost_window_finder_interactive_uitest.cc b/chromium/ui/views/widget/desktop_aura/x11_topmost_window_finder_interactive_uitest.cc index aa249625d3f..fadecc97d9d 100644 --- a/chromium/ui/views/widget/desktop_aura/x11_topmost_window_finder_interactive_uitest.cc +++ b/chromium/ui/views/widget/desktop_aura/x11_topmost_window_finder_interactive_uitest.cc @@ -15,7 +15,12 @@ #include "ui/aura/window.h" #include "ui/aura/window_tree_host.h" #include "ui/base/x/test/x11_property_change_waiter.h" +#include "ui/base/x/x11_util.h" #include "ui/events/platform/x11/x11_event_source.h" +#include "ui/gfx/native_widget_types.h" +#include "ui/gfx/x/connection.h" +#include "ui/gfx/x/event.h" +#include "ui/gfx/x/shape.h" #include "ui/gfx/x/x11.h" #include "ui/gfx/x/x11_atom_cache.h" #include "ui/gfx/x/x11_path.h" @@ -31,15 +36,15 @@ namespace { // Waits till |window| is minimized. class MinimizeWaiter : public ui::X11PropertyChangeWaiter { public: - explicit MinimizeWaiter(XID window) + explicit MinimizeWaiter(x11::Window window) : ui::X11PropertyChangeWaiter(window, "_NET_WM_STATE") {} ~MinimizeWaiter() override = default; private: // ui::X11PropertyChangeWaiter: - bool ShouldKeepOnWaiting(XEvent* event) override { - std::vector<Atom> wm_states; + bool ShouldKeepOnWaiting(x11::Event* event) override { + std::vector<x11::Atom> wm_states; if (ui::GetAtomArrayProperty(xwindow(), "_NET_WM_STATE", &wm_states)) { return !base::Contains(wm_states, gfx::GetAtom("_NET_WM_STATE_HIDDEN")); } @@ -53,7 +58,7 @@ class MinimizeWaiter : public ui::X11PropertyChangeWaiter { // |expected_windows|. class StackingClientListWaiter : public ui::X11PropertyChangeWaiter { public: - StackingClientListWaiter(XID* expected_windows, size_t count) + StackingClientListWaiter(x11::Window* expected_windows, size_t count) : ui::X11PropertyChangeWaiter(ui::GetX11RootWindow(), "_NET_CLIENT_LIST_STACKING"), expected_windows_(expected_windows, expected_windows + count) {} @@ -72,15 +77,15 @@ class StackingClientListWaiter : public ui::X11PropertyChangeWaiter { private: // ui::X11PropertyChangeWaiter: - bool ShouldKeepOnWaiting(XEvent* event) override { - std::vector<XID> stack; + bool ShouldKeepOnWaiting(x11::Event* event) override { + std::vector<x11::Window> stack; ui::GetXWindowStack(ui::GetX11RootWindow(), &stack); return !std::all_of( expected_windows_.cbegin(), expected_windows_.cend(), - [&stack](XID window) { return base::Contains(stack, window); }); + [&stack](x11::Window window) { return base::Contains(stack, window); }); } - std::vector<XID> expected_windows_; + std::vector<x11::Window> expected_windows_; DISALLOW_COPY_AND_ASSIGN(StackingClientListWaiter); }; @@ -120,34 +125,36 @@ class X11TopmostWindowFinderTest : public test::DesktopWidgetTestInteractive { } // Creates and shows an X window with |bounds|. - XID CreateAndShowXWindow(const gfx::Rect& bounds) { - XID root = DefaultRootWindow(xdisplay()); - XID xid = XCreateSimpleWindow(xdisplay(), root, 0, 0, 1, 1, - 0, // border_width - 0, // border - 0); // background - - ui::SetUseOSWindowFrame(xid, false); - ShowAndSetXWindowBounds(xid, bounds); - return xid; + x11::Window CreateAndShowXWindow(const gfx::Rect& bounds) { + x11::Window root = ui::GetX11RootWindow(); + x11::Window window = static_cast<x11::Window>( + XCreateSimpleWindow(xdisplay(), static_cast<uint32_t>(root), 0, 0, 1, 1, + 0, // border_width + 0, // border + 0)); // background + + ui::SetUseOSWindowFrame(window, false); + ShowAndSetXWindowBounds(window, bounds); + return window; } - // Shows |xid| and sets its bounds. - void ShowAndSetXWindowBounds(XID xid, const gfx::Rect& bounds) { - XMapWindow(xdisplay(), xid); + // Shows |window| and sets its bounds. + void ShowAndSetXWindowBounds(x11::Window window, const gfx::Rect& bounds) { + XMapWindow(xdisplay(), static_cast<uint32_t>(window)); XWindowChanges changes = {0}; changes.x = bounds.x(); changes.y = bounds.y(); changes.width = bounds.width(); changes.height = bounds.height(); - XConfigureWindow(xdisplay(), xid, CWX | CWY | CWWidth | CWHeight, &changes); + XConfigureWindow(xdisplay(), static_cast<uint32_t>(window), + CWX | CWY | CWWidth | CWHeight, &changes); } Display* xdisplay() { return gfx::GetXDisplay(); } // Returns the topmost X window at the passed in screen position. - XID FindTopmostXWindowAt(int screen_x, int screen_y) { + x11::Window FindTopmostXWindowAt(int screen_x, int screen_y) { ui::X11TopmostWindowFinder finder; return finder.FindWindowAt(gfx::Point(screen_x, screen_y)); } @@ -158,9 +165,10 @@ class X11TopmostWindowFinderTest : public test::DesktopWidgetTestInteractive { ui::X11TopmostWindowFinder finder; auto widget = finder.FindLocalProcessWindowAt(gfx::Point(screen_x, screen_y), {}); - return widget ? DesktopWindowTreeHostPlatform::GetContentWindowForWidget( - static_cast<gfx::AcceleratedWidget>(widget)) - : nullptr; + return widget != gfx::kNullAcceleratedWidget + ? DesktopWindowTreeHostPlatform::GetContentWindowForWidget( + static_cast<gfx::AcceleratedWidget>(widget)) + : nullptr; } // Returns the topmost aura::Window at the passed in screen position ignoring @@ -175,9 +183,10 @@ class X11TopmostWindowFinderTest : public test::DesktopWidgetTestInteractive { ui::X11TopmostWindowFinder finder; auto widget = finder.FindLocalProcessWindowAt(gfx::Point(screen_x, screen_y), ignore); - return widget ? DesktopWindowTreeHostPlatform::GetContentWindowForWidget( - static_cast<gfx::AcceleratedWidget>(widget)) - : nullptr; + return widget != gfx::kNullAcceleratedWidget + ? DesktopWindowTreeHostPlatform::GetContentWindowForWidget( + static_cast<gfx::AcceleratedWidget>(widget)) + : nullptr; } private: @@ -191,38 +200,38 @@ TEST_F(X11TopmostWindowFinderTest, Basic) { std::unique_ptr<Widget> widget1( CreateAndShowWidget(gfx::Rect(100, 100, 200, 100))); aura::Window* window1 = widget1->GetNativeWindow(); - XID xid1 = window1->GetHost()->GetAcceleratedWidget(); + x11::Window x11_window1 = window1->GetHost()->GetAcceleratedWidget(); - XID xid2 = CreateAndShowXWindow(gfx::Rect(200, 100, 100, 200)); + x11::Window x11_window2 = CreateAndShowXWindow(gfx::Rect(200, 100, 100, 200)); std::unique_ptr<Widget> widget3( CreateAndShowWidget(gfx::Rect(100, 190, 200, 110))); aura::Window* window3 = widget3->GetNativeWindow(); - XID xid3 = window3->GetHost()->GetAcceleratedWidget(); + x11::Window x11_window3 = window3->GetHost()->GetAcceleratedWidget(); - XID xids[] = {xid1, xid2, xid3}; - StackingClientListWaiter waiter(xids, base::size(xids)); + x11::Window windows[] = {x11_window1, x11_window2, x11_window3}; + StackingClientListWaiter waiter(windows, base::size(windows)); waiter.Wait(); ui::X11EventSource::GetInstance()->DispatchXEvents(); - EXPECT_EQ(xid1, FindTopmostXWindowAt(150, 150)); + EXPECT_EQ(x11_window1, FindTopmostXWindowAt(150, 150)); EXPECT_EQ(window1, FindTopmostLocalProcessWindowAt(150, 150)); - EXPECT_EQ(xid2, FindTopmostXWindowAt(250, 150)); + EXPECT_EQ(x11_window2, FindTopmostXWindowAt(250, 150)); EXPECT_FALSE(FindTopmostLocalProcessWindowAt(250, 150)); - EXPECT_EQ(xid3, FindTopmostXWindowAt(250, 250)); + EXPECT_EQ(x11_window3, FindTopmostXWindowAt(250, 250)); EXPECT_EQ(window3, FindTopmostLocalProcessWindowAt(250, 250)); - EXPECT_EQ(xid3, FindTopmostXWindowAt(150, 250)); + EXPECT_EQ(x11_window3, FindTopmostXWindowAt(150, 250)); EXPECT_EQ(window3, FindTopmostLocalProcessWindowAt(150, 250)); - EXPECT_EQ(xid3, FindTopmostXWindowAt(150, 195)); + EXPECT_EQ(x11_window3, FindTopmostXWindowAt(150, 195)); EXPECT_EQ(window3, FindTopmostLocalProcessWindowAt(150, 195)); - EXPECT_NE(xid1, FindTopmostXWindowAt(1000, 1000)); - EXPECT_NE(xid2, FindTopmostXWindowAt(1000, 1000)); - EXPECT_NE(xid3, FindTopmostXWindowAt(1000, 1000)); + EXPECT_NE(x11_window1, FindTopmostXWindowAt(1000, 1000)); + EXPECT_NE(x11_window2, FindTopmostXWindowAt(1000, 1000)); + EXPECT_NE(x11_window3, FindTopmostXWindowAt(1000, 1000)); EXPECT_FALSE(FindTopmostLocalProcessWindowAt(1000, 1000)); EXPECT_EQ(window1, @@ -232,7 +241,7 @@ TEST_F(X11TopmostWindowFinderTest, Basic) { EXPECT_EQ(window1, FindTopmostLocalProcessWindowWithIgnore(150, 195, window3)); - XDestroyWindow(xdisplay(), xid2); + XDestroyWindow(xdisplay(), static_cast<uint32_t>(x11_window2)); } // Test that the minimized state is properly handled. @@ -240,35 +249,35 @@ TEST_F(X11TopmostWindowFinderTest, Minimized) { std::unique_ptr<Widget> widget1( CreateAndShowWidget(gfx::Rect(100, 100, 100, 100))); aura::Window* window1 = widget1->GetNativeWindow(); - XID xid1 = window1->GetHost()->GetAcceleratedWidget(); - XID xid2 = CreateAndShowXWindow(gfx::Rect(300, 100, 100, 100)); + x11::Window x11_window1 = window1->GetHost()->GetAcceleratedWidget(); + x11::Window x11_window2 = CreateAndShowXWindow(gfx::Rect(300, 100, 100, 100)); - XID xids[] = {xid1, xid2}; - StackingClientListWaiter stack_waiter(xids, base::size(xids)); + x11::Window windows[] = {x11_window1, x11_window2}; + StackingClientListWaiter stack_waiter(windows, base::size(windows)); stack_waiter.Wait(); ui::X11EventSource::GetInstance()->DispatchXEvents(); - EXPECT_EQ(xid1, FindTopmostXWindowAt(150, 150)); + EXPECT_EQ(x11_window1, FindTopmostXWindowAt(150, 150)); { - MinimizeWaiter minimize_waiter(xid1); - XIconifyWindow(xdisplay(), xid1, 0); + MinimizeWaiter minimize_waiter(x11_window1); + XIconifyWindow(xdisplay(), static_cast<uint32_t>(x11_window1), 0); minimize_waiter.Wait(); } - EXPECT_NE(xid1, FindTopmostXWindowAt(150, 150)); - EXPECT_NE(xid2, FindTopmostXWindowAt(150, 150)); + EXPECT_NE(x11_window1, FindTopmostXWindowAt(150, 150)); + EXPECT_NE(x11_window2, FindTopmostXWindowAt(150, 150)); // Repeat test for an X window which does not belong to a views::Widget // because the code path is different. - EXPECT_EQ(xid2, FindTopmostXWindowAt(350, 150)); + EXPECT_EQ(x11_window2, FindTopmostXWindowAt(350, 150)); { - MinimizeWaiter minimize_waiter(xid2); - XIconifyWindow(xdisplay(), xid2, 0); + MinimizeWaiter minimize_waiter(x11_window2); + XIconifyWindow(xdisplay(), static_cast<uint32_t>(x11_window2), 0); minimize_waiter.Wait(); } - EXPECT_NE(xid1, FindTopmostXWindowAt(350, 150)); - EXPECT_NE(xid2, FindTopmostXWindowAt(350, 150)); + EXPECT_NE(x11_window1, FindTopmostXWindowAt(350, 150)); + EXPECT_NE(x11_window2, FindTopmostXWindowAt(350, 150)); - XDestroyWindow(xdisplay(), xid2); + XDestroyWindow(xdisplay(), static_cast<uint32_t>(x11_window2)); } // Test that non-rectangular windows are properly handled. @@ -278,7 +287,8 @@ TEST_F(X11TopmostWindowFinderTest, NonRectangular) { std::unique_ptr<Widget> widget1( CreateAndShowWidget(gfx::Rect(100, 100, 100, 100))); - XID xid1 = widget1->GetNativeWindow()->GetHost()->GetAcceleratedWidget(); + x11::Window window1 = + widget1->GetNativeWindow()->GetHost()->GetAcceleratedWidget(); auto shape1 = std::make_unique<Widget::ShapeRects>(); shape1->emplace_back(0, 10, 10, 90); shape1->emplace_back(10, 0, 90, 100); @@ -287,27 +297,31 @@ TEST_F(X11TopmostWindowFinderTest, NonRectangular) { SkRegion skregion2; skregion2.op(SkIRect::MakeXYWH(0, 10, 10, 90), SkRegion::kUnion_Op); skregion2.op(SkIRect::MakeXYWH(10, 0, 90, 100), SkRegion::kUnion_Op); - XID xid2 = CreateAndShowXWindow(gfx::Rect(300, 100, 100, 100)); - gfx::XScopedPtr<REGION, gfx::XObjectDeleter<REGION, int, XDestroyRegion>> - region2(gfx::CreateRegionFromSkRegion(skregion2)); - XShapeCombineRegion(xdisplay(), xid2, ShapeBounding, 0, 0, region2.get(), - false); - XID xids[] = {xid1, xid2}; - StackingClientListWaiter stack_waiter(xids, base::size(xids)); + x11::Window window2 = CreateAndShowXWindow(gfx::Rect(300, 100, 100, 100)); + auto region2 = gfx::CreateRegionFromSkRegion(skregion2); + x11::Connection::Get()->shape().Rectangles({ + .operation = x11::Shape::So::Set, + .destination_kind = x11::Shape::Sk::Bounding, + .ordering = x11::ClipOrdering::YXBanded, + .destination_window = window2, + .rectangles = *region2, + }); + x11::Window windows[] = {window1, window2}; + StackingClientListWaiter stack_waiter(windows, base::size(windows)); stack_waiter.Wait(); ui::X11EventSource::GetInstance()->DispatchXEvents(); - EXPECT_EQ(xid1, FindTopmostXWindowAt(105, 120)); - EXPECT_NE(xid1, FindTopmostXWindowAt(105, 105)); - EXPECT_NE(xid2, FindTopmostXWindowAt(105, 105)); + EXPECT_EQ(window1, FindTopmostXWindowAt(105, 120)); + EXPECT_NE(window1, FindTopmostXWindowAt(105, 105)); + EXPECT_NE(window2, FindTopmostXWindowAt(105, 105)); // Repeat test for an X window which does not belong to a views::Widget // because the code path is different. - EXPECT_EQ(xid2, FindTopmostXWindowAt(305, 120)); - EXPECT_NE(xid1, FindTopmostXWindowAt(305, 105)); - EXPECT_NE(xid2, FindTopmostXWindowAt(305, 105)); + EXPECT_EQ(window2, FindTopmostXWindowAt(305, 120)); + EXPECT_NE(window1, FindTopmostXWindowAt(305, 105)); + EXPECT_NE(window2, FindTopmostXWindowAt(305, 105)); - XDestroyWindow(xdisplay(), xid2); + XDestroyWindow(xdisplay(), static_cast<uint32_t>(window2)); } // Test that a window with an empty shape are properly handled. @@ -317,18 +331,19 @@ TEST_F(X11TopmostWindowFinderTest, NonRectangularEmptyShape) { std::unique_ptr<Widget> widget1( CreateAndShowWidget(gfx::Rect(100, 100, 100, 100))); - XID xid1 = widget1->GetNativeWindow()->GetHost()->GetAcceleratedWidget(); + x11::Window window1 = + widget1->GetNativeWindow()->GetHost()->GetAcceleratedWidget(); auto shape1 = std::make_unique<Widget::ShapeRects>(); shape1->emplace_back(); // Widget takes ownership of |shape1|. widget1->SetShape(std::move(shape1)); - XID xids[] = {xid1}; - StackingClientListWaiter stack_waiter(xids, base::size(xids)); + x11::Window windows[] = {window1}; + StackingClientListWaiter stack_waiter(windows, base::size(windows)); stack_waiter.Wait(); ui::X11EventSource::GetInstance()->DispatchXEvents(); - EXPECT_NE(xid1, FindTopmostXWindowAt(105, 105)); + EXPECT_NE(window1, FindTopmostXWindowAt(105, 105)); } // Test that setting a Null shape removes the shape. @@ -338,7 +353,8 @@ TEST_F(X11TopmostWindowFinderTest, NonRectangularNullShape) { std::unique_ptr<Widget> widget1( CreateAndShowWidget(gfx::Rect(100, 100, 100, 100))); - XID xid1 = widget1->GetNativeWindow()->GetHost()->GetAcceleratedWidget(); + x11::Window window1 = + widget1->GetNativeWindow()->GetHost()->GetAcceleratedWidget(); auto shape1 = std::make_unique<Widget::ShapeRects>(); shape1->emplace_back(); widget1->SetShape(std::move(shape1)); @@ -346,47 +362,48 @@ TEST_F(X11TopmostWindowFinderTest, NonRectangularNullShape) { // Remove the shape - this is now just a normal window. widget1->SetShape(nullptr); - XID xids[] = {xid1}; - StackingClientListWaiter stack_waiter(xids, base::size(xids)); + x11::Window windows[] = {window1}; + StackingClientListWaiter stack_waiter(windows, base::size(windows)); stack_waiter.Wait(); ui::X11EventSource::GetInstance()->DispatchXEvents(); - EXPECT_EQ(xid1, FindTopmostXWindowAt(105, 105)); + EXPECT_EQ(window1, FindTopmostXWindowAt(105, 105)); } // Test that the TopmostWindowFinder finds windows which belong to menus // (which may or may not belong to Chrome). TEST_F(X11TopmostWindowFinderTest, Menu) { - XID xid = CreateAndShowXWindow(gfx::Rect(100, 100, 100, 100)); + x11::Window window = CreateAndShowXWindow(gfx::Rect(100, 100, 100, 100)); - XID root = DefaultRootWindow(xdisplay()); + x11::Window root = ui::GetX11RootWindow(); XSetWindowAttributes swa; swa.override_redirect = x11::True; - XID menu_xid = XCreateWindow(xdisplay(), root, 0, 0, 1, 1, - 0, // border width - CopyFromParent, // depth - InputOutput, - CopyFromParent, // visual - CWOverrideRedirect, &swa); + x11::Window menu_window = static_cast<x11::Window>(XCreateWindow( + xdisplay(), static_cast<uint32_t>(root), 0, 0, 1, 1, + 0, // border width + static_cast<int>(x11::WindowClass::CopyFromParent), // depth + static_cast<int>(x11::WindowClass::InputOutput), + nullptr, // visual + CWOverrideRedirect, &swa)); { - ui::SetAtomProperty(menu_xid, "_NET_WM_WINDOW_TYPE", "ATOM", + ui::SetAtomProperty(menu_window, "_NET_WM_WINDOW_TYPE", "ATOM", gfx::GetAtom("_NET_WM_WINDOW_TYPE_MENU")); } - ui::SetUseOSWindowFrame(menu_xid, false); - ShowAndSetXWindowBounds(menu_xid, gfx::Rect(140, 110, 100, 100)); + ui::SetUseOSWindowFrame(menu_window, false); + ShowAndSetXWindowBounds(menu_window, gfx::Rect(140, 110, 100, 100)); ui::X11EventSource::GetInstance()->DispatchXEvents(); - // |menu_xid| is never added to _NET_CLIENT_LIST_STACKING. - XID xids[] = {xid}; - StackingClientListWaiter stack_waiter(xids, base::size(xids)); + // |menu_window| is never added to _NET_CLIENT_LIST_STACKING. + x11::Window windows[] = {window}; + StackingClientListWaiter stack_waiter(windows, base::size(windows)); stack_waiter.Wait(); - EXPECT_EQ(xid, FindTopmostXWindowAt(110, 110)); - EXPECT_EQ(menu_xid, FindTopmostXWindowAt(150, 120)); - EXPECT_EQ(menu_xid, FindTopmostXWindowAt(210, 120)); + EXPECT_EQ(window, FindTopmostXWindowAt(110, 110)); + EXPECT_EQ(menu_window, FindTopmostXWindowAt(150, 120)); + EXPECT_EQ(menu_window, FindTopmostXWindowAt(210, 120)); - XDestroyWindow(xdisplay(), xid); - XDestroyWindow(xdisplay(), menu_xid); + XDestroyWindow(xdisplay(), static_cast<uint32_t>(window)); + XDestroyWindow(xdisplay(), static_cast<uint32_t>(menu_window)); } } // namespace views diff --git a/chromium/ui/views/widget/native_widget_aura_unittest.cc b/chromium/ui/views/widget/native_widget_aura_unittest.cc index 4e9ce929364..a0a3fb4a69e 100644 --- a/chromium/ui/views/widget/native_widget_aura_unittest.cc +++ b/chromium/ui/views/widget/native_widget_aura_unittest.cc @@ -318,14 +318,13 @@ class PropertyTestLayoutManager : public TestLayoutManagerBase { class PropertyTestWidgetDelegate : public WidgetDelegate { public: - explicit PropertyTestWidgetDelegate(Widget* widget) : widget_(widget) {} + explicit PropertyTestWidgetDelegate(Widget* widget) : widget_(widget) { + SetHasWindowSizeControls(true); + } ~PropertyTestWidgetDelegate() override = default; private: // WidgetDelegate: - bool CanMaximize() const override { return true; } - bool CanMinimize() const override { return true; } - bool CanResize() const override { return true; } void DeleteDelegate() override { delete this; } Widget* GetWidget() override { return widget_; } const Widget* GetWidget() const override { return widget_; } @@ -399,18 +398,18 @@ class GestureTrackingView : public View { TEST_F(NativeWidgetAuraTest, DontCaptureOnGesture) { // Create two views (both sized the same). |child| is configured not to // consume the gesture event. - GestureTrackingView* view = new GestureTrackingView(); + auto content_view = std::make_unique<GestureTrackingView>(); GestureTrackingView* child = new GestureTrackingView(); child->set_consume_gesture_event(false); - view->SetLayoutManager(std::make_unique<FillLayout>()); - view->AddChildView(child); + content_view->SetLayoutManager(std::make_unique<FillLayout>()); + content_view->AddChildView(child); std::unique_ptr<TestWidget> widget(new TestWidget()); Widget::InitParams params(Widget::InitParams::TYPE_WINDOW_FRAMELESS); params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; params.context = root_window(); params.bounds = gfx::Rect(0, 0, 100, 200); widget->Init(std::move(params)); - widget->SetContentsView(view); + GestureTrackingView* view = widget->SetContentsView(std::move(content_view)); widget->Show(); ui::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(41, 51), @@ -441,13 +440,12 @@ TEST_F(NativeWidgetAuraTest, DontCaptureOnGesture) { // Verifies views with layers are targeted for events properly. TEST_F(NativeWidgetAuraTest, PreferViewLayersToChildWindows) { // Create two widgets: |parent| and |child|. |child| is a child of |parent|. - View* parent_root = new View; std::unique_ptr<Widget> parent(new Widget()); Widget::InitParams parent_params(Widget::InitParams::TYPE_WINDOW_FRAMELESS); parent_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; parent_params.context = root_window(); parent->Init(std::move(parent_params)); - parent->SetContentsView(parent_root); + View* parent_root = parent->SetContentsView(std::make_unique<View>()); parent->SetBounds(gfx::Rect(0, 0, 400, 400)); parent->Show(); @@ -499,13 +497,12 @@ TEST_F(NativeWidgetAuraTest, PreferViewLayersToChildWindows) { TEST_F(NativeWidgetAuraTest, ShouldDescendIntoChildForEventHandlingChecksVisibleBounds) { // Create two widgets: |parent| and |child|. |child| is a child of |parent|. - View* parent_root_view = new View; Widget parent; Widget::InitParams parent_params(Widget::InitParams::TYPE_WINDOW_FRAMELESS); parent_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; parent_params.context = root_window(); parent.Init(std::move(parent_params)); - parent.SetContentsView(parent_root_view); + View* parent_root_view = parent.SetContentsView(std::make_unique<View>()); parent.SetBounds(gfx::Rect(0, 0, 400, 400)); parent.Show(); diff --git a/chromium/ui/views/widget/root_view_unittest.cc b/chromium/ui/views/widget/root_view_unittest.cc index a0640142341..6994e4864bf 100644 --- a/chromium/ui/views/widget/root_view_unittest.cc +++ b/chromium/ui/views/widget/root_view_unittest.cc @@ -56,8 +56,7 @@ TEST_F(RootViewTest, DeleteViewDuringKeyEventDispatch) { bool got_key_event = false; - View* content = new View; - widget.SetContentsView(content); + View* content = widget.SetContentsView(std::make_unique<View>()); View* child = new DeleteOnKeyEventView(&got_key_event); content->AddChildView(child); @@ -413,8 +412,7 @@ TEST_F(RootViewTest, DeleteViewOnMouseExitDispatch) { widget.Init(std::move(init_params)); widget.SetBounds(gfx::Rect(10, 10, 500, 500)); - View* content = new View; - widget.SetContentsView(content); + View* content = widget.SetContentsView(std::make_unique<View>()); bool view_destroyed = false; View* child = new DeleteViewOnEvent(ui::ET_MOUSE_EXITED, &view_destroyed); @@ -450,8 +448,7 @@ TEST_F(RootViewTest, DeleteViewOnMouseEnterDispatch) { widget.Init(std::move(init_params)); widget.SetBounds(gfx::Rect(10, 10, 500, 500)); - View* content = new View; - widget.SetContentsView(content); + View* content = widget.SetContentsView(std::make_unique<View>()); bool view_destroyed = false; View* child = new DeleteViewOnEvent(ui::ET_MOUSE_ENTERED, &view_destroyed); @@ -489,8 +486,7 @@ TEST_F(RootViewTest, RemoveViewOnMouseEnterDispatch) { widget.Init(std::move(init_params)); widget.SetBounds(gfx::Rect(10, 10, 500, 500)); - View* content = new View; - widget.SetContentsView(content); + View* content = widget.SetContentsView(std::make_unique<View>()); // |child| gets removed without being deleted, so make it a local // to prevent test memory leak. @@ -529,8 +525,7 @@ TEST_F(RootViewTest, ClearMouseMoveHandlerOnMouseExitDispatch) { widget.Init(std::move(init_params)); widget.SetBounds(gfx::Rect(10, 10, 500, 500)); - View* content = new View; - widget.SetContentsView(content); + View* content = widget.SetContentsView(std::make_unique<View>()); View* root_view = widget.GetRootView(); @@ -565,8 +560,7 @@ TEST_F(RootViewTest, widget.Init(std::move(init_params)); widget.SetBounds(gfx::Rect(10, 10, 500, 500)); - View* content = new View; - widget.SetContentsView(content); + View* content = widget.SetContentsView(std::make_unique<View>()); View* root_view = widget.GetRootView(); @@ -603,8 +597,7 @@ TEST_F(RootViewTest, ClearMouseMoveHandlerOnMouseEnterDispatch) { widget.Init(std::move(init_params)); widget.SetBounds(gfx::Rect(10, 10, 500, 500)); - View* content = new View; - widget.SetContentsView(content); + View* content = widget.SetContentsView(std::make_unique<View>()); View* root_view = widget.GetRootView(); @@ -657,10 +650,10 @@ TEST_F(RootViewTest, DeleteWidgetOnMouseExitDispatch) { widget->SetBounds(gfx::Rect(10, 10, 500, 500)); WidgetDeletionObserver widget_deletion_observer(widget); - View* content = new View(); + auto content = std::make_unique<View>(); View* child = new DeleteWidgetOnMouseExit(widget); content->AddChildView(child); - widget->SetContentsView(content); + widget->SetContentsView(std::move(content)); // Make |child| smaller than the containing Widget and RootView. child->SetBounds(100, 100, 100, 100); @@ -693,10 +686,9 @@ TEST_F(RootViewTest, DeleteWidgetOnMouseExitDispatchFromChild) { widget->SetBounds(gfx::Rect(10, 10, 500, 500)); WidgetDeletionObserver widget_deletion_observer(widget); - View* content = new View(); View* child = new DeleteWidgetOnMouseExit(widget); View* subchild = new View(); - widget->SetContentsView(content); + View* content = widget->SetContentsView(std::make_unique<View>()); content->AddChildView(child); child->AddChildView(subchild); diff --git a/chromium/ui/views/widget/unique_widget_ptr.cc b/chromium/ui/views/widget/unique_widget_ptr.cc new file mode 100644 index 00000000000..558c9f35b23 --- /dev/null +++ b/chromium/ui/views/widget/unique_widget_ptr.cc @@ -0,0 +1,97 @@ +// Copyright 2020 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 "ui/views/widget/unique_widget_ptr.h" + +#include <utility> + +#include "base/scoped_observer.h" +#include "ui/views/view.h" +#include "ui/views/widget/widget.h" +#include "ui/views/widget/widget_observer.h" + +namespace views { + +namespace { + +struct WidgetAutoCloser { + void operator()(Widget* widget) { + widget->CloseWithReason(Widget::ClosedReason::kUnspecified); + } +}; + +using WidgetAutoClosePtr = std::unique_ptr<Widget, WidgetAutoCloser>; + +} // namespace + +class UniqueWidgetPtr::UniqueWidgetPtrImpl : public WidgetObserver { + public: + UniqueWidgetPtrImpl() = default; + // Deliberately implicit + // NOLINTNEXTLINE(runtime/explicit) + UniqueWidgetPtrImpl(std::unique_ptr<Widget> widget) + : widget_closer_(widget.release()) { + widget_observer_.Add(widget_closer_.get()); + } + + UniqueWidgetPtrImpl(const UniqueWidgetPtrImpl&) = delete; + + UniqueWidgetPtrImpl& operator=(const UniqueWidgetPtrImpl&) = delete; + + ~UniqueWidgetPtrImpl() override = default; + + Widget* Get() const { return widget_closer_.get(); } + + void Reset() { + if (!widget_closer_) + return; + widget_observer_.RemoveAll(); + widget_closer_.reset(); + } + + // WidgetObserver overrides. + void OnWidgetDestroying(Widget* widget) override { + DCHECK_EQ(widget, widget_closer_.get()); + widget_observer_.RemoveAll(); + widget_closer_.release(); + } + + private: + ScopedObserver<Widget, WidgetObserver> widget_observer_{this}; + WidgetAutoClosePtr widget_closer_; +}; + +UniqueWidgetPtr::UniqueWidgetPtr() = default; + +UniqueWidgetPtr::UniqueWidgetPtr(std::unique_ptr<Widget> widget) + : unique_widget_ptr_impl_( + std::make_unique<UniqueWidgetPtrImpl>(std::move(widget))) {} + +UniqueWidgetPtr::UniqueWidgetPtr(UniqueWidgetPtr&& other) = default; + +UniqueWidgetPtr& UniqueWidgetPtr::operator=(UniqueWidgetPtr&& other) = default; + +UniqueWidgetPtr::~UniqueWidgetPtr() = default; + +UniqueWidgetPtr::operator bool() const { + return !!get(); +} + +Widget& UniqueWidgetPtr::operator*() const { + return *get(); +} + +Widget* UniqueWidgetPtr::operator->() const { + return get(); +} + +void UniqueWidgetPtr::reset() { + unique_widget_ptr_impl_.reset(); +} + +Widget* UniqueWidgetPtr::get() const { + return unique_widget_ptr_impl_ ? unique_widget_ptr_impl_->Get() : nullptr; +} + +} // namespace views diff --git a/chromium/ui/views/widget/unique_widget_ptr.h b/chromium/ui/views/widget/unique_widget_ptr.h new file mode 100644 index 00000000000..f493c367a73 --- /dev/null +++ b/chromium/ui/views/widget/unique_widget_ptr.h @@ -0,0 +1,43 @@ +// Copyright 2020 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. + +#ifndef UI_VIEWS_WIDGET_UNIQUE_WIDGET_PTR_H_ +#define UI_VIEWS_WIDGET_UNIQUE_WIDGET_PTR_H_ + +#include <memory> + +#include "ui/views/views_export.h" + +namespace views { + +class Widget; + +// Ensures the Widget is properly closed when this special +// auto pointer goes out of scope. + +class VIEWS_EXPORT UniqueWidgetPtr { + public: + UniqueWidgetPtr(); + // Deliberately implicit since it's supposed to resemble a std::unique_ptr. + // NOLINTNEXTLINE(runtime/explicit) + UniqueWidgetPtr(std::unique_ptr<Widget> widget); + UniqueWidgetPtr(UniqueWidgetPtr&&); + UniqueWidgetPtr& operator=(UniqueWidgetPtr&&); + ~UniqueWidgetPtr(); + + explicit operator bool() const; + Widget& operator*() const; + Widget* operator->() const; + void reset(); + Widget* get() const; + + private: + class UniqueWidgetPtrImpl; + + std::unique_ptr<UniqueWidgetPtrImpl> unique_widget_ptr_impl_; +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_UNIQUE_WIDGET_PTR_H_ diff --git a/chromium/ui/views/widget/unique_widget_ptr_unittest.cc b/chromium/ui/views/widget/unique_widget_ptr_unittest.cc new file mode 100644 index 00000000000..df8804ab1f9 --- /dev/null +++ b/chromium/ui/views/widget/unique_widget_ptr_unittest.cc @@ -0,0 +1,133 @@ +// Copyright 2020 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 "ui/views/widget/unique_widget_ptr.h" + +#include <memory> + +#include "base/scoped_observer.h" +#include "ui/views/test/views_test_base.h" +#include "ui/views/view.h" +#include "ui/views/widget/widget.h" +#include "ui/views/widget/widget_observer.h" + +namespace views { + +class UniqueWidgetPtrTest : public ViewsTestBase, public WidgetObserver { + public: + UniqueWidgetPtrTest() = default; + ~UniqueWidgetPtrTest() override = default; + + // ViewsTestBase overrides. + void TearDown() override { + ViewsTestBase::TearDown(); + ASSERT_EQ(widget_, nullptr); + } + + protected: + std::unique_ptr<Widget> AllocateTestWidget() override { + auto widget = ViewsTestBase::AllocateTestWidget(); + widget->Init(CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS)); + widget_observer_.Add(widget.get()); + return widget; + } + + UniqueWidgetPtr CreateUniqueWidgetPtr() { + auto widget = UniqueWidgetPtr(AllocateTestWidget()); + widget->SetContentsView(std::make_unique<View>()); + widget_ = widget.get(); + return widget; + } + + Widget* widget() { return widget_; } + + // WidgetObserver overrides. + void OnWidgetDestroying(Widget* widget) override { + ASSERT_NE(widget_, nullptr); + ASSERT_EQ(widget_, widget); + widget_observer_.Remove(widget_); + widget_ = nullptr; + } + + private: + Widget* widget_ = nullptr; + ScopedObserver<Widget, WidgetObserver> widget_observer_{this}; +}; + +// Make sure explicitly resetting the |unique_widget_ptr| variable properly +// closes the widget. TearDown() will ensure |widget_| has been cleared. +TEST_F(UniqueWidgetPtrTest, TestCloseContent) { + UniqueWidgetPtr unique_widget_ptr = CreateUniqueWidgetPtr(); + EXPECT_EQ(unique_widget_ptr->GetContentsView(), widget()->GetContentsView()); + unique_widget_ptr.reset(); +} + +// Same as above, only testing that going out of scope will accomplish the same +// thing. +TEST_F(UniqueWidgetPtrTest, TestScopeDestruct) { + UniqueWidgetPtr unique_widget_ptr = CreateUniqueWidgetPtr(); + EXPECT_EQ(unique_widget_ptr->GetContentsView(), widget()->GetContentsView()); + // Just go out of scope to close the view; +} + +// Check that proper move semantics for assignments work. +TEST_F(UniqueWidgetPtrTest, TestMoveAssign) { + UniqueWidgetPtr unique_widget_ptr2 = CreateUniqueWidgetPtr(); + { + UniqueWidgetPtr unique_widget_ptr; + EXPECT_EQ(unique_widget_ptr2->GetContentsView(), + widget()->GetContentsView()); + unique_widget_ptr = std::move(unique_widget_ptr2); + EXPECT_EQ(unique_widget_ptr->GetContentsView(), + widget()->GetContentsView()); + EXPECT_FALSE(unique_widget_ptr2); + unique_widget_ptr.reset(); + EXPECT_FALSE(unique_widget_ptr); + } + RunPendingMessages(); + EXPECT_EQ(widget(), nullptr); +} + +// Check that move construction functions correctly. +TEST_F(UniqueWidgetPtrTest, TestMoveConstruct) { + UniqueWidgetPtr unique_widget_ptr2 = CreateUniqueWidgetPtr(); + { + EXPECT_EQ(unique_widget_ptr2->GetContentsView(), + widget()->GetContentsView()); + UniqueWidgetPtr unique_widget_ptr = std::move(unique_widget_ptr2); + EXPECT_EQ(unique_widget_ptr->GetContentsView(), + widget()->GetContentsView()); + EXPECT_FALSE(unique_widget_ptr2); + unique_widget_ptr.reset(); + EXPECT_FALSE(unique_widget_ptr); + } + RunPendingMessages(); + EXPECT_EQ(widget(), nullptr); +} + +// Make sure that any external closing of the widget is properly tracked in the +// |unique_widget_ptr|. +TEST_F(UniqueWidgetPtrTest, TestCloseWidget) { + UniqueWidgetPtr unique_widget_ptr = CreateUniqueWidgetPtr(); + EXPECT_EQ(unique_widget_ptr->GetContentsView(), widget()->GetContentsView()); + // Initiate widget destruction. + widget()->CloseWithReason(Widget::ClosedReason::kUnspecified); + // Cycle the run loop to allow the deferred destruction to happen. + RunPendingMessages(); + // The UniqueWidgetPtr should have dropped its reference to the content view. + EXPECT_FALSE(unique_widget_ptr); +} + +// When the NativeWidget is destroyed, ensure that the Widget is also destroyed +// which in turn clears the |unique_widget_ptr|. +TEST_F(UniqueWidgetPtrTest, TestCloseNativeWidget) { + UniqueWidgetPtr unique_widget_ptr = CreateUniqueWidgetPtr(); + EXPECT_EQ(unique_widget_ptr->GetContentsView(), widget()->GetContentsView()); + // Initiate an OS level native widget destruction. + SimulateNativeDestroy(widget()); + // The UniqueWidgetPtr should have dropped its reference to the content view. + EXPECT_FALSE(unique_widget_ptr); +} + +} // namespace views diff --git a/chromium/ui/views/widget/widget.cc b/chromium/ui/views/widget/widget.cc index ecd7be9185f..a06ebe82c33 100644 --- a/chromium/ui/views/widget/widget.cc +++ b/chromium/ui/views/widget/widget.cc @@ -93,7 +93,7 @@ bool Widget::g_disable_activation_change_handling_ = false; // WidgetDelegate is supplied. class DefaultWidgetDelegate : public WidgetDelegate { public: - explicit DefaultWidgetDelegate(Widget* widget) : widget_(widget) { + DefaultWidgetDelegate() { // In most situations where a Widget is used without a delegate the Widget // is used as a container, so that we want focus to advance to the top-level // widget. A good example of this is the find bar. @@ -103,12 +103,8 @@ class DefaultWidgetDelegate : public WidgetDelegate { // WidgetDelegate: void DeleteDelegate() override { delete this; } - Widget* GetWidget() override { return widget_; } - const Widget* GetWidget() const override { return widget_; } private: - Widget* widget_; - DISALLOW_COPY_AND_ASSIGN(DefaultWidgetDelegate); }; @@ -179,6 +175,8 @@ Widget::Widget(InitParams params) { } Widget::~Widget() { + if (widget_delegate_) + widget_delegate_->WidgetDestroying(); DestroyRootView(); if (ownership_ == InitParams::WIDGET_OWNS_NATIVE_WIDGET) { delete native_widget_; @@ -298,8 +296,7 @@ void Widget::Init(InitParams params) { // ViewsDelegate::OnBeforeWidgetInit() may change `params.delegate` either // by setting it to null or assigning a different value to it, so handle // both cases. - auto default_widget_delegate = - std::make_unique<DefaultWidgetDelegate>(this); + auto default_widget_delegate = std::make_unique<DefaultWidgetDelegate>(); widget_delegate_ = params.delegate ? params.delegate : default_widget_delegate.get(); @@ -322,6 +319,9 @@ void Widget::Init(InitParams params) { // Henceforth, ensure the delegate outlives the Widget. widget_delegate_->can_delete_this_ = false; + if (params.delegate) + params.delegate->WidgetInitializing(this); + ownership_ = params.ownership; native_widget_ = CreateNativeWidget(params, this)->AsNativeWidgetPrivate(); root_view_.reset(CreateRootView()); @@ -377,7 +377,7 @@ void Widget::Init(InitParams params) { native_widget_->OnWidgetInitDone(); if (delegate) - delegate->OnWidgetInitialized(); + delegate->WidgetInitialized(); internal::AnyWidgetObserverSingleton::GetInstance()->OnAnyWidgetInitialized( this); @@ -1162,6 +1162,8 @@ gfx::Size Widget::GetMaximumSize() const { } void Widget::OnNativeWidgetMove() { + TRACE_EVENT0("ui", "Widget::OnNativeWidgetMove"); + widget_delegate_->OnWidgetMove(); NotifyCaretBoundsChanged(GetInputMethod()); @@ -1170,6 +1172,8 @@ void Widget::OnNativeWidgetMove() { } void Widget::OnNativeWidgetSizeChanged(const gfx::Size& new_size) { + TRACE_EVENT0("ui", "Widget::OnNativeWidgetSizeChanged"); + View* root = GetRootView(); if (root) root->SetSize(new_size); @@ -1229,6 +1233,8 @@ void Widget::OnKeyEvent(ui::KeyEvent* event) { // RootView from anywhere in Widget. Use // SendEventToSink() instead. See crbug.com/348087. void Widget::OnMouseEvent(ui::MouseEvent* event) { + TRACE_EVENT0("ui", "Widget::OnMouseEvent"); + View* root_view = GetRootView(); switch (event->type()) { case ui::ET_MOUSE_PRESSED: { @@ -1472,6 +1478,8 @@ View* Widget::GetFocusTraversableParentView() { // Widget, ui::NativeThemeObserver implementation: void Widget::OnNativeThemeUpdated(ui::NativeTheme* observed_theme) { + TRACE_EVENT0("ui", "Widget::OnNativeThemeUpdated"); + DCHECK(observer_manager_.IsObserving(observed_theme)); #if defined(OS_MACOSX) || defined(OS_WIN) diff --git a/chromium/ui/views/widget/widget_delegate.cc b/chromium/ui/views/widget/widget_delegate.cc index 4ba80f860b3..6916ad1c393 100644 --- a/chromium/ui/views/widget/widget_delegate.cc +++ b/chromium/ui/views/widget/widget_delegate.cc @@ -7,6 +7,7 @@ #include "base/check.h" #include "base/strings/utf_string_conversions.h" #include "ui/accessibility/ax_enums.mojom.h" +#include "ui/base/l10n/l10n_util.h" #include "ui/display/display.h" #include "ui/display/screen.h" #include "ui/gfx/image/image_skia.h" @@ -46,7 +47,7 @@ View* WidgetDelegate::GetInitiallyFocusedView() { return nullptr; } -BubbleDialogDelegateView* WidgetDelegate::AsBubbleDialogDelegate() { +BubbleDialogDelegate* WidgetDelegate::AsBubbleDialogDelegate() { return nullptr; } @@ -75,11 +76,12 @@ ui::ModalType WidgetDelegate::GetModalType() const { } ax::mojom::Role WidgetDelegate::GetAccessibleWindowRole() { - return ax::mojom::Role::kWindow; + return params_.accessible_role; } base::string16 WidgetDelegate::GetAccessibleWindowTitle() const { - return GetWindowTitle(); + return params_.accessible_title.empty() ? GetWindowTitle() + : params_.accessible_title; } base::string16 WidgetDelegate::GetWindowTitle() const { @@ -152,6 +154,19 @@ bool WidgetDelegate::ShouldRestoreWindowSize() const { return true; } +void WidgetDelegate::WidgetInitializing(Widget* widget) { + widget_ = widget; + OnWidgetInitializing(); +} + +void WidgetDelegate::WidgetInitialized() { + OnWidgetInitialized(); +} + +void WidgetDelegate::WidgetDestroying() { + widget_ = nullptr; +} + void WidgetDelegate::WindowWillClose() { // TODO(ellyjones): For this and the other callback methods, establish whether // any other code calls these methods. If not, DCHECK here and below that @@ -170,6 +185,14 @@ void WidgetDelegate::DeleteDelegate() { std::move(callback).Run(); } +Widget* WidgetDelegate::GetWidget() { + return widget_; +} + +const Widget* WidgetDelegate::GetWidget() const { + return widget_; +} + View* WidgetDelegate::GetContentsView() { if (!default_contents_view_) default_contents_view_ = new View; @@ -206,6 +229,14 @@ bool WidgetDelegate::ShouldDescendIntoChildForEventHandling( return true; } +void WidgetDelegate::SetAccessibleRole(ax::mojom::Role role) { + params_.accessible_role = role; +} + +void WidgetDelegate::SetAccessibleTitle(base::string16 title) { + params_.accessible_title = std::move(title); +} + void WidgetDelegate::SetCanMaximize(bool can_maximize) { params_.can_maximize = can_maximize; } @@ -246,12 +277,22 @@ void WidgetDelegate::SetTitle(const base::string16& title) { GetWidget()->UpdateWindowTitle(); } +void WidgetDelegate::SetTitle(int title_message_id) { + SetTitle(l10n_util::GetStringUTF16(title_message_id)); +} + #if defined(USE_AURA) void WidgetDelegate::SetCenterTitle(bool center_title) { params_.center_title = center_title; } #endif +void WidgetDelegate::SetHasWindowSizeControls(bool has_controls) { + SetCanMaximize(has_controls); + SetCanMinimize(has_controls); + SetCanResize(has_controls); +} + void WidgetDelegate::RegisterWindowWillCloseCallback( base::OnceClosure callback) { window_will_close_callbacks_.emplace_back(std::move(callback)); diff --git a/chromium/ui/views/widget/widget_delegate.h b/chromium/ui/views/widget/widget_delegate.h index 23396ad8c3a..6b4ef19ee72 100644 --- a/chromium/ui/views/widget/widget_delegate.h +++ b/chromium/ui/views/widget/widget_delegate.h @@ -9,7 +9,7 @@ #include <vector> #include "base/macros.h" -#include "ui/accessibility/ax_enums.mojom-forward.h" +#include "ui/accessibility/ax_enums.mojom.h" #include "ui/base/ui_base_types.h" #include "ui/views/view.h" #include "ui/views/widget/widget.h" @@ -20,7 +20,7 @@ class Rect; } // namespace gfx namespace views { -class BubbleDialogDelegateView; +class BubbleDialogDelegate; class ClientView; class DialogDelegate; class NonClientFrameView; @@ -33,6 +33,17 @@ class VIEWS_EXPORT WidgetDelegate { Params(); ~Params(); + // The window's role. Useful values include kWindow (a plain window), + // kDialog (a dialog), and kAlertDialog (a high-priority dialog whose body + // is read when it appears). Using a role outside this set is not likely to + // work across platforms. + ax::mojom::Role accessible_role = ax::mojom::Role::kWindow; + + // The accessible title for the window, often more descriptive than the + // plain title. If no accessible title is present the result of + // GetWindowTitle() will be used. + base::string16 accessible_title; + // Whether the window should display controls for the user to minimize, // maximize, or resize it. bool can_maximize = false; @@ -68,6 +79,7 @@ class VIEWS_EXPORT WidgetDelegate { }; WidgetDelegate(); + virtual ~WidgetDelegate(); // Sets the return value of CanActivate(). Default is true. void SetCanActivate(bool can_activate); @@ -82,6 +94,10 @@ class VIEWS_EXPORT WidgetDelegate { // menu bars, etc.) changes in size. virtual void OnWorkAreaChanged(); + // Called when the widget's initialization is beginning, right after the + // ViewsDelegate decides to use this WidgetDelegate for a Widget. + virtual void OnWidgetInitializing() {} + // Called when the widget's initialization is complete. virtual void OnWidgetInitialized() {} @@ -98,7 +114,7 @@ class VIEWS_EXPORT WidgetDelegate { // NULL no view is focused. virtual View* GetInitiallyFocusedView(); - virtual BubbleDialogDelegateView* AsBubbleDialogDelegate(); + virtual BubbleDialogDelegate* AsBubbleDialogDelegate(); virtual DialogDelegate* AsDialogDelegate(); // Returns true if the window can be resized. @@ -193,10 +209,28 @@ class VIEWS_EXPORT WidgetDelegate { virtual void OnWindowEndUserBoundsChange() {} // Returns the Widget associated with this delegate. - virtual Widget* GetWidget() = 0; - virtual const Widget* GetWidget() const = 0; + virtual Widget* GetWidget(); + virtual const Widget* GetWidget() const; - // Returns the View that is contained within this Widget. + // Get the view that is contained within this widget. + // + // WARNING: This method has unusual ownership behavior: + // * If the returned view is owned_by_client(), then the returned pointer is + // never an owning pointer; + // * If the returned view is !owned_by_client() (the default & the + // recommendation), then the returned pointer is *sometimes* an owning + // pointer and sometimes not. Specifically, it is an owning pointer exactly + // once, when this method is being used to construct the ClientView, which + // takes ownership of the ContentsView() when !owned_by_client(). + // + // Apart from being difficult to reason about this introduces a problem: a + // WidgetDelegate can't know whether it owns its contents view or not, so + // constructing a WidgetDelegate which one does not then use to construct a + // Widget (often done in tests) leaks memory in a way that can't be locally + // fixed. + // + // TODO(ellyjones): This is not tenable - figure out how this should work and + // replace it. virtual View* GetContentsView(); // Called by the Widget to create the Client View used to host the contents @@ -237,6 +271,8 @@ class VIEWS_EXPORT WidgetDelegate { // Setters for data parameters of the WidgetDelegate. If you use these // setters, there is no need to override the corresponding virtual getters. + void SetAccessibleRole(ax::mojom::Role role); + void SetAccessibleTitle(base::string16 title); void SetCanMaximize(bool can_maximize); void SetCanMinimize(bool can_minimize); void SetCanResize(bool can_resize); @@ -246,16 +282,24 @@ class VIEWS_EXPORT WidgetDelegate { void SetShowIcon(bool show_icon); void SetShowTitle(bool show_title); void SetTitle(const base::string16& title); + void SetTitle(int title_message_id); #if defined(USE_AURA) void SetCenterTitle(bool center_title); #endif + // A convenience wrapper that does all three of SetCanMaximize, + // SetCanMinimize, and SetCanResize. + void SetHasWindowSizeControls(bool has_controls); + void RegisterWindowWillCloseCallback(base::OnceClosure callback); void RegisterWindowClosingCallback(base::OnceClosure callback); void RegisterDeleteDelegateCallback(base::OnceClosure callback); - // Call this to notify the WidgetDelegate that its Widget is about to start - // closing. + // Called to notify the WidgetDelegate of changes to the state of its Widget. + // It is not usually necessary to call these from client code. + void WidgetInitializing(Widget* widget); + void WidgetInitialized(); + void WidgetDestroying(); void WindowWillClose(); // Returns true if the title text should be centered. @@ -263,12 +307,12 @@ class VIEWS_EXPORT WidgetDelegate { bool focus_traverses_out() const { return params_.focus_traverses_out; } - protected: - virtual ~WidgetDelegate(); - private: friend class Widget; + // The Widget that was initialized with this instance as its WidgetDelegate, + // if any. + Widget* widget_ = nullptr; Params params_; View* default_contents_view_ = nullptr; @@ -299,7 +343,7 @@ class VIEWS_EXPORT WidgetDelegateView : public WidgetDelegate, public View { void DeleteDelegate() override; Widget* GetWidget() override; const Widget* GetWidget() const override; - views::View* GetContentsView() override; + View* GetContentsView() override; private: DISALLOW_COPY_AND_ASSIGN(WidgetDelegateView); diff --git a/chromium/ui/views/widget/widget_deletion_observer.cc b/chromium/ui/views/widget/widget_deletion_observer.cc index 15df9c391ce..3e64f1613b6 100644 --- a/chromium/ui/views/widget/widget_deletion_observer.cc +++ b/chromium/ui/views/widget/widget_deletion_observer.cc @@ -16,6 +16,7 @@ WidgetDeletionObserver::WidgetDeletionObserver(Widget* widget) WidgetDeletionObserver::~WidgetDeletionObserver() { CleanupWidget(); + CHECK(!IsInObserverList()); } void WidgetDeletionObserver::OnWidgetDestroying(Widget* widget) { diff --git a/chromium/ui/views/widget/widget_hwnd_utils.cc b/chromium/ui/views/widget/widget_hwnd_utils.cc index 40e66a212e3..89f5b62f95d 100644 --- a/chromium/ui/views/widget/widget_hwnd_utils.cc +++ b/chromium/ui/views/widget/widget_hwnd_utils.cc @@ -118,6 +118,8 @@ void CalculateWindowStylesFromInitParams( else *style |= WS_BORDER; } + if (!params.force_show_in_taskbar) + *ex_style |= WS_EX_TOOLWINDOW; break; case Widget::InitParams::TYPE_TOOLTIP: *style |= WS_POPUP; diff --git a/chromium/ui/views/widget/widget_interactive_uitest.cc b/chromium/ui/views/widget/widget_interactive_uitest.cc index f4ba5ba6cad..a333ca057ae 100644 --- a/chromium/ui/views/widget/widget_interactive_uitest.cc +++ b/chromium/ui/views/widget/widget_interactive_uitest.cc @@ -4,6 +4,7 @@ #include <stddef.h> +#include <memory> #include <utility> #include "base/bind.h" @@ -603,7 +604,7 @@ TEST_F(WidgetTestInteractive, ChildStackedRelativeToParent) { // Test view focus retention when a widget's HWND is disabled and re-enabled. TEST_F(WidgetTestInteractive, ViewFocusOnHWNDEnabledChanges) { WidgetAutoclosePtr widget(CreateTopLevelFramelessPlatformWidget()); - widget->SetContentsView(new View); + widget->SetContentsView(std::make_unique<View>()); for (size_t i = 0; i < 2; ++i) { auto child = std::make_unique<View>(); child->SetFocusBehavior(View::FocusBehavior::ALWAYS); @@ -1456,10 +1457,10 @@ TEST_F(WidgetCaptureTest, FailedCaptureRequestIsNoop) { MouseView* mouse_view1 = new MouseView; MouseView* mouse_view2 = new MouseView; - View* contents_view = new View; + auto contents_view = std::make_unique<View>(); contents_view->AddChildView(mouse_view1); contents_view->AddChildView(mouse_view2); - widget.SetContentsView(contents_view); + widget.SetContentsView(std::move(contents_view)); mouse_view1->SetBounds(0, 0, 200, 400); mouse_view2->SetBounds(200, 0, 200, 400); @@ -1480,8 +1481,7 @@ TEST_F(WidgetCaptureTest, FailedCaptureRequestIsNoop) { TEST_F(WidgetCaptureTest, CaptureAutoReset) { WidgetAutoclosePtr toplevel(CreateTopLevelFramelessPlatformWidget()); - View* container = new View; - toplevel->SetContentsView(container); + toplevel->SetContentsView(std::make_unique<View>()); EXPECT_FALSE(toplevel->HasCapture()); toplevel->SetCapture(nullptr); @@ -1507,8 +1507,7 @@ TEST_F(WidgetCaptureTest, CaptureAutoReset) { TEST_F(WidgetCaptureTest, ResetCaptureOnGestureEnd) { WidgetAutoclosePtr toplevel(CreateTopLevelFramelessPlatformWidget()); - View* container = new View; - toplevel->SetContentsView(container); + View* container = toplevel->SetContentsView(std::make_unique<View>()); View* gesture = new GestureCaptureView; gesture->SetBounds(0, 0, 30, 30); @@ -1570,10 +1569,11 @@ TEST_F(WidgetCaptureTest, DisableCaptureWidgetFromMousePress) { WidgetAutoclosePtr first(CreateTopLevelFramelessPlatformWidget()); Widget* second = CreateTopLevelFramelessPlatformWidget(); - NestedLoopCaptureView* container = new NestedLoopCaptureView(second); - first->SetContentsView(container); + NestedLoopCaptureView* container = + first->SetContentsView(std::make_unique<NestedLoopCaptureView>(second)); - second->SetContentsView(new ExitLoopOnRelease(container->GetQuitClosure())); + second->SetContentsView( + std::make_unique<ExitLoopOnRelease>(container->GetQuitClosure())); first->SetSize(gfx::Size(100, 100)); first->Show(); @@ -1597,21 +1597,21 @@ TEST_F(WidgetCaptureTest, DisableCaptureWidgetFromMousePress) { // time. TEST_F(WidgetCaptureTest, GrabUngrab) { auto top_level = CreateTestWidget(); - top_level->SetContentsView(new MouseView()); + top_level->SetContentsView(std::make_unique<MouseView>()); Widget* child1 = new Widget; Widget::InitParams params1 = CreateParams(Widget::InitParams::TYPE_CONTROL); params1.parent = top_level->GetNativeView(); params1.bounds = gfx::Rect(10, 10, 100, 100); child1->Init(std::move(params1)); - child1->SetContentsView(new MouseView()); + child1->SetContentsView(std::make_unique<MouseView>()); Widget* child2 = new Widget; Widget::InitParams params2 = CreateParams(Widget::InitParams::TYPE_CONTROL); params2.parent = top_level->GetNativeView(); params2.bounds = gfx::Rect(110, 10, 100, 100); child2->Init(std::move(params2)); - child2->SetContentsView(new MouseView()); + child2->SetContentsView(std::make_unique<MouseView>()); top_level->Show(); RunPendingMessages(); @@ -1732,8 +1732,8 @@ TEST_F(WidgetCaptureTest, MAYBE_MouseExitOnCaptureGrab) { CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); params1.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; widget1.Init(std::move(params1)); - MouseView* mouse_view1 = new MouseView; - widget1.SetContentsView(mouse_view1); + MouseView* mouse_view1 = + widget1.SetContentsView(std::make_unique<MouseView>()); widget1.Show(); widget1.SetBounds(gfx::Rect(300, 300)); diff --git a/chromium/ui/views/widget/window_reorderer_unittest.cc b/chromium/ui/views/widget/window_reorderer_unittest.cc index 670cc0f1238..c46fda38a8e 100644 --- a/chromium/ui/views/widget/window_reorderer_unittest.cc +++ b/chromium/ui/views/widget/window_reorderer_unittest.cc @@ -59,8 +59,7 @@ TEST_F(WindowReordererTest, Basic) { parent->Show(); aura::Window* parent_window = parent->GetNativeWindow(); - View* contents_view = new View(); - parent->SetContentsView(contents_view); + View* contents_view = parent->SetContentsView(std::make_unique<View>()); // 1) Test that layers for views and layers for windows associated to a host // view are stacked below the layers for any windows not associated to a host @@ -130,8 +129,7 @@ TEST_F(WindowReordererTest, Association) { parent->Show(); aura::Window* parent_window = parent->GetNativeWindow(); - View* contents_view = new View(); - parent->SetContentsView(contents_view); + View* contents_view = parent->SetContentsView(std::make_unique<View>()); aura::Window* w1 = aura::test::CreateTestWindowWithId(0, parent->GetNativeWindow()); @@ -185,8 +183,7 @@ TEST_F(WindowReordererTest, HostViewParentHasLayer) { parent->Show(); aura::Window* parent_window = parent->GetNativeWindow(); - View* contents_view = new View(); - parent->SetContentsView(contents_view); + View* contents_view = parent->SetContentsView(std::make_unique<View>()); // Create the following view hierarchy. (*) denotes views which paint to a // layer. @@ -251,8 +248,7 @@ TEST_F(WindowReordererTest, ViewWithLayerBeneath) { aura::Window* parent_window = parent->GetNativeWindow(); - View* contents_view = new View; - parent->SetContentsView(contents_view); + View* contents_view = parent->SetContentsView(std::make_unique<View>()); View* view_with_layer_beneath = contents_view->AddChildView(std::make_unique<View>()); |