diff options
Diffstat (limited to 'chromium/components/exo')
76 files changed, 2317 insertions, 361 deletions
diff --git a/chromium/components/exo/BUILD.gn b/chromium/components/exo/BUILD.gn index 3f3efb0587f..56e5f781552 100644 --- a/chromium/components/exo/BUILD.gn +++ b/chromium/components/exo/BUILD.gn @@ -6,7 +6,7 @@ import("//build/config/ui.gni") import("//chrome/common/features.gni") import("//testing/test.gni") -source_set("exo") { +static_library("exo") { sources = [ "buffer.cc", "buffer.h", @@ -79,6 +79,7 @@ source_set("exo") { "//skia", "//third_party/blink/public/common", "//ui/aura", + "//ui/base/cursor", "//ui/base/ime", "//ui/compositor", "//ui/compositor_extra", @@ -97,12 +98,13 @@ source_set("exo") { ] public_deps = [ - "//ui/base/cursor", + "//ui/base/cursor:cursor_base", "//ui/base/cursor/mojom:cursor_type", ] if (is_chromeos) { deps += [ + "//ash", "//ash/keyboard/ui", "//ash/public/cpp", "//chromeos/constants", @@ -237,6 +239,7 @@ source_set("unit_tests") { "data_offer_unittest.cc", "data_source_unittest.cc", "display_unittest.cc", + "drag_drop_operation_unittest.cc", "gaming_seat_unittest.cc", "input_method_surface_unittest.cc", "keyboard_unittest.cc", @@ -259,6 +262,7 @@ source_set("unit_tests") { "//ash/keyboard/ui", "//ash/public/cpp", "//chromeos/constants", + "//ui/base:test_support", "//ui/base/cursor/mojom:cursor_type", ] } diff --git a/chromium/components/exo/client_controlled_shell_surface.cc b/chromium/components/exo/client_controlled_shell_surface.cc index 0e55b1931e4..56fa48f3fd7 100644 --- a/chromium/components/exo/client_controlled_shell_surface.cc +++ b/chromium/components/exo/client_controlled_shell_surface.cc @@ -690,28 +690,7 @@ bool ClientControlledShellSurface::IsInputEnabled(Surface* surface) const { } void ClientControlledShellSurface::OnSetFrame(SurfaceFrameType type) { - if (container_ == ash::kShellWindowId_SystemModalContainer && - type != SurfaceFrameType::NONE) { - LOG(WARNING) - << "A surface in system modal container should not have a frame:" - << static_cast<int>(type); - return; - } - - // TODO(oshima): We shouldn't send the synthesized motion event when just - // changing the frame type. The better solution would be to keep the window - // position regardless of the frame state, but that won't be available until - // next arc version. - // This is a stopgap solution not to generate the event until it is resolved. - EventTargetingBlocker blocker; - bool suppress_mouse_event = frame_type_ != type && widget_; - if (suppress_mouse_event) - blocker.Block(widget_->GetNativeWindow()); - ShellSurfaceBase::OnSetFrame(type); - UpdateAutoHideFrame(); - - if (suppress_mouse_event) - UpdateSurfaceBounds(); + pending_frame_type_ = type; } void ClientControlledShellSurface::OnSetFrameColors(SkColor active_color, @@ -1254,6 +1233,31 @@ void ClientControlledShellSurface::UpdateFrameWidth() { ->SetWidthInPixels(width); } +void ClientControlledShellSurface::UpdateFrameType() { + if (container_ == ash::kShellWindowId_SystemModalContainer && + pending_frame_type_ != SurfaceFrameType::NONE) { + LOG(WARNING) + << "A surface in system modal container should not have a frame:" + << static_cast<int>(pending_frame_type_); + return; + } + + // TODO(oshima): We shouldn't send the synthesized motion event when just + // changing the frame type. The better solution would be to keep the window + // position regardless of the frame state, but that won't be available until + // next arc version. + // This is a stopgap solution not to generate the event until it is resolved. + EventTargetingBlocker blocker; + bool suppress_mouse_event = frame_type_ != pending_frame_type_ && widget_; + if (suppress_mouse_event) + blocker.Block(widget_->GetNativeWindow()); + ShellSurfaceBase::OnSetFrame(pending_frame_type_); + UpdateAutoHideFrame(); + + if (suppress_mouse_event) + UpdateSurfaceBounds(); +} + void ClientControlledShellSurface:: EnsureCompositorIsLockedForOrientationChange() { if (!orientation_compositor_lock_) { diff --git a/chromium/components/exo/client_controlled_shell_surface.h b/chromium/components/exo/client_controlled_shell_surface.h index 2d339f43983..20c5833a06d 100644 --- a/chromium/components/exo/client_controlled_shell_surface.h +++ b/chromium/components/exo/client_controlled_shell_surface.h @@ -285,6 +285,8 @@ class ClientControlledShellSurface : public ShellSurfaceBase, void UpdateFrameWidth(); + void UpdateFrameType() override; + void AttemptToStartDrag(int component, const gfx::PointF& location); // Lock the compositor if it's not already locked, or extends the @@ -325,6 +327,8 @@ class ClientControlledShellSurface : public ShellSurfaceBase, bool pending_always_on_top_ = false; + SurfaceFrameType pending_frame_type_ = SurfaceFrameType::NONE; + ash::WindowPinType current_pin_; bool can_maximize_ = true; diff --git a/chromium/components/exo/client_controlled_shell_surface_unittest.cc b/chromium/components/exo/client_controlled_shell_surface_unittest.cc index a99d492f2e9..924ef3dd608 100644 --- a/chromium/components/exo/client_controlled_shell_surface_unittest.cc +++ b/chromium/components/exo/client_controlled_shell_surface_unittest.cc @@ -1586,6 +1586,7 @@ TEST_F(ClientControlledShellSurfaceTest, SetExtraTitle) { // Setting the extra title/debug text won't change the window's title, but it // will be drawn by the frame header. shell_surface->SetExtraTitle(base::ASCIIToUTF16("extra")); + surface->Commit(); EXPECT_EQ(window_title, window->GetTitle()); EXPECT_TRUE(paint_does_draw_text()); EXPECT_FALSE( @@ -1933,6 +1934,7 @@ TEST_F(ClientControlledShellSurfaceTest, SnappedInTabletMode) { shell_surface->GetWidget()->non_client_view()->frame_view()); // Snapped window can also use auto hide. surface->SetFrame(SurfaceFrameType::AUTOHIDE); + surface->Commit(); EXPECT_TRUE(frame_view->GetVisible()); EXPECT_TRUE(frame_view->GetHeaderView()->in_immersive_mode()); } diff --git a/chromium/components/exo/data_device.cc b/chromium/components/exo/data_device.cc index e93d7352196..4682130192f 100644 --- a/chromium/components/exo/data_device.cc +++ b/chromium/components/exo/data_device.cc @@ -4,6 +4,7 @@ #include "components/exo/data_device.h" +#include "base/run_loop.h" #include "components/exo/data_device_delegate.h" #include "components/exo/data_offer.h" #include "components/exo/data_source.h" @@ -16,10 +17,34 @@ namespace exo { +namespace { + +constexpr base::TimeDelta kDataOfferDestructionTimeout = + base::TimeDelta::FromMilliseconds(1000); + +ui::DragDropTypes::DragOperation DndActionToDragOperation( + DndAction dnd_action) { + switch (dnd_action) { + case DndAction::kMove: + return ui::DragDropTypes::DRAG_MOVE; + case DndAction::kCopy: + return ui::DragDropTypes::DRAG_COPY; + case DndAction::kAsk: + return ui::DragDropTypes::DRAG_LINK; + case DndAction::kNone: + return ui::DragDropTypes::DRAG_NONE; + } +} + +} // namespace + DataDevice::DataDevice(DataDeviceDelegate* delegate, Seat* seat, FileHelper* file_helper) - : delegate_(delegate), seat_(seat), file_helper_(file_helper) { + : delegate_(delegate), + seat_(seat), + file_helper_(file_helper), + drop_succeeded_(false) { WMHelper::GetInstance()->AddDragDropObserver(this); ui::ClipboardMonitor::GetInstance()->AddObserver(this); @@ -44,8 +69,7 @@ void DataDevice::StartDrag(DataSource* source, seat_->StartDrag(source, origin, icon, event_source); } -void DataDevice::SetSelection(DataSource* source, uint32_t serial) { - // TODO(hirono): Check if serial is valid. crbug.com/746111 +void DataDevice::SetSelection(DataSource* source) { seat_->SetSelection(source); } @@ -83,16 +107,7 @@ int DataDevice::OnDragUpdated(const ui::DropTargetEvent& event) { // TODO(hirono): dnd_action() here may not be updated. Chrome needs to provide // a way to update DND action asynchronously. - switch (data_offer_->get()->dnd_action()) { - case DndAction::kMove: - return ui::DragDropTypes::DRAG_MOVE; - case DndAction::kCopy: - return ui::DragDropTypes::DRAG_COPY; - case DndAction::kAsk: - return ui::DragDropTypes::DRAG_LINK; - case DndAction::kNone: - return ui::DragDropTypes::DRAG_NONE; - } + return DndActionToDragOperation(data_offer_->get()->dnd_action()); } void DataDevice::OnDragExited() { @@ -107,9 +122,29 @@ int DataDevice::OnPerformDrop(const ui::DropTargetEvent& event) { if (!data_offer_) return ui::DragDropTypes::DRAG_NONE; + DndAction dnd_action = data_offer_->get()->dnd_action(); + delegate_->OnDrop(); - data_offer_.reset(); - return ui::DragDropTypes::DRAG_NONE; + + // TODO(tetsui): Avoid using nested loop by adding asynchronous callback to + // aura::client::DragDropDelegate. + base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed); + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, run_loop.QuitClosure(), kDataOfferDestructionTimeout); + quit_closure_ = run_loop.QuitClosure(); + run_loop.Run(); + + if (quit_closure_) { + // DataOffer not destroyed by the client until the timeout. + quit_closure_.Reset(); + data_offer_.reset(); + drop_succeeded_ = false; + } + + if (!drop_succeeded_) + return ui::DragDropTypes::DRAG_NONE; + + return DndActionToDragOperation(dnd_action); } void DataDevice::OnClipboardDataChanged() { @@ -140,8 +175,12 @@ void DataDevice::OnSurfaceFocusing(Surface* surface) { void DataDevice::OnSurfaceFocused(Surface* surface) {} void DataDevice::OnDataOfferDestroying(DataOffer* data_offer) { - if (data_offer_ && data_offer_->get() == data_offer) + if (data_offer_ && data_offer_->get() == data_offer) { + drop_succeeded_ = data_offer_->get()->finished(); + if (quit_closure_) + std::move(quit_closure_).Run(); data_offer_.reset(); + } } void DataDevice::OnSurfaceDestroying(Surface* surface) { diff --git a/chromium/components/exo/data_device.h b/chromium/components/exo/data_device.h index 9f110ef6dfb..8b0bb19b371 100644 --- a/chromium/components/exo/data_device.h +++ b/chromium/components/exo/data_device.h @@ -48,17 +48,15 @@ class DataDevice : public WMHelper::DragDropObserver, // be null if the data will be transferred only in the client. |origin| is // the surface which starts the drag and drop operation. |icon| is the // nullable image which is rendered at the next to cursor while drag - // operation. |serial| is the unique number comes from input events which - // triggers the drag and drop operation. + // operation. void StartDrag(DataSource* source, Surface* origin, Surface* icon, ui::DragDropTypes::DragEventSource event_source); // Sets selection data to the clipboard. - // |source| represents data comes from the client. |serial| is the unique - // number comes from input events which triggers the drag and drop operation. - void SetSelection(DataSource* source, uint32_t serial); + // |source| represents data comes from the client. + void SetSelection(DataSource* source); // Overridden from WMHelper::DragDropObserver: void OnDragEntered(const ui::DropTargetEvent& event) override; @@ -91,6 +89,9 @@ class DataDevice : public WMHelper::DragDropObserver, std::unique_ptr<ScopedDataOffer> data_offer_; std::unique_ptr<ScopedSurface> focused_surface_; + base::OnceClosure quit_closure_; + bool drop_succeeded_; + DISALLOW_COPY_AND_ASSIGN(DataDevice); }; diff --git a/chromium/components/exo/data_device_unittest.cc b/chromium/components/exo/data_device_unittest.cc index 8315408b871..189a6d9f6de 100644 --- a/chromium/components/exo/data_device_unittest.cc +++ b/chromium/components/exo/data_device_unittest.cc @@ -61,7 +61,11 @@ class TestDataDeviceDelegate : public DataDeviceDelegate { return out->size(); } Surface* entered_surface() const { return entered_surface_; } - void DeleteDataOffer() { data_offer_.reset(); } + void DeleteDataOffer(bool finished) { + if (finished) + data_offer_->Finish(); + data_offer_.reset(); + } void set_can_accept_data_events_for_surface(bool value) { can_accept_data_events_for_surface_ = value; } @@ -186,7 +190,12 @@ TEST_F(DataDeviceTest, DataEventsDrop) { ASSERT_EQ(1u, delegate_.PopEvents(&events)); EXPECT_EQ(DataEvent::kMotion, events[0]); - device_->OnPerformDrop(event); + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&TestDataDeviceDelegate::DeleteDataOffer, + base::Unretained(&delegate_), true)); + + int result = device_->OnPerformDrop(event); + EXPECT_EQ(ui::DragDropTypes::DRAG_LINK, result); ASSERT_EQ(1u, delegate_.PopEvents(&events)); EXPECT_EQ(DataEvent::kDrop, events[0]); } @@ -222,7 +231,7 @@ TEST_F(DataDeviceTest, DeleteDataOfferDuringDrag) { EXPECT_EQ(DataEvent::kOffer, events[0]); EXPECT_EQ(DataEvent::kEnter, events[1]); - delegate_.DeleteDataOffer(); + delegate_.DeleteDataOffer(false); EXPECT_EQ(ui::DragDropTypes::DRAG_NONE, device_->OnDragUpdated(event)); EXPECT_EQ(0u, delegate_.PopEvents(&events)); @@ -231,6 +240,31 @@ TEST_F(DataDeviceTest, DeleteDataOfferDuringDrag) { EXPECT_EQ(0u, delegate_.PopEvents(&events)); } +TEST_F(DataDeviceTest, DataOfferNotFinished) { + ui::DropTargetEvent event(data_, gfx::PointF(), gfx::PointF(), + ui::DragDropTypes::DRAG_MOVE); + ui::Event::DispatcherApi(&event).set_target(surface_->window()); + + std::vector<DataEvent> events; + device_->OnDragEntered(event); + ASSERT_EQ(2u, delegate_.PopEvents(&events)); + EXPECT_EQ(DataEvent::kOffer, events[0]); + EXPECT_EQ(DataEvent::kEnter, events[1]); + + EXPECT_EQ(ui::DragDropTypes::DRAG_LINK, device_->OnDragUpdated(event)); + ASSERT_EQ(1u, delegate_.PopEvents(&events)); + EXPECT_EQ(DataEvent::kMotion, events[0]); + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&TestDataDeviceDelegate::DeleteDataOffer, + base::Unretained(&delegate_), false)); + + int result = device_->OnPerformDrop(event); + EXPECT_EQ(ui::DragDropTypes::DRAG_NONE, result); + ASSERT_EQ(1u, delegate_.PopEvents(&events)); + EXPECT_EQ(DataEvent::kDrop, events[0]); +} + TEST_F(DataDeviceTest, NotAcceptDataEventsForSurface) { ui::DropTargetEvent event(data_, gfx::PointF(), gfx::PointF(), ui::DragDropTypes::DRAG_MOVE); diff --git a/chromium/components/exo/data_offer.cc b/chromium/components/exo/data_offer.cc index eee7b8cbf2d..8ec119a950b 100644 --- a/chromium/components/exo/data_offer.cc +++ b/chromium/components/exo/data_offer.cc @@ -14,6 +14,7 @@ #include "base/strings/utf_string_conversions.h" #include "base/task/post_task.h" #include "base/task/thread_pool.h" +#include "components/exo/data_device.h" #include "components/exo/data_offer_delegate.h" #include "components/exo/data_offer_observer.h" #include "components/exo/file_helper.h" @@ -181,7 +182,10 @@ ScopedDataOffer::~ScopedDataOffer() { } DataOffer::DataOffer(DataOfferDelegate* delegate, Purpose purpose) - : delegate_(delegate), purpose_(purpose) {} + : delegate_(delegate), + dnd_action_(DndAction::kNone), + purpose_(purpose), + finished_(false) {} DataOffer::~DataOffer() { delegate_->OnDataOfferDestroying(this); @@ -226,7 +230,9 @@ void DataOffer::Receive(const std::string& mime_type, base::ScopedFD fd) { } } -void DataOffer::Finish() {} +void DataOffer::Finish() { + finished_ = true; +} void DataOffer::SetActions(const base::flat_set<DndAction>& dnd_actions, DndAction preferred_action) { diff --git a/chromium/components/exo/data_offer.h b/chromium/components/exo/data_offer.h index 1086d53158c..3c540f35583 100644 --- a/chromium/components/exo/data_offer.h +++ b/chromium/components/exo/data_offer.h @@ -8,6 +8,7 @@ #include <cstdint> #include <string> +#include "base/callback_forward.h" #include "base/containers/flat_map.h" #include "base/containers/flat_set.h" #include "base/files/scoped_file.h" @@ -79,7 +80,8 @@ class DataOffer final : public ui::PropertyHandler { // DataOffer object. void SetSourceActions(const base::flat_set<DndAction>& source_actions); - DndAction dnd_action() { return dnd_action_; } + DndAction dnd_action() const { return dnd_action_; } + bool finished() const { return finished_; } private: void OnPickledUrlsResolved(const std::string& uri_list_mime_type, @@ -104,6 +106,7 @@ class DataOffer final : public ui::PropertyHandler { DndAction dnd_action_; base::ObserverList<DataOfferObserver>::Unchecked observers_; Purpose purpose_; + bool finished_; base::WeakPtrFactory<DataOffer> weak_ptr_factory_{this}; diff --git a/chromium/components/exo/drag_drop_operation.cc b/chromium/components/exo/drag_drop_operation.cc index 1fee5aa1595..e7ad6b32e7d 100644 --- a/chromium/components/exo/drag_drop_operation.cc +++ b/chromium/components/exo/drag_drop_operation.cc @@ -78,8 +78,10 @@ base::WeakPtr<DragDropOperation> DragDropOperation::Create( DataSource* source, Surface* origin, Surface* icon, + const gfx::Point& drag_start_point, ui::DragDropTypes::DragEventSource event_source) { - auto* dnd_op = new DragDropOperation(source, origin, icon, event_source); + auto* dnd_op = new DragDropOperation(source, origin, icon, drag_start_point, + event_source); return dnd_op->weak_ptr_factory_.GetWeakPtr(); } @@ -87,11 +89,12 @@ DragDropOperation::DragDropOperation( DataSource* source, Surface* origin, Surface* icon, + const gfx::Point& drag_start_point, ui::DragDropTypes::DragEventSource event_source) : SurfaceTreeHost("ExoDragDropOperation"), source_(std::make_unique<ScopedDataSource>(source, this)), origin_(std::make_unique<ScopedSurface>(origin, this)), - drag_start_point_(display::Screen::GetScreen()->GetCursorScreenPoint()), + drag_start_point_(drag_start_point), os_exchange_data_(std::make_unique<ui::OSExchangeData>()), event_source_(event_source), weak_ptr_factory_(this) { @@ -185,6 +188,7 @@ void DragDropOperation::CaptureDragIcon() { viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP, base::BindOnce(&DragDropOperation::OnDragIconCaptured, weak_ptr_factory_.GetWeakPtr())); + request->set_result_task_runner(base::SequencedTaskRunnerHandle::Get()); host_window()->layer()->RequestCopyOfOutput(std::move(request)); } @@ -235,6 +239,8 @@ void DragDropOperation::StartDragDropOperation() { uint32_t dnd_operations = DndActionsToDragOperations(source_->get()->GetActions()); + base::WeakPtr<DragDropOperation> weak_ptr = weak_ptr_factory_.GetWeakPtr(); + started_by_this_object_ = true; // This triggers a nested run loop that terminates when the drag and drop // operation is completed. @@ -243,6 +249,10 @@ void DragDropOperation::StartDragDropOperation() { origin_->get()->window(), drag_start_point_, dnd_operations, event_source_); + // The instance deleted during StartDragAndDrop's nested RunLoop. + if (!weak_ptr) + return; + if (op) { // Success diff --git a/chromium/components/exo/drag_drop_operation.h b/chromium/components/exo/drag_drop_operation.h index ae02f4494bb..823258c8198 100644 --- a/chromium/components/exo/drag_drop_operation.h +++ b/chromium/components/exo/drag_drop_operation.h @@ -50,6 +50,7 @@ class DragDropOperation : public DataSourceObserver, DataSource* source, Surface* origin, Surface* icon, + const gfx::Point& drag_start_point, ui::DragDropTypes::DragEventSource event_source); // Abort the operation if it hasn't been started yet, otherwise do nothing. @@ -77,6 +78,7 @@ class DragDropOperation : public DataSourceObserver, DragDropOperation(DataSource* source, Surface* origin, Surface* icon, + const gfx::Point& drag_start_point, ui::DragDropTypes::DragEventSource event_source); ~DragDropOperation() override; diff --git a/chromium/components/exo/drag_drop_operation_unittest.cc b/chromium/components/exo/drag_drop_operation_unittest.cc new file mode 100644 index 00000000000..9e02d45146c --- /dev/null +++ b/chromium/components/exo/drag_drop_operation_unittest.cc @@ -0,0 +1,127 @@ +// 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 "components/exo/drag_drop_operation.h" + +#include <memory> + +#include "ash/shell.h" +#include "base/bind.h" +#include "base/files/file_util.h" +#include "base/run_loop.h" +#include "components/exo/buffer.h" +#include "components/exo/data_source.h" +#include "components/exo/data_source_delegate.h" +#include "components/exo/surface.h" +#include "components/exo/test/exo_test_base.h" +#include "ui/aura/client/drag_drop_client.h" + +namespace exo { +namespace { + +constexpr char kText[] = "test"; +constexpr char kTextMimeType[] = "text/plain"; + +} // namespace + +class TestDataSourceDelegate : public DataSourceDelegate { + public: + // DataSourceDelegate: + void OnDataSourceDestroying(DataSource* source) override {} + + void OnTarget(const base::Optional<std::string>& mime_type) override {} + + void OnSend(const std::string& mime_type, base::ScopedFD fd) override { + base::WriteFileDescriptor(fd.get(), kText, sizeof(kText)); + } + + void OnCancelled() override {} + + void OnDndDropPerformed() override {} + + void OnDndFinished() override {} + + void OnAction(DndAction dnd_action) override {} + + bool CanAcceptDataEventsForSurface(Surface* surface) const override { + return true; + } +}; + +class DragDropOperationTest : public test::ExoTestBase, + public aura::client::DragDropClientObserver { + public: + DragDropOperationTest() = default; + ~DragDropOperationTest() override = default; + DragDropOperationTest(const DragDropOperationTest&) = delete; + DragDropOperationTest& operator=(const DragDropOperationTest&) = delete; + + void SetUp() override { + test::ExoTestBase::SetUp(); + aura::client::GetDragDropClient(ash::Shell::GetPrimaryRootWindow()) + ->AddObserver(this); + } + + void TearDown() override { + aura::client::GetDragDropClient(ash::Shell::GetPrimaryRootWindow()) + ->RemoveObserver(this); + test::ExoTestBase::TearDown(); + } + + // aura::client::DragDropClientObserver: + void OnDragStarted() override { + base::SequencedTaskRunnerHandle::Get()->PostTask( + FROM_HERE, std::move(drag_blocked_callback_)); + } + + void OnDragEnded() override {} + + protected: + void set_drag_blocked_callback(base::OnceClosure callback) { + drag_blocked_callback_ = std::move(callback); + } + + private: + // Callback running inside the nested RunLoop in StartDragAndDrop(). + base::OnceClosure drag_blocked_callback_; +}; + +TEST_F(DragDropOperationTest, DeleteDuringDragging) { + auto delegate = std::make_unique<TestDataSourceDelegate>(); + auto data_source = std::make_unique<DataSource>(delegate.get()); + data_source->Offer(kTextMimeType); + + auto origin_surface = std::make_unique<Surface>(); + ash::Shell::GetPrimaryRootWindow()->AddChild(origin_surface->window()); + + gfx::Size buffer_size(100, 100); + std::unique_ptr<Buffer> buffer( + new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size))); + auto icon_surface = std::make_unique<Surface>(); + icon_surface->Attach(buffer.get()); + + auto operation = DragDropOperation::Create( + data_source.get(), origin_surface.get(), icon_surface.get(), gfx::Point(), + ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE); + icon_surface->Commit(); + + base::RunLoop run_loop; + set_drag_blocked_callback(base::BindOnce( + [](std::unique_ptr<DataSource> data_source, + base::WeakPtr<DragDropOperation> operation, + base::OnceClosure quit_closure) { + // This function runs inside the nested RunLoop in + // ash::DragDropController::StartDragAndDrop(). + EXPECT_TRUE(operation); + // Deleting DataSource causes DragDropOperation to be deleted as well. + data_source.reset(); + EXPECT_FALSE(operation); + std::move(quit_closure).Run(); + }, + std::move(data_source), operation, run_loop.QuitClosure())); + run_loop.Run(); + EXPECT_FALSE(operation); +} + +} // namespace exo diff --git a/chromium/components/exo/fullscreen_shell_surface.cc b/chromium/components/exo/fullscreen_shell_surface.cc index b2bc0b7fbc8..ef253ea127b 100644 --- a/chromium/components/exo/fullscreen_shell_surface.cc +++ b/chromium/components/exo/fullscreen_shell_surface.cc @@ -15,14 +15,40 @@ #include "ui/aura/window_targeter.h" #include "ui/compositor/compositor.h" #include "ui/compositor/dip_util.h" +#include "ui/views/view.h" #include "ui/views/widget/widget.h" #include "ui/wm/core/window_util.h" namespace exo { +class FullscreenShellSurface::FullscreenShellView : public views::View { + public: + FullscreenShellView() = default; + FullscreenShellView(const FullscreenShellView&) = delete; + FullscreenShellView& operator=(const FullscreenShellView&) = delete; + ~FullscreenShellView() override = default; + + // views::View: + void GetAccessibleNodeData(ui::AXNodeData* node_data) override { + node_data->role = ax::mojom::Role::kClient; + + if (child_ax_tree_id_ == ui::AXTreeIDUnknown()) + return; + + node_data->AddStringAttribute(ax::mojom::StringAttribute::kChildTreeId, + child_ax_tree_id_.ToString()); + } + + void SetChildAxTreeId(ui::AXTreeID child_ax_tree_id) { + child_ax_tree_id_ = child_ax_tree_id; + } + + private: + ui::AXTreeID child_ax_tree_id_ = ui::AXTreeIDUnknown(); +}; + FullscreenShellSurface::FullscreenShellSurface() : SurfaceTreeHost("FullscreenShellSurfaceHost") { - set_owned_by_client(); CreateFullscreenShellSurfaceWidget(ui::SHOW_STATE_FULLSCREEN); widget_->SetFullscreen(true); } @@ -69,7 +95,6 @@ void FullscreenShellSurface::SetSurface(Surface* surface) { if (root_surface()) root_surface()->RemoveSurfaceObserver(this); SetRootSurface(surface); - set_owned_by_client(); SetShellMainSurface(widget_->GetNativeWindow(), root_surface()); if (surface) { surface->AddSurfaceObserver(this); @@ -167,7 +192,8 @@ bool FullscreenShellSurface::ShouldShowWindowTitle() const { } void FullscreenShellSurface::WindowClosing() { - SetEnabled(false); + contents_view_->SetEnabled(false); + contents_view_ = nullptr; widget_ = nullptr; } @@ -180,7 +206,9 @@ const views::Widget* FullscreenShellSurface::GetWidget() const { } views::View* FullscreenShellSurface::GetContentsView() { - return this; + if (!contents_view_) + contents_view_ = new FullscreenShellView(); + return contents_view_; } bool FullscreenShellSurface::WidgetHasHitTestMask() const { @@ -210,18 +238,19 @@ void FullscreenShellSurface::OnWindowDestroying(aura::Window* window) { window->RemoveObserver(this); } -void FullscreenShellSurface::GetAccessibleNodeData(ui::AXNodeData* node_data) { - node_data->role = ax::mojom::Role::kClient; - - if (child_ax_tree_id_ == ui::AXTreeIDUnknown()) - return; +void FullscreenShellSurface::SetChildAxTreeId(ui::AXTreeID child_ax_tree_id) { + DCHECK(contents_view_); + contents_view_->SetChildAxTreeId(child_ax_tree_id); +} - node_data->AddStringAttribute(ax::mojom::StringAttribute::kChildTreeId, - child_ax_tree_id_.ToString()); +void FullscreenShellSurface::SetEnabled(bool enabled) { + DCHECK(contents_view_); + contents_view_->SetEnabled(enabled); } -void FullscreenShellSurface::SetChildAxTreeId(ui::AXTreeID child_ax_tree_id) { - child_ax_tree_id_ = child_ax_tree_id; +void FullscreenShellSurface::GetAccessibleNodeData(ui::AXNodeData* node_data) { + DCHECK(contents_view_); + contents_view_->GetAccessibleNodeData(node_data); } void FullscreenShellSurface::UpdateHostWindowBounds() { @@ -237,7 +266,6 @@ void FullscreenShellSurface::UpdateHostWindowBounds() { void FullscreenShellSurface::CreateFullscreenShellSurfaceWidget( ui::WindowShowState show_state) { - DCHECK(GetEnabled()); DCHECK(!widget_); views::Widget::InitParams params; @@ -278,8 +306,12 @@ void FullscreenShellSurface::CommitWidget() { } bool FullscreenShellSurface::OnPreWidgetCommit() { - if (!widget_ && GetEnabled() && host_window()->bounds().IsEmpty()) + // If we have a |widget_|, then we must have a |contents_view_| as both are + // created together. + if (!widget_ && contents_view_->GetEnabled() && + host_window()->bounds().IsEmpty()) { return false; + } return true; } diff --git a/chromium/components/exo/fullscreen_shell_surface.h b/chromium/components/exo/fullscreen_shell_surface.h index c867a791cc4..15bedf9c384 100644 --- a/chromium/components/exo/fullscreen_shell_surface.h +++ b/chromium/components/exo/fullscreen_shell_surface.h @@ -20,8 +20,7 @@ class Surface; class FullscreenShellSurface : public SurfaceTreeHost, public SurfaceObserver, public aura::WindowObserver, - public views::WidgetDelegate, - public views::View { + public views::WidgetDelegate { public: FullscreenShellSurface(); ~FullscreenShellSurface() override; @@ -78,12 +77,12 @@ class FullscreenShellSurface : public SurfaceTreeHost, // aura::WindowObserver: void OnWindowDestroying(aura::Window* window) override; - // ui::View: - void GetAccessibleNodeData(ui::AXNodeData* node_data) override; - void SetChildAxTreeId(ui::AXTreeID child_ax_tree_id); + void SetEnabled(bool enabled); + void GetAccessibleNodeData(ui::AXNodeData* node_data); private: + class FullscreenShellView; // Keep the bounds in sync with the root surface bounds. void UpdateHostWindowBounds() override; @@ -97,7 +96,7 @@ class FullscreenShellSurface : public SurfaceTreeHost, base::Optional<std::string> startup_id_; base::RepeatingClosure close_callback_; base::OnceClosure surface_destroyed_callback_; - ui::AXTreeID child_ax_tree_id_ = ui::AXTreeIDUnknown(); + FullscreenShellView* contents_view_ = nullptr; DISALLOW_COPY_AND_ASSIGN(FullscreenShellSurface); }; diff --git a/chromium/components/exo/gamepad_delegate.h b/chromium/components/exo/gamepad_delegate.h index 8ca47f8c625..bb698612c6f 100644 --- a/chromium/components/exo/gamepad_delegate.h +++ b/chromium/components/exo/gamepad_delegate.h @@ -5,6 +5,8 @@ #ifndef COMPONENTS_EXO_GAMEPAD_DELEGATE_H_ #define COMPONENTS_EXO_GAMEPAD_DELEGATE_H_ +#include "base/time/time.h" + namespace exo { // Handles events for a specific gamepad. @@ -14,14 +16,16 @@ class GamepadDelegate { virtual void OnRemoved() = 0; // Called when the user moved an axis of the gamepad. - virtual void OnAxis(int axis, double value) = 0; + virtual void OnAxis(int axis, double value, base::TimeTicks timestamp) = 0; // Called when the user pressed or moved a button of the gamepad. - virtual void OnButton(int button, bool pressed) = 0; + virtual void OnButton(int button, + bool pressed, + base::TimeTicks timestamp) = 0; // Called after all gamepad information of this frame has been set and the // client should evaluate the updated state. - virtual void OnFrame() = 0; + virtual void OnFrame(base::TimeTicks timestamp) = 0; protected: virtual ~GamepadDelegate() {} diff --git a/chromium/components/exo/gaming_seat.cc b/chromium/components/exo/gaming_seat.cc index c578348c3d1..f63b2fac4dc 100644 --- a/chromium/components/exo/gaming_seat.cc +++ b/chromium/components/exo/gaming_seat.cc @@ -4,6 +4,8 @@ #include "components/exo/gaming_seat.h" +#include <vector> + #include "components/exo/gamepad_delegate.h" #include "components/exo/gaming_seat_delegate.h" #include "components/exo/shell_surface_util.h" @@ -97,13 +99,13 @@ void GamingSeat::OnGamepadEvent(const ui::GamepadEvent& event) { switch (event.type()) { case ui::GamepadEventType::BUTTON: - it->second->OnButton(event.code(), event.value()); + it->second->OnButton(event.code(), event.value(), event.timestamp()); break; case ui::GamepadEventType::AXIS: - it->second->OnAxis(event.code(), event.value()); + it->second->OnAxis(event.code(), event.value(), event.timestamp()); break; case ui::GamepadEventType::FRAME: - it->second->OnFrame(); + it->second->OnFrame(event.timestamp()); break; } } diff --git a/chromium/components/exo/gaming_seat_unittest.cc b/chromium/components/exo/gaming_seat_unittest.cc index 6a04350f0de..4d763b60c18 100644 --- a/chromium/components/exo/gaming_seat_unittest.cc +++ b/chromium/components/exo/gaming_seat_unittest.cc @@ -3,9 +3,13 @@ // found in the LICENSE file. #include "components/exo/gaming_seat.h" + +#include <vector> + #include "ash/shell.h" #include "base/command_line.h" #include "base/run_loop.h" +#include "base/time/time.h" #include "components/exo/buffer.h" #include "components/exo/gamepad_delegate.h" #include "components/exo/gaming_seat_delegate.h" @@ -37,9 +41,9 @@ class MockGamepadDelegate : public GamepadDelegate { // Overridden from GamepadDelegate: MOCK_METHOD0(OnRemoved, void()); - MOCK_METHOD2(OnAxis, void(int, double)); - MOCK_METHOD2(OnButton, void(int, bool)); - MOCK_METHOD0(OnFrame, void()); + MOCK_METHOD3(OnAxis, void(int, double, base::TimeTicks)); + MOCK_METHOD3(OnButton, void(int, bool, base::TimeTicks)); + MOCK_METHOD1(OnFrame, void(base::TimeTicks)); }; class GamingSeatTest : public test::ExoTestBase { @@ -73,6 +77,15 @@ class GamingSeatTest : public test::ExoTestBase { } } + void SendButtonToGamepads(const std::vector<int>& gamepad_device_ids, + base::TimeTicks timestamp) { + for (auto& id : gamepad_device_ids) { + ui::GamepadEvent event(id, ui::GamepadEventType::BUTTON, 310, 1, + timestamp); + ui::GamepadProviderOzone::GetInstance()->DispatchGamepadEvent(event); + } + } + protected: std::unique_ptr<GamingSeat> gaming_seat_; @@ -104,19 +117,19 @@ TEST_F(GamingSeatTest, ConnectionChange) { .WillOnce(testing::Return(&gamepad_delegate[0])) .WillOnce(testing::Return(&gamepad_delegate[1])); // Send frame to connected gamepad. - EXPECT_CALL(gamepad_delegate[0], OnFrame()).Times(1); - EXPECT_CALL(gamepad_delegate[1], OnFrame()).Times(1); + EXPECT_CALL(gamepad_delegate[0], OnFrame(testing::_)).Times(1); + EXPECT_CALL(gamepad_delegate[1], OnFrame(testing::_)).Times(1); // Connect 3 more. EXPECT_CALL(*gaming_seat_delegate, GamepadAdded(testing::_)) .WillOnce(testing::Return(&gamepad_delegate[2])) .WillOnce(testing::Return(&gamepad_delegate[3])) .WillOnce(testing::Return(&gamepad_delegate[4])); // Send frame to all gamepads. - EXPECT_CALL(gamepad_delegate[0], OnFrame()).Times(1); - EXPECT_CALL(gamepad_delegate[1], OnFrame()).Times(1); - EXPECT_CALL(gamepad_delegate[2], OnFrame()).Times(1); - EXPECT_CALL(gamepad_delegate[3], OnFrame()).Times(1); - EXPECT_CALL(gamepad_delegate[4], OnFrame()).Times(1); + EXPECT_CALL(gamepad_delegate[0], OnFrame(testing::_)).Times(1); + EXPECT_CALL(gamepad_delegate[1], OnFrame(testing::_)).Times(1); + EXPECT_CALL(gamepad_delegate[2], OnFrame(testing::_)).Times(1); + EXPECT_CALL(gamepad_delegate[3], OnFrame(testing::_)).Times(1); + EXPECT_CALL(gamepad_delegate[4], OnFrame(testing::_)).Times(1); // Disconnect gamepad 0 and gamepad 2 and connect a new gamepad. EXPECT_CALL(gamepad_delegate[0], OnRemoved()).Times(1); EXPECT_CALL(gamepad_delegate[2], OnRemoved()).Times(1); @@ -124,9 +137,9 @@ TEST_F(GamingSeatTest, ConnectionChange) { EXPECT_CALL(*gaming_seat_delegate, GamepadAdded(testing::_)) .WillOnce(testing::Return(&gamepad_delegate[5])); // Send frame to all gamepads. - EXPECT_CALL(gamepad_delegate[1], OnFrame()).Times(1); - EXPECT_CALL(gamepad_delegate[3], OnFrame()).Times(1); - EXPECT_CALL(gamepad_delegate[5], OnFrame()).Times(1); + EXPECT_CALL(gamepad_delegate[1], OnFrame(testing::_)).Times(1); + EXPECT_CALL(gamepad_delegate[3], OnFrame(testing::_)).Times(1); + EXPECT_CALL(gamepad_delegate[5], OnFrame(testing::_)).Times(1); // disconnect other gamepads EXPECT_CALL(gamepad_delegate[1], OnRemoved()).Times(1); @@ -140,6 +153,47 @@ TEST_F(GamingSeatTest, ConnectionChange) { SendFrameToGamepads({0, 1, 2, 3, 4}); UpdateGamepadDevice({1, 3, 5}); SendFrameToGamepads({1, 2, 3, 4, 5}); + UpdateGamepadDevice({}); + DestroyGamingSeat(gaming_seat_delegate); +} + +TEST_F(GamingSeatTest, Timestamp) { + std::unique_ptr<Surface> surface(new Surface); + std::unique_ptr<ShellSurface> shell_surface(new ShellSurface(surface.get())); + gfx::Size buffer_size(10, 10); + std::unique_ptr<Buffer> buffer( + new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size))); + surface->Attach(buffer.get()); + surface->Commit(); + + testing::StrictMock<MockGamingSeatDelegate>* gaming_seat_delegate = + new testing::StrictMock<MockGamingSeatDelegate>(); + EXPECT_CALL(*gaming_seat_delegate, + CanAcceptGamepadEventsForSurface(testing::_)) + .WillOnce(testing::Return(true)); + + InitializeGamingSeat(gaming_seat_delegate); + testing::StrictMock<MockGamepadDelegate> gamepad_delegate; + + base::TimeTicks expected_time = base::TimeTicks::Now(); + + { // Test sequence + testing::InSequence s; + + // Connect gamepad. + EXPECT_CALL(*gaming_seat_delegate, GamepadAdded(testing::_)) + .WillOnce(testing::Return(&gamepad_delegate)); + // Send button to connected gamepad. Expect correct timestamp. + EXPECT_CALL(gamepad_delegate, + OnButton(testing::_, testing::_, testing::Eq(expected_time))) + .Times(1); + // Disconnect gamepad. + EXPECT_CALL(gamepad_delegate, OnRemoved()).Times(1); + } + // Gamepad connected. + UpdateGamepadDevice({1}); + SendButtonToGamepads({1}, expected_time); + UpdateGamepadDevice({}); DestroyGamingSeat(gaming_seat_delegate); } diff --git a/chromium/components/exo/keyboard.cc b/chromium/components/exo/keyboard.cc index 64aa296e8a9..554e57e3400 100644 --- a/chromium/components/exo/keyboard.cc +++ b/chromium/components/exo/keyboard.cc @@ -4,12 +4,10 @@ #include "components/exo/keyboard.h" -#include "ash/accessibility/accessibility_controller_impl.h" #include "ash/keyboard/ui/keyboard_ui_controller.h" #include "ash/keyboard/ui/keyboard_util.h" #include "ash/public/cpp/app_types.h" #include "ash/public/cpp/keyboard/keyboard_controller.h" -#include "ash/shell.h" #include "base/bind.h" #include "base/threading/thread_task_runner_handle.h" #include "components/exo/input_trace.h" @@ -224,12 +222,7 @@ bool Keyboard::AreKeyboardKeyAcksNeeded() const { // While the spoken feedback is enabled, a key event is sent to both of a // wayland client and Chrome to give a chance to work to Chrome OS's // shortcuts. - return are_keyboard_key_acks_needed_ - // TODO(yhanada): Remove this once ARC++ can send ack with a serial - // correctly while ChromeVox is on. - && !ash::Shell::Get() - ->accessibility_controller() - ->spoken_feedback_enabled(); + return are_keyboard_key_acks_needed_; } void Keyboard::AckKeyboardKey(uint32_t serial, bool handled) { @@ -279,7 +272,12 @@ void Keyboard::OnKeyEvent(ui::KeyEvent* event) { // When IME ate a key event, we use the event only for tracking key states and // ignore for further processing. Otherwise it is handled in two places (IME // and client) and causes undesired behavior. - bool consumed_by_ime = ConsumedByIme(focus_, event); + // If the window should receive a key event before IME, Exo should send any + // key events to a client. The client will send back the events to IME if + // needed. + const bool consumed_by_ime = + !focus_->window()->GetProperty(aura::client::kSkipImeProcessing) && + ConsumedByIme(focus_, event); // Always update modifiers. int modifier_flags = event->flags() & kModifierMask; diff --git a/chromium/components/exo/keyboard_unittest.cc b/chromium/components/exo/keyboard_unittest.cc index 83e176ff0d4..50e037c21c8 100644 --- a/chromium/components/exo/keyboard_unittest.cc +++ b/chromium/components/exo/keyboard_unittest.cc @@ -23,7 +23,9 @@ #include "components/exo/test/exo_test_base.h" #include "components/exo/test/exo_test_helper.h" #include "testing/gmock/include/gmock/gmock.h" +#include "ui/aura/client/aura_constants.h" #include "ui/aura/client/focus_client.h" +#include "ui/base/ime/dummy_text_input_client.h" #include "ui/events/devices/device_data_manager.h" #include "ui/events/event_constants.h" #include "ui/events/keycodes/dom/dom_code.h" @@ -50,6 +52,7 @@ class MockKeyboardDelegate : public KeyboardDelegate { MOCK_METHOD3(OnKeyRepeatSettingsChanged, void(bool, base::TimeDelta, base::TimeDelta)); }; +using NiceMockKeyboardDelegate = ::testing::NiceMock<MockKeyboardDelegate>; class MockKeyboardDeviceConfigurationDelegate : public KeyboardDeviceConfigurationDelegate { @@ -80,7 +83,7 @@ class TestShellSurface : public ShellSurface { // key events. https://crbug.com/1008574. TEST_F(KeyboardTest, CorrectSeatPressedKeysOnSwitchingDesks) { Seat seat; - MockKeyboardDelegate delegate; + NiceMockKeyboardDelegate delegate; auto keyboard = std::make_unique<Keyboard>(&delegate, &seat); // Create 2 desks. @@ -155,7 +158,7 @@ TEST_F(KeyboardTest, OnKeyboardEnter) { focus_client->FocusWindow(surface->window()); // Keyboard should try to set initial focus to surface. - MockKeyboardDelegate delegate; + NiceMockKeyboardDelegate delegate; EXPECT_CALL(delegate, CanAcceptKeyboardEventsForSurface(surface.get())) .WillOnce(testing::Return(false)); auto keyboard = std::make_unique<Keyboard>(&delegate, &seat); @@ -201,7 +204,7 @@ TEST_F(KeyboardTest, OnKeyboardLeave) { aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow()); focus_client->FocusWindow(nullptr); - MockKeyboardDelegate delegate; + NiceMockKeyboardDelegate delegate; Seat seat; auto keyboard = std::make_unique<Keyboard>(&delegate, &seat); @@ -242,7 +245,7 @@ TEST_F(KeyboardTest, OnKeyboardKey) { aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow()); focus_client->FocusWindow(nullptr); - MockKeyboardDelegate delegate; + NiceMockKeyboardDelegate delegate; Seat seat; auto keyboard = std::make_unique<Keyboard>(&delegate, &seat); @@ -328,6 +331,77 @@ TEST_F(KeyboardTest, OnKeyboardKey) { keyboard.reset(); } +TEST_F(KeyboardTest, OnKeyboardKey_NotSendKeyIfConsumedByIme) { + std::unique_ptr<Surface> surface(new Surface); + std::unique_ptr<ShellSurface> shell_surface(new ShellSurface(surface.get())); + gfx::Size buffer_size(10, 10); + std::unique_ptr<Buffer> buffer( + new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size))); + surface->Attach(buffer.get()); + surface->Commit(); + + aura::client::FocusClient* focus_client = + aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow()); + focus_client->FocusWindow(nullptr); + + NiceMockKeyboardDelegate delegate; + Seat seat; + auto keyboard = std::make_unique<Keyboard>(&delegate, &seat); + + EXPECT_CALL(delegate, CanAcceptKeyboardEventsForSurface(surface.get())) + .WillOnce(testing::Return(true)); + EXPECT_CALL(delegate, OnKeyboardModifiers(0)); + EXPECT_CALL(delegate, + OnKeyboardEnter(surface.get(), + base::flat_map<ui::DomCode, ui::DomCode>())); + focus_client->FocusWindow(surface->window()); + + ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow()); + views::Widget* widget = + views::Widget::GetTopLevelWidgetForNativeView(surface->window()); + ui::InputMethod* input_method = widget->GetInputMethod(); + ui::DummyTextInputClient client{ui::TEXT_INPUT_TYPE_TEXT}; + input_method->SetFocusedTextInputClient(&client); + + // If a text field is focused, a pressed key event is not sent to a client + // because a key event should be consumed by the IME. + EXPECT_CALL(delegate, OnKeyboardKey(testing::_, ui::DomCode::US_A, true)) + .Times(0); + seat.set_physical_code_for_currently_processing_event_for_testing( + ui::DomCode::US_A); + generator.PressKey(ui::VKEY_A, 0); + // TODO(yhanada): The below EXPECT_CALL fails because exo::Keyboard currently + // sends a key release event for the keys which exo::Keyboard sent a pressed + // event for. It might causes a never-ending key repeat in the client. + // EXPECT_CALL(delegate, OnKeyboardKey(testing::_, ui::DomCode::US_A, false)); + generator.ReleaseKey(ui::VKEY_A, 0); + + // Any key event should be sent to a client if the focused window is marked as + // ImeBlocking. + WMHelper::GetInstance()->SetImeBlocked(surface->window()->GetToplevelWindow(), + true); + EXPECT_CALL(delegate, OnKeyboardKey(testing::_, ui::DomCode::US_B, true)); + seat.set_physical_code_for_currently_processing_event_for_testing( + ui::DomCode::US_B); + generator.PressKey(ui::VKEY_B, 0); + EXPECT_CALL(delegate, OnKeyboardKey(testing::_, ui::DomCode::US_B, false)); + generator.ReleaseKey(ui::VKEY_B, 0); + WMHelper::GetInstance()->SetImeBlocked(surface->window()->GetToplevelWindow(), + false); + + // Any key event should be sent to a client if a key event skips IME. + surface->window()->SetProperty(aura::client::kSkipImeProcessing, true); + EXPECT_CALL(delegate, OnKeyboardKey(testing::_, ui::DomCode::US_C, true)); + seat.set_physical_code_for_currently_processing_event_for_testing( + ui::DomCode::US_C); + generator.PressKey(ui::VKEY_C, 0); + EXPECT_CALL(delegate, OnKeyboardKey(testing::_, ui::DomCode::US_C, false)); + generator.ReleaseKey(ui::VKEY_C, 0); + + input_method->SetFocusedTextInputClient(nullptr); + keyboard.reset(); +} + TEST_F(KeyboardTest, OnKeyboardModifiers) { std::unique_ptr<Surface> surface(new Surface); std::unique_ptr<ShellSurface> shell_surface(new ShellSurface(surface.get())); @@ -341,7 +415,7 @@ TEST_F(KeyboardTest, OnKeyboardModifiers) { aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow()); focus_client->FocusWindow(nullptr); - MockKeyboardDelegate delegate; + NiceMockKeyboardDelegate delegate; Seat seat; auto keyboard = std::make_unique<Keyboard>(&delegate, &seat); @@ -407,7 +481,7 @@ TEST_F(KeyboardTest, OnKeyboardTypeChanged) { ash::Shell::Get()->tablet_mode_controller(); tablet_mode_controller->SetEnabledForTest(true); - MockKeyboardDelegate delegate; + NiceMockKeyboardDelegate delegate; Seat seat; auto keyboard = std::make_unique<Keyboard>(&delegate, &seat); MockKeyboardDeviceConfigurationDelegate configuration_delegate; @@ -452,7 +526,7 @@ TEST_F(KeyboardTest, OnKeyboardTypeChanged_AccessibilityKeyboard) { ui::InputDevice(2, ui::InputDeviceType::INPUT_DEVICE_USB, "keyboard")}; device_data_manager->OnKeyboardDevicesUpdated(keyboards); - MockKeyboardDelegate delegate; + NiceMockKeyboardDelegate delegate; Seat seat; auto keyboard = std::make_unique<Keyboard>(&delegate, &seat); MockKeyboardDeviceConfigurationDelegate configuration_delegate; @@ -586,7 +660,7 @@ TEST_F(KeyboardTest, KeyRepeatSettingsUpdateOnProfileChange) { } TEST_F(KeyboardTest, KeyboardObserver) { - MockKeyboardDelegate delegate; + NiceMockKeyboardDelegate delegate; Seat seat; auto keyboard = std::make_unique<Keyboard>(&delegate, &seat); MockKeyboardObserver observer1; @@ -619,7 +693,7 @@ TEST_F(KeyboardTest, NeedKeyboardKeyAcks) { aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow()); focus_client->FocusWindow(nullptr); - MockKeyboardDelegate delegate; + NiceMockKeyboardDelegate delegate; Seat seat; auto keyboard = std::make_unique<Keyboard>(&delegate, &seat); @@ -645,7 +719,7 @@ TEST_F(KeyboardTest, AckKeyboardKey) { aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow()); focus_client->FocusWindow(nullptr); - MockKeyboardDelegate delegate; + NiceMockKeyboardDelegate delegate; Seat seat; auto keyboard = std::make_unique<Keyboard>(&delegate, &seat); @@ -743,7 +817,7 @@ TEST_F(KeyboardTest, AckKeyboardKeyMoveFocus) { aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow()); focus_client->FocusWindow(nullptr); - MockKeyboardDelegate delegate; + NiceMockKeyboardDelegate delegate; Seat seat; auto keyboard = std::make_unique<Keyboard>(&delegate, &seat); @@ -789,7 +863,7 @@ TEST_F(KeyboardTest, AckKeyboardKeyExpired) { aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow()); focus_client->FocusWindow(nullptr); - MockKeyboardDelegate delegate; + NiceMockKeyboardDelegate delegate; Seat seat; auto keyboard = std::make_unique<Keyboard>(&delegate, &seat); @@ -874,7 +948,7 @@ TEST_F(KeyboardTest, AckKeyboardKeyExpiredWithMovingFocusAccelerator) { aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow()); focus_client->FocusWindow(nullptr); - MockKeyboardDelegate delegate; + NiceMockKeyboardDelegate delegate; Seat seat; auto keyboard = std::make_unique<Keyboard>(&delegate, &seat); @@ -913,60 +987,5 @@ TEST_F(KeyboardTest, AckKeyboardKeyExpiredWithMovingFocusAccelerator) { keyboard.reset(); } - -// A test case for b/130312917. While spoken feedback is enabled, a key event is -// sent to both of a wayland client and Chrome. -TEST_F(KeyboardTest, AckKeyboardKeyWithSpokenFeedback) { - std::unique_ptr<Surface> surface(new Surface); - auto shell_surface = std::make_unique<TestShellSurface>(surface.get()); - gfx::Size buffer_size(10, 10); - std::unique_ptr<Buffer> buffer( - new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size))); - surface->Attach(buffer.get()); - surface->Commit(); - - aura::client::FocusClient* focus_client = - aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow()); - focus_client->FocusWindow(nullptr); - - MockKeyboardDelegate delegate; - Seat seat; - auto keyboard = std::make_unique<Keyboard>(&delegate, &seat); - - EXPECT_CALL(delegate, CanAcceptKeyboardEventsForSurface(surface.get())) - .WillOnce(testing::Return(true)); - EXPECT_CALL(delegate, OnKeyboardModifiers(0)); - EXPECT_CALL(delegate, - OnKeyboardEnter(surface.get(), - base::flat_map<ui::DomCode, ui::DomCode>())); - focus_client->FocusWindow(surface->window()); - - // Enable spoken feedback. - ash::Shell::Get()->accessibility_controller()->SetSpokenFeedbackEnabled( - true, ash::A11Y_NOTIFICATION_NONE); - - ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow()); - // Press KEY_W with Ctrl. - // Key event should be sent to both of AcceleratorPressed and OnKeyboardKey. - EXPECT_CALL(delegate, OnKeyboardModifiers(4)); - EXPECT_CALL(*shell_surface.get(), AcceleratorPressed(ui::Accelerator( - ui::VKEY_W, ui::EF_CONTROL_DOWN, - ui::Accelerator::KeyState::PRESSED))); - EXPECT_CALL(delegate, OnKeyboardKey(testing::_, ui::DomCode::US_W, true)) - .WillOnce(testing::Return(1)); - seat.set_physical_code_for_currently_processing_event_for_testing( - ui::DomCode::US_W); - generator.PressKey(ui::VKEY_W, ui::EF_CONTROL_DOWN); - - // Sending ack for the keypress doesn't cause anything. - keyboard->AckKeyboardKey(1, false /* handled */); - - // Release the key and reset modifier_flags. - EXPECT_CALL(delegate, OnKeyboardModifiers(0)); - EXPECT_CALL(delegate, OnKeyboardKey(testing::_, ui::DomCode::US_W, false)); - generator.ReleaseKey(ui::VKEY_W, 0); - - keyboard.reset(); -} } // namespace } // namespace exo diff --git a/chromium/components/exo/notification_surface_manager.h b/chromium/components/exo/notification_surface_manager.h index f5427b815fc..5840cfce000 100644 --- a/chromium/components/exo/notification_surface_manager.h +++ b/chromium/components/exo/notification_surface_manager.h @@ -13,6 +13,8 @@ class NotificationSurface; class NotificationSurfaceManager { public: + virtual ~NotificationSurfaceManager() = default; + // Gets the NotificationSurface associated with the given notification id. // Returns nullptr if no NotificationSurface is associated with the id. virtual NotificationSurface* GetSurface( @@ -23,9 +25,6 @@ class NotificationSurfaceManager { // Removes a NotificationSurface from the manager. virtual void RemoveSurface(NotificationSurface* surface) = 0; - - protected: - virtual ~NotificationSurfaceManager() {} }; } // namespace exo diff --git a/chromium/components/exo/pointer.cc b/chromium/components/exo/pointer.cc index 9435a608c8a..cc5735f5eb9 100644 --- a/chromium/components/exo/pointer.cc +++ b/chromium/components/exo/pointer.cc @@ -8,6 +8,7 @@ #include "base/bind.h" #include "base/feature_list.h" +#include "base/threading/sequenced_task_runner_handle.h" #include "components/exo/input_trace.h" #include "components/exo/pointer_constraint_delegate.h" #include "components/exo/pointer_delegate.h" @@ -41,11 +42,7 @@ #endif #if defined(USE_OZONE) -#include "ui/ozone/public/cursor_factory_ozone.h" -#endif - -#if defined(USE_X11) -#include "ui/base/cursor/cursor_loader_x11.h" +#include "ui/base/cursor/cursor_factory.h" #endif namespace exo { @@ -343,6 +340,8 @@ void Pointer::OnSurfaceDestroying(Surface* surface) { // ui::EventHandler overrides: void Pointer::OnMouseEvent(ui::MouseEvent* event) { + seat_->SetLastLocation(event->root_location()); + Surface* target = GetEffectiveTargetForEvent(event); gfx::PointF location_in_target = event->location_f(); if (target) { @@ -555,7 +554,8 @@ void Pointer::OnWindowFocused(aura::Window* gained_focus, //////////////////////////////////////////////////////////////////////////////// // Pointer, private: -Surface* Pointer::GetEffectiveTargetForEvent(ui::LocatedEvent* event) const { +Surface* Pointer::GetEffectiveTargetForEvent( + const ui::LocatedEvent* event) const { if (capture_window_) return Surface::AsSurface(capture_window_); @@ -637,6 +637,7 @@ void Pointer::CaptureCursor(const gfx::Point& hotspot) { base::BindOnce(&Pointer::OnCursorCaptured, cursor_capture_weak_ptr_factory_.GetWeakPtr(), hotspot)); + request->set_result_task_runner(base::SequencedTaskRunnerHandle::Get()); request->set_source(cursor_capture_source_id_); host_window()->layer()->RequestCopyOfOutput(std::move(request)); @@ -688,8 +689,8 @@ void Pointer::UpdateCursor() { // TODO(reveman): Add interface for creating cursors from GpuMemoryBuffers // and use that here instead of the current bitmap API. // https://crbug.com/686600 - platform_cursor = ui::CursorFactoryOzone::GetInstance()->CreateImageCursor( - bitmap, hotspot, 0); + platform_cursor = + ui::CursorFactory::GetInstance()->CreateImageCursor(bitmap, hotspot); #elif defined(USE_X11) XcursorImage* image = ui::SkBitmapToXcursorImage(&bitmap, hotspot); platform_cursor = ui::CreateReffedCustomXCursor(image); @@ -698,7 +699,7 @@ void Pointer::UpdateCursor() { cursor_.set_custom_bitmap(bitmap); cursor_.set_custom_hotspot(hotspot); #if defined(USE_OZONE) - ui::CursorFactoryOzone::GetInstance()->UnrefImageCursor(platform_cursor); + ui::CursorFactory::GetInstance()->UnrefImageCursor(platform_cursor); #elif defined(USE_X11) ui::UnrefCustomXCursor(platform_cursor); #endif diff --git a/chromium/components/exo/pointer.h b/chromium/components/exo/pointer.h index bc8f52e1076..d9f0cff3d81 100644 --- a/chromium/components/exo/pointer.h +++ b/chromium/components/exo/pointer.h @@ -115,7 +115,7 @@ class Pointer : public SurfaceTreeHost, void DisablePointerCapture(); // Returns the effective target for |event|. - Surface* GetEffectiveTargetForEvent(ui::LocatedEvent* event) const; + Surface* GetEffectiveTargetForEvent(const ui::LocatedEvent* event) const; // Change pointer focus to |surface|. void SetFocus(Surface* surface, diff --git a/chromium/components/exo/seat.cc b/chromium/components/exo/seat.cc index af1838fcc63..e4915ffdd24 100644 --- a/chromium/components/exo/seat.cc +++ b/chromium/components/exo/seat.cc @@ -83,8 +83,12 @@ void Seat::StartDrag(DataSource* source, Surface* icon, ui::DragDropTypes::DragEventSource event_source) { // DragDropOperation manages its own lifetime. - drag_drop_operation_ = - DragDropOperation::Create(source, origin, icon, event_source); + drag_drop_operation_ = DragDropOperation::Create( + source, origin, icon, last_location_, event_source); +} + +void Seat::SetLastLocation(const gfx::Point& last_location) { + last_location_ = last_location; } void Seat::AbortPendingDragOperation() { diff --git a/chromium/components/exo/seat.h b/chromium/components/exo/seat.h index e114c1eac77..d5730af5df0 100644 --- a/chromium/components/exo/seat.h +++ b/chromium/components/exo/seat.h @@ -72,6 +72,10 @@ class Seat : public aura::client::FocusChangeObserver, Surface* icon, ui::DragDropTypes::DragEventSource event_source); + // Sets the last location in screen coordinates, irrespective of mouse or + // touch. + void SetLastLocation(const gfx::Point& last_location); + // Abort any drag operations that haven't been started yet. void AbortPendingDragOperation(); @@ -159,6 +163,8 @@ class Seat : public aura::client::FocusChangeObserver, // True while Seat is updating clipboard data to selection source. bool changing_clipboard_data_to_selection_source_; + gfx::Point last_location_; + base::WeakPtrFactory<Seat> weak_ptr_factory_{this}; DISALLOW_COPY_AND_ASSIGN(Seat); diff --git a/chromium/components/exo/server/BUILD.gn b/chromium/components/exo/server/BUILD.gn new file mode 100644 index 00000000000..0dc6caef345 --- /dev/null +++ b/chromium/components/exo/server/BUILD.gn @@ -0,0 +1,21 @@ +# Copyright 2017 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. + +assert(is_chromeos) + +source_set("server") { + sources = [ + "wayland_server_controller.cc", + "wayland_server_controller.h", + ] + + deps = [ + "//ash/public/cpp", + "//ash/public/cpp/external_arc", + "//base", + "//components/exo", + "//components/exo/wayland", + "//skia", + ] +} diff --git a/chromium/components/exo/server/OWNERS b/chromium/components/exo/server/OWNERS new file mode 100644 index 00000000000..2285adf99b1 --- /dev/null +++ b/chromium/components/exo/server/OWNERS @@ -0,0 +1 @@ +penghuang@chromium.org diff --git a/chromium/components/exo/server/wayland_server_controller.cc b/chromium/components/exo/server/wayland_server_controller.cc new file mode 100644 index 00000000000..8f2823796be --- /dev/null +++ b/chromium/components/exo/server/wayland_server_controller.cc @@ -0,0 +1,62 @@ +// Copyright 2017 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 "components/exo/server/wayland_server_controller.h" + +#include <memory> + +#include "base/command_line.h" +#include "base/memory/ptr_util.h" +#include "base/message_loop/message_loop_current.h" +#include "components/exo/display.h" +#include "components/exo/file_helper.h" +#include "components/exo/input_method_surface_manager.h" +#include "components/exo/notification_surface_manager.h" +#include "components/exo/wayland/server.h" +#include "components/exo/wayland/wayland_watcher.h" +#include "components/exo/wm_helper.h" +#include "components/exo/wm_helper_chromeos.h" + +namespace exo { + +// static +std::unique_ptr<WaylandServerController> +WaylandServerController::CreateIfNecessary( + std::unique_ptr<FileHelper> file_helper, + std::unique_ptr<NotificationSurfaceManager> notification_surface_manager, + std::unique_ptr<InputMethodSurfaceManager> input_method_surface_manager) { + return std::make_unique<WaylandServerController>( + std::move(file_helper), std::move(notification_surface_manager), + std::move(input_method_surface_manager)); +} + +WaylandServerController::~WaylandServerController() { + // Delete the instance in the reversed order they are created. + wayland_watcher_.reset(); + wayland_server_.reset(); + display_.reset(); + WMHelper::SetInstance(nullptr); + wm_helper_.reset(); +} + +WaylandServerController::WaylandServerController( + std::unique_ptr<FileHelper> file_helper, + std::unique_ptr<NotificationSurfaceManager> notification_surface_manager, + std::unique_ptr<InputMethodSurfaceManager> input_method_surface_manager) + : wm_helper_(std::make_unique<WMHelperChromeOS>()), + notification_surface_manager_(std::move(notification_surface_manager)), + input_method_surface_manager_(std::move(input_method_surface_manager)) { + WMHelper::SetInstance(wm_helper_.get()); + display_ = std::make_unique<Display>(notification_surface_manager_.get(), + input_method_surface_manager_.get(), + std::move(file_helper)); + wayland_server_ = wayland::Server::Create(display_.get()); + // Wayland server creation can fail if XDG_RUNTIME_DIR is not set correctly. + if (wayland_server_) { + wayland_watcher_ = + std::make_unique<wayland::WaylandWatcher>(wayland_server_.get()); + } +} + +} // namespace exo diff --git a/chromium/components/exo/server/wayland_server_controller.h b/chromium/components/exo/server/wayland_server_controller.h new file mode 100644 index 00000000000..9f448c6e9fd --- /dev/null +++ b/chromium/components/exo/server/wayland_server_controller.h @@ -0,0 +1,61 @@ +// Copyright 2017 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 COMPONENTS_EXO_SERVER_WAYLAND_SERVER_CONTROLLER_H_ +#define COMPONENTS_EXO_SERVER_WAYLAND_SERVER_CONTROLLER_H_ + +#include <memory> + +#include "base/macros.h" + +namespace exo { + +namespace wayland { +class Server; +class WaylandWatcher; +} // namespace wayland + +class Display; +class FileHelper; +class WMHelper; +class NotificationSurfaceManager; +class InputMethodSurfaceManager; + +class WaylandServerController { + public: + static std::unique_ptr<WaylandServerController> CreateForArcIfNecessary( + std::unique_ptr<FileHelper> file_helper); + + // Creates WaylandServerController. Returns null if controller should not be + // created. + static std::unique_ptr<WaylandServerController> CreateIfNecessary( + std::unique_ptr<FileHelper> file_helper, + std::unique_ptr<NotificationSurfaceManager> notification_surface_manager, + std::unique_ptr<InputMethodSurfaceManager> input_method_surface_manager); + + ~WaylandServerController(); + + InputMethodSurfaceManager* input_method_surface_manager() { + return input_method_surface_manager_.get(); + } + + WaylandServerController( + std::unique_ptr<FileHelper> file_helper, + std::unique_ptr<NotificationSurfaceManager> notification_surface_manager, + std::unique_ptr<InputMethodSurfaceManager> input_method_surface_manager); + + private: + std::unique_ptr<WMHelper> wm_helper_; + std::unique_ptr<Display> display_; + std::unique_ptr<wayland::Server> wayland_server_; + std::unique_ptr<wayland::WaylandWatcher> wayland_watcher_; + std::unique_ptr<NotificationSurfaceManager> notification_surface_manager_; + std::unique_ptr<InputMethodSurfaceManager> input_method_surface_manager_; + + DISALLOW_COPY_AND_ASSIGN(WaylandServerController); +}; + +} // namespace exo + +#endif // COMPONENTS_EXO_SERVER_WAYLAND_SERVER_CONTROLLER_H_ diff --git a/chromium/components/exo/shell_surface_base.cc b/chromium/components/exo/shell_surface_base.cc index 315948b4243..2a6951c2f02 100644 --- a/chromium/components/exo/shell_surface_base.cc +++ b/chromium/components/exo/shell_surface_base.cc @@ -58,6 +58,14 @@ namespace exo { namespace { +// Set aura::client::kSkipImeProcessing to all Surface descendants. +void SetSkipImeProcessingToDescendentSurfaces(aura::Window* window) { + if (Surface::AsSurface(window)) + window->SetProperty(aura::client::kSkipImeProcessing, true); + for (aura::Window* child : window->children()) + SetSkipImeProcessingToDescendentSurfaces(child); +} + // The accelerator keys used to close ShellSurfaces. const struct { ui::KeyboardCode keycode; @@ -286,6 +294,8 @@ void CloseAllTransientChildren(aura::Window* window) { } } +int shell_id = 0; + } // namespace //////////////////////////////////////////////////////////////////////////////// @@ -296,7 +306,7 @@ ShellSurfaceBase::ShellSurfaceBase(Surface* surface, bool activatable, bool can_minimize, int container) - : SurfaceTreeHost("ExoShellSurfaceHost"), + : SurfaceTreeHost(base::StringPrintf("ExoShellSurfaceHost-%d", shell_id)), origin_(origin), container_(container), activatable_(activatable), @@ -327,6 +337,7 @@ ShellSurfaceBase::~ShellSurfaceBase() { root_surface()->RemoveSurfaceObserver(this); if (has_grab_) WMHelper::GetInstance()->GetCaptureClient()->RemoveObserver(this); + CHECK(!views::WidgetObserver::IsInObserverList()); } void ShellSurfaceBase::Activate() { @@ -556,7 +567,7 @@ void ShellSurfaceBase::OnSetFrame(SurfaceFrameType frame_type) { } bool frame_was_disabled = !frame_enabled(); - // TODO(b/141151475): Make frame_type a committable property. + bool frame_type_Changed = frame_type_ != frame_type; frame_type_ = frame_type; switch (frame_type) { case SurfaceFrameType::NONE: @@ -565,16 +576,15 @@ void ShellSurfaceBase::OnSetFrame(SurfaceFrameType frame_type) { case SurfaceFrameType::NORMAL: case SurfaceFrameType::AUTOHIDE: case SurfaceFrameType::OVERLAY: + case SurfaceFrameType::SHADOW: // Initialize the shadow if it didn't exist. Do not reset if // the frame type just switched from another enabled type or // there is a pending shadow_bounds_ change to avoid overriding // a shadow bounds which have been changed and not yet committed. - if (!shadow_bounds_ || (frame_was_disabled && !shadow_bounds_changed_)) + if (frame_type_Changed && + (!shadow_bounds_ || (frame_was_disabled && !shadow_bounds_changed_))) shadow_bounds_ = gfx::Rect(); break; - case SurfaceFrameType::SHADOW: - shadow_bounds_ = gfx::Rect(); - break; } if (!widget_) return; @@ -832,6 +842,15 @@ void ShellSurfaceBase::OnWindowDestroying(aura::Window* window) { window->RemoveObserver(this); } +void ShellSurfaceBase::OnWindowPropertyChanged(aura::Window* window, + const void* key, + intptr_t old_value) { + if (widget_ && window == widget_->GetNativeWindow() && + key == aura::client::kSkipImeProcessing) { + SetSkipImeProcessingToDescendentSurfaces(window); + } +} + //////////////////////////////////////////////////////////////////////////////// // wm::ActivationChangeObserver overrides: @@ -922,7 +941,7 @@ void ShellSurfaceBase::CreateShellSurfaceWidget( widget_->AddObserver(this); aura::Window* window = widget_->GetNativeWindow(); - window->SetName("ExoShellSurface"); + window->SetName(base::StringPrintf("ExoShellSurface-%d", shell_id++)); window->AddChild(host_window()); // Works for both mash and non-mash. https://crbug.com/839521 window->SetEventTargetingPolicy( @@ -1012,8 +1031,6 @@ void ShellSurfaceBase::UpdateShadow() { if (!widget_ || !root_surface()) return; - shadow_bounds_changed_ = false; - aura::Window* window = widget_->GetNativeWindow(); if (!shadow_bounds_) { @@ -1037,6 +1054,11 @@ void ShellSurfaceBase::UpdateShadow() { } } +void ShellSurfaceBase::UpdateFrameType() { + // Nothing to do here for now as frame type is updated immediately in + // OnSetFrame() by default. +} + gfx::Rect ShellSurfaceBase::GetVisibleBounds() const { // Use |geometry_| if set, otherwise use the visual bounds of the surface. if (geometry_.IsEmpty()) { @@ -1104,6 +1126,14 @@ void ShellSurfaceBase::StartCapture() { widget_->SetCapture(nullptr /* view */); } +void ShellSurfaceBase::OnPostWidgetCommit() { + // |shadow_bounds_changed_| represents whether |shadow_bounds_| has changed + // since the last commit, but as UpdateShadow() can be called multiple times + // in a single commit process, we need to ensure that it's not reset halfway + // in the current commit by resetting it here. + shadow_bounds_changed_ = false; +} + void ShellSurfaceBase::CommitWidget() { // Apply new window geometry. geometry_ = pending_geometry_; @@ -1127,6 +1157,7 @@ void ShellSurfaceBase::CommitWidget() { UpdateWidgetBounds(); SurfaceTreeHost::UpdateHostWindowBounds(); + UpdateFrameType(); UpdateShadow(); // System modal container is used by clients to implement overlay diff --git a/chromium/components/exo/shell_surface_base.h b/chromium/components/exo/shell_surface_base.h index 652950e3458..cfdec704180 100644 --- a/chromium/components/exo/shell_surface_base.h +++ b/chromium/components/exo/shell_surface_base.h @@ -181,6 +181,9 @@ class ShellSurfaceBase : public SurfaceTreeHost, // aura::WindowObserver: void OnWindowDestroying(aura::Window* window) override; + void OnWindowPropertyChanged(aura::Window* window, + const void* key, + intptr_t old_value) override; // wm::ActivationChangeObserver: void OnWindowActivated(ActivationReason reason, @@ -218,6 +221,8 @@ class ShellSurfaceBase : public SurfaceTreeHost, // |shadow_bounds_|. void UpdateShadow(); + virtual void UpdateFrameType(); + // Applies |system_modal_| to |widget_|. void UpdateSystemModal(); @@ -279,7 +284,7 @@ class ShellSurfaceBase : public SurfaceTreeHost, // Commit is deferred if this returns false. virtual bool OnPreWidgetCommit() = 0; - virtual void OnPostWidgetCommit() = 0; + virtual void OnPostWidgetCommit(); void CommitWidget(); diff --git a/chromium/components/exo/shell_surface_unittest.cc b/chromium/components/exo/shell_surface_unittest.cc index ed22f8fcd02..ecf1502e75a 100644 --- a/chromium/components/exo/shell_surface_unittest.cc +++ b/chromium/components/exo/shell_surface_unittest.cc @@ -11,14 +11,11 @@ #include "ash/shell.h" #include "ash/wm/window_state.h" #include "ash/wm/wm_event.h" -#include "ash/wm/workspace/workspace_window_resizer.h" #include "ash/wm/workspace_controller_test_api.h" #include "base/bind.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "components/exo/buffer.h" -#include "components/exo/client_controlled_shell_surface.h" -#include "components/exo/display.h" #include "components/exo/permission.h" #include "components/exo/shell_surface_util.h" #include "components/exo/sub_surface.h" @@ -27,6 +24,7 @@ #include "components/exo/test/exo_test_helper.h" #include "components/exo/wm_helper.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/aura/client/aura_constants.h" #include "ui/aura/client/capture_client.h" #include "ui/aura/window.h" #include "ui/aura/window_delegate.h" @@ -864,42 +862,136 @@ TEST_F(ShellSurfaceTest, Popup) { sub_popup_surface->Commit(); ASSERT_EQ(gfx::Rect(100, 50, 256, 256), sub_popup_shell_surface->GetWidget()->GetWindowBoundsInScreen()); - + aura::Window* target = + sub_popup_shell_surface->GetWidget()->GetNativeWindow(); // The capture should be on sub_popup_shell_surface. EXPECT_EQ(WMHelper::GetInstance()->GetCaptureClient()->GetCaptureWindow(), - sub_popup_shell_surface->GetWidget()->GetNativeWindow()); - EXPECT_EQ(aura::client::WINDOW_TYPE_POPUP, - sub_popup_shell_surface->GetWidget()->GetNativeWindow()->type()); + target); + EXPECT_EQ(aura::client::WINDOW_TYPE_POPUP, target->type()); { // Mouse is on the top most popup. ui::MouseEvent event(ui::ET_MOUSE_MOVED, gfx::Point(0, 0), gfx::Point(100, 50), ui::EventTimeForNow(), 0, 0); + ui::Event::DispatcherApi(&event).set_target(target); EXPECT_EQ(sub_popup_surface.get(), GetTargetSurfaceForLocatedEvent(&event)); } { // Move the mouse to the parent popup. ui::MouseEvent event(ui::ET_MOUSE_MOVED, gfx::Point(-25, 0), gfx::Point(75, 50), ui::EventTimeForNow(), 0, 0); + ui::Event::DispatcherApi(&event).set_target(target); EXPECT_EQ(popup_surface.get(), GetTargetSurfaceForLocatedEvent(&event)); } { // Move the mouse to the main window. ui::MouseEvent event(ui::ET_MOUSE_MOVED, gfx::Point(-25, -25), gfx::Point(75, 25), ui::EventTimeForNow(), 0, 0); + ui::Event::DispatcherApi(&event).set_target(target); EXPECT_EQ(surface.get(), GetTargetSurfaceForLocatedEvent(&event)); } // Removing top most popup moves the grab to parent popup. sub_popup_shell_surface.reset(); + target = popup_shell_surface->GetWidget()->GetNativeWindow(); EXPECT_EQ(WMHelper::GetInstance()->GetCaptureClient()->GetCaptureWindow(), - popup_shell_surface->GetWidget()->GetNativeWindow()); + target); { // Targetting should still work. ui::MouseEvent event(ui::ET_MOUSE_MOVED, gfx::Point(0, 0), gfx::Point(50, 50), ui::EventTimeForNow(), 0, 0); + ui::Event::DispatcherApi(&event).set_target(target); + EXPECT_EQ(popup_surface.get(), GetTargetSurfaceForLocatedEvent(&event)); + } +} + +TEST_F(ShellSurfaceTest, PopupWithInputRegion) { + gfx::Size buffer_size(256, 256); + std::unique_ptr<Buffer> buffer( + new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size))); + std::unique_ptr<Surface> surface(new Surface); + std::unique_ptr<ShellSurface> shell_surface(new ShellSurface(surface.get())); + + surface->Attach(buffer.get()); + surface->SetInputRegion(cc::Region()); + + std::unique_ptr<Buffer> child_buffer( + new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size))); + std::unique_ptr<Surface> child_surface(new Surface); + child_surface->Attach(child_buffer.get()); + + auto subsurface = + std::make_unique<SubSurface>(child_surface.get(), surface.get()); + subsurface->SetPosition(gfx::Point(10, 10)); + child_surface->SetInputRegion(cc::Region(gfx::Rect(0, 0, 256, 2560))); + child_surface->Commit(); + surface->Commit(); + shell_surface->GetWidget()->SetBounds(gfx::Rect(0, 0, 256, 256)); + + std::unique_ptr<Buffer> popup_buffer( + new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size))); + std::unique_ptr<Surface> popup_surface(new Surface); + popup_surface->Attach(popup_buffer.get()); + std::unique_ptr<ShellSurface> popup_shell_surface(CreatePopupShellSurface( + popup_surface.get(), shell_surface.get(), gfx::Point(50, 50))); + popup_shell_surface->Grab(); + popup_surface->Commit(); + ASSERT_EQ(gfx::Rect(50, 50, 256, 256), + popup_shell_surface->GetWidget()->GetWindowBoundsInScreen()); + + // Verify that created shell surface is popup and has capture. + EXPECT_EQ(aura::client::WINDOW_TYPE_POPUP, + popup_shell_surface->GetWidget()->GetNativeWindow()->type()); + EXPECT_EQ(WMHelper::GetInstance()->GetCaptureClient()->GetCaptureWindow(), + popup_shell_surface->GetWidget()->GetNativeWindow()); + + // Setting frame type on popup should have no effect. + popup_surface->SetFrame(SurfaceFrameType::NORMAL); + EXPECT_FALSE(popup_shell_surface->frame_enabled()); + + aura::Window* target = popup_shell_surface->GetWidget()->GetNativeWindow(); + + { + // Mouse is on the popup. + ui::MouseEvent event(ui::ET_MOUSE_MOVED, gfx::Point(0, 0), + gfx::Point(50, 50), ui::EventTimeForNow(), 0, 0); + ui::Event::DispatcherApi(&event).set_target(target); EXPECT_EQ(popup_surface.get(), GetTargetSurfaceForLocatedEvent(&event)); } + + { + // If it matches the parent's sub surface, use it. + ui::MouseEvent event(ui::ET_MOUSE_MOVED, gfx::Point(-25, 0), + gfx::Point(25, 50), ui::EventTimeForNow(), 0, 0); + ui::Event::DispatcherApi(&event).set_target(target); + EXPECT_EQ(child_surface.get(), GetTargetSurfaceForLocatedEvent(&event)); + } + { + // If it didnt't match any surface in the parent, fallback to + // the popup's surface. + ui::MouseEvent event(ui::ET_MOUSE_MOVED, gfx::Point(-50, 0), + gfx::Point(0, 50), ui::EventTimeForNow(), 0, 0); + ui::Event::DispatcherApi(&event).set_target(target); + EXPECT_EQ(popup_surface.get(), GetTargetSurfaceForLocatedEvent(&event)); + } + popup_surface.reset(); + EXPECT_EQ(WMHelper::GetInstance()->GetCaptureClient()->GetCaptureWindow(), + nullptr); + + auto window = std::make_unique<aura::Window>(nullptr); + window->Init(ui::LAYER_TEXTURED); + window->SetBounds(gfx::Rect(0, 0, 256, 256)); + shell_surface->GetWidget()->GetNativeWindow()->AddChild(window.get()); + window->Show(); + + { + // If non surface window covers the window, + /// GetTargetSurfaceForLocatedEvent should return nullptr. + ui::MouseEvent event(ui::ET_MOUSE_MOVED, gfx::Point(0, 0), + gfx::Point(50, 50), ui::EventTimeForNow(), 0, 0); + ui::Event::DispatcherApi(&event).set_target(window.get()); + EXPECT_EQ(nullptr, GetTargetSurfaceForLocatedEvent(&event)); + } } TEST_F(ShellSurfaceTest, Caption) { @@ -914,13 +1006,15 @@ TEST_F(ShellSurfaceTest, Caption) { surface->Commit(); shell_surface->GetWidget()->SetBounds(gfx::Rect(0, 0, 256, 256)); - shell_surface->GetWidget()->GetNativeWindow()->SetCapture(); + aura::Window* target = shell_surface->GetWidget()->GetNativeWindow(); + target->SetCapture(); EXPECT_EQ(WMHelper::GetInstance()->GetCaptureClient()->GetCaptureWindow(), shell_surface->GetWidget()->GetNativeWindow()); { // Move the mouse at the caption of the captured window. ui::MouseEvent event(ui::ET_MOUSE_MOVED, gfx::Point(5, 5), gfx::Point(5, 5), ui::EventTimeForNow(), 0, 0); + ui::Event::DispatcherApi(&event).set_target(target); EXPECT_EQ(nullptr, GetTargetSurfaceForLocatedEvent(&event)); } @@ -930,6 +1024,7 @@ TEST_F(ShellSurfaceTest, Caption) { gfx::Point center = bounds.CenterPoint(); ui::MouseEvent event(ui::ET_MOUSE_MOVED, center - bounds.OffsetFromOrigin(), center, ui::EventTimeForNow(), 0, 0); + ui::Event::DispatcherApi(&event).set_target(target); EXPECT_EQ(surface.get(), GetTargetSurfaceForLocatedEvent(&event)); } } @@ -954,13 +1049,14 @@ TEST_F(ShellSurfaceTest, CaptionWithPopup) { popup_surface.get(), shell_surface.get(), gfx::Point(50, 50))); popup_shell_surface->Grab(); popup_surface->Commit(); - + aura::Window* target = popup_shell_surface->GetWidget()->GetNativeWindow(); EXPECT_EQ(WMHelper::GetInstance()->GetCaptureClient()->GetCaptureWindow(), - popup_shell_surface->GetWidget()->GetNativeWindow()); + target); { // Move the mouse at the popup window. ui::MouseEvent event(ui::ET_MOUSE_MOVED, gfx::Point(5, 5), gfx::Point(55, 55), ui::EventTimeForNow(), 0, 0); + ui::Event::DispatcherApi(&event).set_target(target); EXPECT_EQ(popup_surface.get(), GetTargetSurfaceForLocatedEvent(&event)); } @@ -968,6 +1064,7 @@ TEST_F(ShellSurfaceTest, CaptionWithPopup) { // Move the mouse at the caption of the main window. ui::MouseEvent event(ui::ET_MOUSE_MOVED, gfx::Point(-45, -45), gfx::Point(5, 5), ui::EventTimeForNow(), 0, 0); + ui::Event::DispatcherApi(&event).set_target(target); EXPECT_EQ(nullptr, GetTargetSurfaceForLocatedEvent(&event)); } @@ -975,8 +1072,31 @@ TEST_F(ShellSurfaceTest, CaptionWithPopup) { // Move the mouse in the main window. ui::MouseEvent event(ui::ET_MOUSE_MOVED, gfx::Point(-25, 0), gfx::Point(25, 50), ui::EventTimeForNow(), 0, 0); + ui::Event::DispatcherApi(&event).set_target(target); EXPECT_EQ(surface.get(), GetTargetSurfaceForLocatedEvent(&event)); } } +TEST_F(ShellSurfaceTest, SkipImeProcessingPropagateToSurface) { + gfx::Size buffer_size(256, 256); + auto buffer = std::make_unique<Buffer>( + exo_test_helper()->CreateGpuMemoryBuffer(buffer_size)); + auto surface = std::make_unique<Surface>(); + auto shell_surface = std::make_unique<ShellSurface>(surface.get()); + + surface->Attach(buffer.get()); + surface->Commit(); + shell_surface->GetWidget()->SetBounds(gfx::Rect(0, 0, 256, 256)); + shell_surface->OnSetFrame(SurfaceFrameType::NORMAL); + + aura::Window* window = shell_surface->GetWidget()->GetNativeWindow(); + ASSERT_FALSE(window->GetProperty(aura::client::kSkipImeProcessing)); + ASSERT_FALSE( + surface->window()->GetProperty(aura::client::kSkipImeProcessing)); + + window->SetProperty(aura::client::kSkipImeProcessing, true); + EXPECT_TRUE(window->GetProperty(aura::client::kSkipImeProcessing)); + EXPECT_TRUE(surface->window()->GetProperty(aura::client::kSkipImeProcessing)); +} + } // namespace exo diff --git a/chromium/components/exo/shell_surface_util.cc b/chromium/components/exo/shell_surface_util.cc index b53d60c62a2..a6a523d83e3 100644 --- a/chromium/components/exo/shell_surface_util.cc +++ b/chromium/components/exo/shell_surface_util.cc @@ -17,6 +17,7 @@ #include "ui/aura/client/capture_client.h" #include "ui/aura/window.h" #include "ui/aura/window_delegate.h" +#include "ui/aura/window_targeter.h" #include "ui/events/event.h" #include "ui/views/widget/widget.h" #include "ui/wm/core/window_util.h" @@ -57,6 +58,18 @@ bool ShouldHTComponentBlocked(int component) { } } +// Find the lowest targeter in the parent chain. +aura::WindowTargeter* FindTargeter(ui::EventTarget* target) { + do { + ui::EventTargeter* targeter = target->GetEventTargeter(); + if (targeter) + return static_cast<aura::WindowTargeter*>(targeter); + target = target->GetParentTarget(); + } while (target); + + return nullptr; +} + } // namespace void SetShellApplicationId(aura::Window* window, @@ -130,13 +143,14 @@ ShellSurfaceBase* GetShellSurfaceBaseForWindow(aura::Window* window) { return static_cast<ShellSurfaceBase*>(widget->widget_delegate()); } -Surface* GetTargetSurfaceForLocatedEvent(ui::LocatedEvent* event) { +Surface* GetTargetSurfaceForLocatedEvent( + const ui::LocatedEvent* original_event) { aura::Window* window = WMHelper::GetInstance()->GetCaptureClient()->GetCaptureWindow(); - gfx::PointF location_in_target_f = event->location_f(); - - if (!window) - return Surface::AsSurface(static_cast<aura::Window*>(event->target())); + if (!window) { + return Surface::AsSurface( + static_cast<aura::Window*>(original_event->target())); + } Surface* main_surface = GetShellMainSurface(window); // Skip if the event is captured by non exo windows. @@ -148,13 +162,33 @@ Surface* GetTargetSurfaceForLocatedEvent(ui::LocatedEvent* event) { if (!main_surface) return nullptr; } + DCHECK_EQ(window, static_cast<aura::Window*>(original_event->target())); - while (true) { - gfx::Point location_in_target = gfx::ToFlooredPoint(location_in_target_f); - aura::Window* focused = window->GetEventHandlerForPoint(location_in_target); + // Create a clone of the event as targeter may update it during the + // search. + auto cloned = ui::Event::Clone(*original_event); + ui::LocatedEvent* event = cloned->AsLocatedEvent(); - if (focused) - return Surface::AsSurface(focused); + while (true) { + gfx::PointF location_in_target_f = event->location_f(); + gfx::Point location_in_target = event->location(); + ui::EventTarget* event_target = window; + aura::WindowTargeter* targeter = FindTargeter(event_target); + DCHECK(targeter); + + aura::Window* focused = + static_cast<aura::Window*>(targeter->FindTargetForEvent(window, event)); + + if (focused) { + Surface* surface = Surface::AsSurface(focused); + if (focused != window) + return surface; + else if (surface && surface->HitTest(location_in_target)) { + // If the targeting fallback to the root (first) window, test the + // hit region again. + return surface; + } + } // If the event falls into the place where the window system should care // about (i.e. window caption), do not check the transient parent but just @@ -170,8 +204,8 @@ Surface* GetTargetSurfaceForLocatedEvent(ui::LocatedEvent* event) { if (!parent_window) return main_surface; - aura::Window::ConvertPointToTarget(window, parent_window, - &location_in_target_f); + event->set_location_f(location_in_target_f); + event_target->ConvertEventToTarget(parent_window, event); window = parent_window; } } diff --git a/chromium/components/exo/shell_surface_util.h b/chromium/components/exo/shell_surface_util.h index ac0a7f94e56..746ffeb4f13 100644 --- a/chromium/components/exo/shell_surface_util.h +++ b/chromium/components/exo/shell_surface_util.h @@ -65,7 +65,7 @@ ShellSurfaceBase* GetShellSurfaceBaseForWindow(aura::Window* window); // event handling is grabbed by an window, it'll first examine that // window, then traverse to its transient parent if the parent also // requested grab. -Surface* GetTargetSurfaceForLocatedEvent(ui::LocatedEvent* event); +Surface* GetTargetSurfaceForLocatedEvent(const ui::LocatedEvent* event); // Allow the |window| to activate itself for the diration of |timeout|. Returns // the permission object, where deleting the object ammounts to Revoke()ing the diff --git a/chromium/components/exo/sub_surface.cc b/chromium/components/exo/sub_surface.cc index b18beb319e5..8c81ed507fe 100644 --- a/chromium/components/exo/sub_surface.cc +++ b/chromium/components/exo/sub_surface.cc @@ -8,6 +8,7 @@ #include "base/trace_event/trace_event.h" #include "base/trace_event/traced_value.h" #include "components/exo/surface.h" +#include "ui/aura/client/aura_constants.h" namespace exo { @@ -113,6 +114,11 @@ bool SubSurface::IsInputEnabled(Surface* surface) const { return !parent_ || parent_->IsInputEnabled(surface); } +void SubSurface::OnSetParent(Surface* parent, const gfx::Point&) { + if (parent->window()->GetProperty(aura::client::kSkipImeProcessing)) + surface_->window()->SetProperty(aura::client::kSkipImeProcessing, true); +} + //////////////////////////////////////////////////////////////////////////////// // SurfaceObserver overrides: diff --git a/chromium/components/exo/sub_surface.h b/chromium/components/exo/sub_surface.h index 634de9d81e1..97daeacbc04 100644 --- a/chromium/components/exo/sub_surface.h +++ b/chromium/components/exo/sub_surface.h @@ -55,7 +55,7 @@ class SubSurface : public SurfaceDelegate, public SurfaceObserver { void OnSetFrame(SurfaceFrameType type) override {} void OnSetFrameColors(SkColor active_color, SkColor inactive_color) override { } - void OnSetParent(Surface* parent, const gfx::Point& position) override {} + void OnSetParent(Surface* parent, const gfx::Point& position) override; void OnSetStartupId(const char* startup_id) override {} void OnSetApplicationId(const char* application_id) override {} void OnActivationRequested() override {} diff --git a/chromium/components/exo/sub_surface_unittest.cc b/chromium/components/exo/sub_surface_unittest.cc index dd18c8c95dc..946008680d4 100644 --- a/chromium/components/exo/sub_surface_unittest.cc +++ b/chromium/components/exo/sub_surface_unittest.cc @@ -4,11 +4,13 @@ #include "components/exo/sub_surface.h" +#include "components/exo/buffer.h" #include "components/exo/shell_surface.h" #include "components/exo/surface.h" #include "components/exo/test/exo_test_base.h" #include "components/exo/test/exo_test_helper.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/aura/client/aura_constants.h" namespace exo { namespace { @@ -158,5 +160,25 @@ TEST_F(SubSurfaceTest, SetCommitBehavior) { grandchild->window()->bounds().origin().ToString()); } +TEST_F(SubSurfaceTest, SetOnParent) { + gfx::Size buffer_size(32, 32); + std::unique_ptr<Buffer> buffer( + new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size))); + auto parent = std::make_unique<Surface>(); + auto shell_surface = std::make_unique<ShellSurface>(parent.get()); + parent->Attach(buffer.get()); + parent->Commit(); + + shell_surface->GetWidget()->GetNativeWindow()->SetProperty( + aura::client::kSkipImeProcessing, true); + ASSERT_TRUE(parent->window()->GetProperty(aura::client::kSkipImeProcessing)); + + // SkipImeProcessing property is propagated to SubSurface. + auto surface = std::make_unique<Surface>(); + auto sub_surface = std::make_unique<SubSurface>(surface.get(), parent.get()); + surface->SetParent(parent.get(), gfx::Point(10, 10)); + EXPECT_TRUE(surface->window()->GetProperty(aura::client::kSkipImeProcessing)); +} + } // namespace } // namespace exo diff --git a/chromium/components/exo/surface.cc b/chromium/components/exo/surface.cc index d9979147092..01fb6a805b7 100644 --- a/chromium/components/exo/surface.cc +++ b/chromium/components/exo/surface.cc @@ -221,6 +221,8 @@ const std::string& GetApplicationId(aura::Window* window) { return empty_app_id; } +int surface_id = 0; + } // namespace DEFINE_UI_CLASS_PROPERTY_KEY(int32_t, kClientSurfaceIdKey, 0) @@ -241,7 +243,7 @@ Surface::Surface() : window_( std::make_unique<aura::Window>(new CustomWindowDelegate(this), aura::client::WINDOW_TYPE_CONTROL)) { - window_->SetName("ExoSurface"); + window_->SetName(base::StringPrintf("ExoSurface-%d", surface_id++)); window_->SetProperty(kSurfaceKey, this); window_->Init(ui::LAYER_NOT_DRAWN); window_->SetEventTargeter(std::make_unique<CustomWindowTargeter>()); @@ -610,7 +612,8 @@ void Surface::CommitSurfaceHierarchy(bool synchronized) { state_.only_visible_on_secure_output || pending_state_.blend_mode != state_.blend_mode || pending_state_.alpha != state_.alpha || - pending_state_.color_space != state_.color_space; + pending_state_.color_space != state_.color_space || + pending_state_.is_tracking_occlusion != state_.is_tracking_occlusion; bool needs_update_buffer_transform = pending_state_.buffer_scale != state_.buffer_scale || @@ -638,6 +641,13 @@ void Surface::CommitSurfaceHierarchy(bool synchronized) { ? aura::EventTargetingPolicy::kDescendantsOnly : aura::EventTargetingPolicy::kTargetAndDescendants); + if (state_.is_tracking_occlusion) { + // TODO(edcourtney): Currently, it doesn't seem to be possible to stop + // tracking the occlusion state once started, but it would be nice to stop + // if the tracked occlusion region becomes empty. + window_->TrackOcclusionState(); + } + #if defined(OS_CHROMEOS) if (needs_output_protection) { if (!output_protection_) { @@ -859,12 +869,11 @@ bool Surface::FillsBoundsOpaquely() const { } void Surface::SetOcclusionTracking(bool tracking) { - is_tracking_occlusion_ = tracking; - // TODO(edcourtney): Currently, it doesn't seem to be possible to stop - // tracking the occlusion state once started, but it would be nice to stop if - // the tracked occlusion region becomes empty. - if (is_tracking_occlusion_) - window()->TrackOcclusionState(); + pending_state_.is_tracking_occlusion = tracking; +} + +bool Surface::IsTrackingOcclusion() { + return state_.is_tracking_occlusion; } void Surface::SetSurfaceHierarchyContentBoundsForTest( @@ -999,6 +1008,7 @@ void Surface::AppendContentsToFrame(const gfx::Point& origin, render_pass->damage_rect.Union( gfx::ConvertRectToPixel(device_scale_factor, damage_rect)); } + damage_.Clear(); gfx::PointF scale(content_size_.width(), content_size_.height()); @@ -1157,7 +1167,7 @@ void Surface::UpdateContentSize() { } void Surface::OnWindowOcclusionChanged() { - if (!is_tracking_occlusion_) + if (!state_.is_tracking_occlusion) return; for (SurfaceObserver& observer : observers_) diff --git a/chromium/components/exo/surface.h b/chromium/components/exo/surface.h index b49e8e9315d..d8a944af11f 100644 --- a/chromium/components/exo/surface.h +++ b/chromium/components/exo/surface.h @@ -281,7 +281,7 @@ class Surface final : public ui::PropertyHandler { void OnWindowOcclusionChanged(); // True if the window for this surface has its occlusion tracked. - bool is_tracking_occlusion() const { return is_tracking_occlusion_; } + bool IsTrackingOcclusion(); // Sets the |surface_hierarchy_content_bounds_|. void SetSurfaceHierarchyContentBoundsForTest(const gfx::Rect& content_bounds); @@ -309,6 +309,7 @@ class Surface final : public ui::PropertyHandler { float alpha = 1.0f; gfx::Vector2d offset; gfx::ColorSpace color_space; + bool is_tracking_occlusion = false; }; class BufferAttachment { public: @@ -451,9 +452,6 @@ class Surface final : public ui::PropertyHandler { // Surface observer list. Surface does not own the observers. base::ObserverList<SurfaceObserver, true>::Unchecked observers_; - // Whether this surface is tracking occlusion for the client. - bool is_tracking_occlusion_ = false; - #if defined(OS_CHROMEOS) std::unique_ptr<ash::OutputProtectionDelegate> output_protection_; #endif // defined(OS_CHROMEOS) diff --git a/chromium/components/exo/surface_unittest.cc b/chromium/components/exo/surface_unittest.cc index 680135e1159..9cdb9565cfc 100644 --- a/chromium/components/exo/surface_unittest.cc +++ b/chromium/components/exo/surface_unittest.cc @@ -190,6 +190,69 @@ TEST_P(SurfaceTest, Damage) { } } +TEST_P(SurfaceTest, SubsurfaceDamageAggregation) { + gfx::Size buffer_size(256, 512); + auto buffer = std::make_unique<Buffer>( + exo_test_helper()->CreateGpuMemoryBuffer(buffer_size)); + auto surface = std::make_unique<Surface>(); + auto shell_surface = std::make_unique<ShellSurface>(surface.get()); + surface->Attach(buffer.get()); + + gfx::Size child_buffer_size(64, 128); + auto child_buffer = std::make_unique<Buffer>( + exo_test_helper()->CreateGpuMemoryBuffer(child_buffer_size)); + auto child_surface = std::make_unique<Surface>(); + auto sub_surface = + std::make_unique<SubSurface>(child_surface.get(), surface.get()); + child_surface->Attach(child_buffer.get()); + child_surface->Commit(); + surface->Commit(); + base::RunLoop().RunUntilIdle(); + + { + // Initial frame has full damage. + const viz::CompositorFrame& frame = + GetFrameFromSurface(shell_surface.get()); + const gfx::Rect scaled_damage = gfx::ToNearestRect(gfx::ScaleRect( + gfx::RectF(gfx::Rect(buffer_size)), device_scale_factor())); + EXPECT_EQ(scaled_damage, frame.render_pass_list.back()->damage_rect); + } + + const gfx::RectF surface_damage(16, 16); + const gfx::RectF subsurface_damage(32, 32, 16, 16); + int margin = ceil(device_scale_factor()); + + child_surface->Damage(gfx::ToNearestRect(subsurface_damage)); + child_surface->Commit(); + surface->Commit(); + base::RunLoop().RunUntilIdle(); + + { + // Subsurface damage should be propagated. + const viz::CompositorFrame& frame = + GetFrameFromSurface(shell_surface.get()); + const gfx::Rect scaled_damage = gfx::ToNearestRect( + gfx::ScaleRect(subsurface_damage, device_scale_factor())); + EXPECT_TRUE(scaled_damage.ApproximatelyEqual( + frame.render_pass_list.back()->damage_rect, margin)); + } + + surface->Damage(gfx::ToNearestRect(surface_damage)); + surface->Commit(); + base::RunLoop().RunUntilIdle(); + + { + // When commit is called on the root with no call on the child, the damage + // from the previous frame shouldn't persist. + const viz::CompositorFrame& frame = + GetFrameFromSurface(shell_surface.get()); + const gfx::Rect scaled_damage = gfx::ToNearestRect( + gfx::ScaleRect(surface_damage, device_scale_factor())); + EXPECT_TRUE(scaled_damage.ApproximatelyEqual( + frame.render_pass_list.back()->damage_rect, margin)); + } +} + void SetFrameTime(base::TimeTicks* result, base::TimeTicks frame_time) { *result = frame_time; } @@ -1102,11 +1165,11 @@ TEST_P(SurfaceTest, UpdatesOcclusionOnDestroyingSubsurface) { auto sub_surface = std::make_unique<SubSurface>(child_surface.get(), surface.get()); child_surface->Attach(child_buffer.get()); + // Turn on occlusion tracking. + child_surface->SetOcclusionTracking(true); child_surface->Commit(); surface->Commit(); - // Turn on occlusion tracking. - child_surface->SetOcclusionTracking(true); SurfaceObserverForTest observer; ScopedSurface scoped_child_surface(child_surface.get(), &observer); diff --git a/chromium/components/exo/text_input.cc b/chromium/components/exo/text_input.cc index d004a1f8cd3..370a77ee995 100644 --- a/chromium/components/exo/text_input.cc +++ b/chromium/components/exo/text_input.cc @@ -324,6 +324,13 @@ bool TextInput::SetCompositionFromExistingText( return false; } +// TODO(crbug.com/1091088) Implement setAutocorrectRange +bool TextInput::SetAutocorrectRange(const base::string16& autocorrect_text, + const gfx::Range& range) { + NOTIMPLEMENTED_LOG_ONCE(); + return false; +} + void TextInput::OnKeyboardVisibilityChanged(bool is_visible) { delegate_->OnVirtualKeyboardVisibilityChanged(is_visible); } diff --git a/chromium/components/exo/text_input.h b/chromium/components/exo/text_input.h index e3f89fdd81a..f463cfd298d 100644 --- a/chromium/components/exo/text_input.h +++ b/chromium/components/exo/text_input.h @@ -140,6 +140,8 @@ class TextInput : public ui::TextInputClient, bool SetCompositionFromExistingText( const gfx::Range& range, const std::vector<ui::ImeTextSpan>& ui_ime_text_spans) override; + bool SetAutocorrectRange(const base::string16& autocorrect_text, + const gfx::Range& range) override; // ash::KeyboardControllerObserver: void OnKeyboardVisibilityChanged(bool is_visible) override; diff --git a/chromium/components/exo/touch.cc b/chromium/components/exo/touch.cc index b59b11efca2..c1dc6b3ad6e 100644 --- a/chromium/components/exo/touch.cc +++ b/chromium/components/exo/touch.cc @@ -60,6 +60,8 @@ bool Touch::HasStylusDelegate() const { // ui::EventHandler overrides: void Touch::OnTouchEvent(ui::TouchEvent* event) { + seat_->SetLastLocation(event->root_location()); + bool send_details = false; const int touch_pointer_id = event->pointer_details().id; diff --git a/chromium/components/exo/wayland/BUILD.gn b/chromium/components/exo/wayland/BUILD.gn index 28c660133d4..6800a87c54d 100644 --- a/chromium/components/exo/wayland/BUILD.gn +++ b/chromium/components/exo/wayland/BUILD.gn @@ -36,7 +36,6 @@ source_set("wayland") { "wayland_touch_delegate.cc", "wayland_touch_delegate.h", "wayland_watcher.cc", - "wayland_watcher.h", "wl_compositor.cc", "wl_compositor.h", "wl_data_device_manager.cc", @@ -120,10 +119,12 @@ source_set("wayland") { deps += [ "//build/config/linux/libdrm", "//third_party/wayland-protocols:linux_dmabuf_protocol", + "//ui/ozone", ] if (is_chromeos) { deps += [ + "//ash", "//ash/public/cpp", "//ui/events/ozone/layout", ] @@ -152,6 +153,8 @@ source_set("wayland") { "wayland_positioner.h", "wl_shell.cc", "wl_shell.h", + "xdg_shell.cc", + "xdg_shell.h", "zcr_color_space.cc", "zcr_color_space.h", "zcr_cursor_shapes.cc", diff --git a/chromium/components/exo/wayland/DEPS b/chromium/components/exo/wayland/DEPS index 33a9ebc4583..074321a0083 100644 --- a/chromium/components/exo/wayland/DEPS +++ b/chromium/components/exo/wayland/DEPS @@ -1,4 +1,5 @@ include_rules = [ "+services/viz/public/mojom/compositing/compositor_frame_sink.mojom.h", "+third_party/wayland/include", + "+ui/ozone/public", ] diff --git a/chromium/components/exo/wayland/clients/blur.cc b/chromium/components/exo/wayland/clients/blur.cc index 4b37d4db4d3..e30e562cd8a 100644 --- a/chromium/components/exo/wayland/clients/blur.cc +++ b/chromium/components/exo/wayland/clients/blur.cc @@ -215,7 +215,7 @@ void Blur::Run(double sigma_x, } if (gr_context_) { - gr_context_->flush(); + gr_context_->flushAndSubmit(); glFinish(); } diff --git a/chromium/components/exo/wayland/clients/blur_main.cc b/chromium/components/exo/wayland/clients/blur_main.cc index 477041346cd..f1718f8dfe6 100644 --- a/chromium/components/exo/wayland/clients/blur_main.cc +++ b/chromium/components/exo/wayland/clients/blur_main.cc @@ -8,6 +8,7 @@ #include "base/at_exit.h" #include "base/command_line.h" +#include "base/logging.h" #include "base/message_loop/message_pump_type.h" #include "base/strings/string_number_conversions.h" #include "base/task/single_thread_task_executor.h" diff --git a/chromium/components/exo/wayland/clients/client_base.cc b/chromium/components/exo/wayland/clients/client_base.cc index e2f0dd9ac27..95323b80b57 100644 --- a/chromium/components/exo/wayland/clients/client_base.cc +++ b/chromium/components/exo/wayland/clients/client_base.cc @@ -968,7 +968,7 @@ std::unique_ptr<ClientBase::Buffer> ClientBase::CreateDrmBuffer( texture_info.fFormat = kSizedInternalFormat; GrBackendTexture backend_texture(size.width(), size.height(), GrMipMapped::kNo, texture_info); - buffer->sk_surface = SkSurface::MakeFromBackendTextureAsRenderTarget( + buffer->sk_surface = SkSurface::MakeFromBackendTexture( gr_context_.get(), backend_texture, kTopLeft_GrSurfaceOrigin, /* sampleCnt */ 0, kColorType, /* colorSpace */ nullptr, /* props */ nullptr); diff --git a/chromium/components/exo/wayland/clients/color_space.cc b/chromium/components/exo/wayland/clients/color_space.cc index 6e366d84a06..27931638ef4 100644 --- a/chromium/components/exo/wayland/clients/color_space.cc +++ b/chromium/components/exo/wayland/clients/color_space.cc @@ -11,6 +11,7 @@ #include "base/at_exit.h" #include "base/command_line.h" +#include "base/logging.h" #include "base/message_loop/message_pump_type.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" diff --git a/chromium/components/exo/wayland/clients/explicit_synchronization.cc b/chromium/components/exo/wayland/clients/explicit_synchronization.cc index cc346d2c231..a54d2cc0f94 100644 --- a/chromium/components/exo/wayland/clients/explicit_synchronization.cc +++ b/chromium/components/exo/wayland/clients/explicit_synchronization.cc @@ -88,7 +88,7 @@ void ExplicitSynchronizationClient::Run() { .toSkColor()); // Create an EGLSyncKHR object to signal when rendering is done. - gr_context_->flush(); + gr_context_->flushAndSubmit(); buffer->egl_sync.reset(new ScopedEglSync( eglCreateSyncKHR(eglGetCurrentDisplay(), egl_sync_type_, nullptr))); DCHECK(buffer->egl_sync->is_valid()); diff --git a/chromium/components/exo/wayland/clients/fullscreen_shell.cc b/chromium/components/exo/wayland/clients/fullscreen_shell.cc index 19024dafae2..59a5a76022e 100644 --- a/chromium/components/exo/wayland/clients/fullscreen_shell.cc +++ b/chromium/components/exo/wayland/clients/fullscreen_shell.cc @@ -112,7 +112,7 @@ void FullscreenClient::Paint(const wl_callback_listener& frame_listener) { canvas->drawRect(rect, paint); if (gr_context_) { - gr_context_->flush(); + gr_context_->flushAndSubmit(); glFinish(); } diff --git a/chromium/components/exo/wayland/clients/rects.cc b/chromium/components/exo/wayland/clients/rects.cc index d4d101cc03d..8f34046b6b9 100644 --- a/chromium/components/exo/wayland/clients/rects.cc +++ b/chromium/components/exo/wayland/clients/rects.cc @@ -438,7 +438,7 @@ int RectsClient::Run(const ClientBase::InitParams& params, } GrContext* gr_context = gr_context_.get(); if (gr_context) { - gr_context->flush(); + gr_context->flushAndSubmit(); #if defined(USE_GBM) if (egl_sync_type_) { diff --git a/chromium/components/exo/wayland/clients/simple.cc b/chromium/components/exo/wayland/clients/simple.cc index 1a517247ca2..37015530eb2 100644 --- a/chromium/components/exo/wayland/clients/simple.cc +++ b/chromium/components/exo/wayland/clients/simple.cc @@ -140,7 +140,7 @@ void Simple::Run(int frames, canvas->clear(kColors[++frame_count % base::size(kColors)]); if (gr_context_) { - gr_context_->flush(); + gr_context_->flushAndSubmit(); glFinish(); } diff --git a/chromium/components/exo/wayland/clients/subsurface.cc b/chromium/components/exo/wayland/clients/subsurface.cc index 0ef02b65bc0..41afd463c22 100644 --- a/chromium/components/exo/wayland/clients/subsurface.cc +++ b/chromium/components/exo/wayland/clients/subsurface.cc @@ -98,7 +98,7 @@ void SubSurfaceClient::Run(const ClientBase::InitParams& params) { canvas->drawIRect(rect, paint); canvas->restore(); if (gr_context_) { - gr_context_->flush(); + gr_context_->flushAndSubmit(); glFinish(); } wl_surface_damage(child_surface.get(), 0, 0, kSubsurfaceWidth, @@ -114,7 +114,7 @@ void SubSurfaceClient::Run(const ClientBase::InitParams& params) { static const SkColor kColors[] = {SK_ColorRED, SK_ColorBLACK}; canvas->clear(kColors[frame_count % base::size(kColors)]); if (gr_context_) { - gr_context_->flush(); + gr_context_->flushAndSubmit(); glFinish(); } wl_surface_set_buffer_scale(surface_.get(), scale_); diff --git a/chromium/components/exo/wayland/clients/yuv.cc b/chromium/components/exo/wayland/clients/yuv.cc index 026c8091448..e626a58f129 100644 --- a/chromium/components/exo/wayland/clients/yuv.cc +++ b/chromium/components/exo/wayland/clients/yuv.cc @@ -9,6 +9,7 @@ #include "base/at_exit.h" #include "base/command_line.h" +#include "base/logging.h" #include "base/message_loop/message_pump_type.h" #include "base/task/single_thread_task_executor.h" #include "components/exo/wayland/clients/client_base.h" diff --git a/chromium/components/exo/wayland/server.cc b/chromium/components/exo/wayland/server.cc index 281e49f7876..7aadc0b13fc 100644 --- a/chromium/components/exo/wayland/server.cc +++ b/chromium/components/exo/wayland/server.cc @@ -28,6 +28,7 @@ #include <vsync-feedback-unstable-v1-server-protocol.h> #include <wayland-server-core.h> #include <wayland-server-protocol-core.h> +#include <xdg-shell-server-protocol.h> #include <xdg-shell-unstable-v6-server-protocol.h> #include <memory> @@ -60,6 +61,7 @@ #if defined(OS_CHROMEOS) #include "components/exo/wayland/wl_shell.h" +#include "components/exo/wayland/xdg_shell.h" #include "components/exo/wayland/zcr_color_space.h" #include "components/exo/wayland/zcr_cursor_shapes.h" #include "components/exo/wayland/zcr_gaming_input.h" @@ -79,6 +81,7 @@ #if defined(USE_OZONE) #include <linux-dmabuf-unstable-v1-server-protocol.h> #include "components/exo/wayland/zwp_linux_dmabuf.h" +#include "ui/ozone/public/ozone_platform.h" #endif #if defined(USE_FULLSCREEN_SHELL) @@ -104,6 +107,18 @@ const base::FilePath::CharType kSocketName[] = FILE_PATH_LITERAL("wayland-0"); // Group used for wayland socket. const char kWaylandSocketGroup[] = "wayland"; +bool IsDrmAtomicAvailable() { +#if defined(USE_OZONE) + auto& host_properties = + ui::OzonePlatform::GetInstance()->GetInitializedHostProperties(); + return host_properties.supports_overlays; +#else + LOG(WARNING) << "Ozone disabled, cannot determine whether DrmAtomic is " + "present. Assuming it is not"; + return false; +#endif +} + } // namespace //////////////////////////////////////////////////////////////////////////////// @@ -150,9 +165,14 @@ Server::Server(Display* display) wl_global_create(wl_display_.get(), &wl_seat_interface, kWlSeatVersion, seat_data_.get(), bind_seat); - wl_global_create(wl_display_.get(), - &zwp_linux_explicit_synchronization_v1_interface, 1, - display_, bind_linux_explicit_synchronization); + if (IsDrmAtomicAvailable()) { + // The release fence needed by linux-explicit-sync comes from DRM-atomic. + // If DRM atomic is not supported, linux-explicit-sync interface is + // disabled. + wl_global_create(wl_display_.get(), + &zwp_linux_explicit_synchronization_v1_interface, 1, + display_, bind_linux_explicit_synchronization); + } wl_global_create(wl_display_.get(), &zaura_shell_interface, kZAuraShellVersion, display_, bind_aura_shell); #if defined(OS_CHROMEOS) @@ -192,10 +212,15 @@ Server::Server(Display* display) wl_global_create(wl_display_.get(), &zwp_text_input_manager_v1_interface, 1, zwp_text_manager_data_.get(), bind_text_input_manager); + zxdg_shell_data_ = + std::make_unique<WaylandZxdgShell>(display_, serial_tracker_.get()); + wl_global_create(wl_display_.get(), &zxdg_shell_v6_interface, 1, + zxdg_shell_data_.get(), bind_zxdg_shell_v6); + xdg_shell_data_ = std::make_unique<WaylandXdgShell>(display_, serial_tracker_.get()); - wl_global_create(wl_display_.get(), &zxdg_shell_v6_interface, 1, - xdg_shell_data_.get(), bind_xdg_shell_v6); + wl_global_create(wl_display_.get(), &xdg_wm_base_interface, 1, + xdg_shell_data_.get(), bind_xdg_shell); #endif #if defined(USE_FULLSCREEN_SHELL) diff --git a/chromium/components/exo/wayland/server.h b/chromium/components/exo/wayland/server.h index 3f36907b9af..971164e2e00 100644 --- a/chromium/components/exo/wayland/server.h +++ b/chromium/components/exo/wayland/server.h @@ -25,6 +25,7 @@ class WaylandDisplayOutput; struct WaylandSeat; struct WaylandTextInputManager; struct WaylandXdgShell; +struct WaylandZxdgShell; // This class is a thin wrapper around a Wayland display server. All Wayland // requests are dispatched into the given Exosphere display. @@ -67,6 +68,7 @@ class Server : public display::DisplayObserver { #if defined(OS_CHROMEOS) std::unique_ptr<WaylandTextInputManager> zwp_text_manager_data_; + std::unique_ptr<WaylandZxdgShell> zxdg_shell_data_; std::unique_ptr<WaylandXdgShell> xdg_shell_data_; #endif diff --git a/chromium/components/exo/wayland/wayland_display_observer.cc b/chromium/components/exo/wayland/wayland_display_observer.cc index 850781e00e2..6633ddf7c17 100644 --- a/chromium/components/exo/wayland/wayland_display_observer.cc +++ b/chromium/components/exo/wayland/wayland_display_observer.cc @@ -8,6 +8,7 @@ #include <string> +#include "components/exo/wayland/wayland_display_output.h" #include "components/exo/wm_helper.h" #include "ui/display/manager/managed_display_info.h" #include "ui/display/screen.h" @@ -15,14 +16,16 @@ namespace exo { namespace wayland { -WaylandDisplayObserver::WaylandDisplayObserver(int64_t id, +WaylandDisplayObserver::WaylandDisplayObserver(WaylandDisplayOutput* output, wl_resource* output_resource) - : id_(id), output_resource_(output_resource) { + : output_(output), output_resource_(output_resource) { + output_->RegisterOutput(output_resource_); display::Screen::GetScreen()->AddObserver(this); SendDisplayMetrics(); } WaylandDisplayObserver::~WaylandDisplayObserver() { + output_->UnregisterOutput(output_resource_); display::Screen::GetScreen()->RemoveObserver(this); } @@ -39,7 +42,7 @@ bool WaylandDisplayObserver::HasScaleObserver() const { void WaylandDisplayObserver::OnDisplayMetricsChanged( const display::Display& display, uint32_t changed_metrics) { - if (id_ != display.id()) + if (output_->id() != display.id()) return; // There is no need to check DISPLAY_METRIC_PRIMARY because when primary @@ -59,8 +62,8 @@ void WaylandDisplayObserver::OnDisplayMetricsChanged( void WaylandDisplayObserver::SendDisplayMetrics() { display::Display display; - bool rv = - display::Screen::GetScreen()->GetDisplayWithDisplayId(id_, &display); + bool rv = display::Screen::GetScreen()->GetDisplayWithDisplayId(output_->id(), + &display); DCHECK(rv); const display::ManagedDisplayInfo& info = diff --git a/chromium/components/exo/wayland/wayland_display_observer.h b/chromium/components/exo/wayland/wayland_display_observer.h index 9bd126651f6..fcd67650b11 100644 --- a/chromium/components/exo/wayland/wayland_display_observer.h +++ b/chromium/components/exo/wayland/wayland_display_observer.h @@ -15,6 +15,7 @@ struct wl_resource; namespace exo { namespace wayland { +class WaylandDisplayOutput; class WaylandDisplayObserver : public display::DisplayObserver { public: @@ -28,7 +29,8 @@ class WaylandDisplayObserver : public display::DisplayObserver { virtual ~ScaleObserver() {} }; - WaylandDisplayObserver(int64_t id, wl_resource* output_resource); + WaylandDisplayObserver(WaylandDisplayOutput* output, + wl_resource* output_resource); ~WaylandDisplayObserver() override; void SetScaleObserver(base::WeakPtr<ScaleObserver> scale_observer); bool HasScaleObserver() const; @@ -43,8 +45,8 @@ class WaylandDisplayObserver : public display::DisplayObserver { // compensate for the rotation of an output device. wl_output_transform OutputTransform(display::Display::Rotation rotation); - // The ID of the display being observed. - const int64_t id_; + // Output. + WaylandDisplayOutput* output_; // The output resource associated with the display. wl_resource* const output_resource_; diff --git a/chromium/components/exo/wayland/wayland_display_output.cc b/chromium/components/exo/wayland/wayland_display_output.cc index 6d451f4d6c0..560cf5a08c5 100644 --- a/chromium/components/exo/wayland/wayland_display_output.cc +++ b/chromium/components/exo/wayland/wayland_display_output.cc @@ -7,12 +7,22 @@ #include <wayland-server-core.h> #include <wayland-server-protocol-core.h> +#include "base/logging.h" +#include "base/stl_util.h" +#include "components/exo/surface.h" +#include "components/exo/wayland/server_util.h" + namespace exo { namespace wayland { WaylandDisplayOutput::WaylandDisplayOutput(int64_t id) : id_(id) {} WaylandDisplayOutput::~WaylandDisplayOutput() { + // Empty the output_ids_ so that Unregister will be no op. + auto ids = std::move(output_ids_); + for (auto pair : ids) + wl_resource_destroy(pair.second); + if (global_) wl_global_destroy(global_); } @@ -25,5 +35,24 @@ void WaylandDisplayOutput::set_global(wl_global* global) { global_ = global; } +void WaylandDisplayOutput::UnregisterOutput(wl_resource* output_resource) { + base::EraseIf(output_ids_, [output_resource](auto& pair) { + return pair.second == output_resource; + }); +} + +void WaylandDisplayOutput::RegisterOutput(wl_resource* output_resource) { + auto* client = wl_resource_get_client(output_resource); + output_ids_.insert(std::make_pair(client, output_resource)); +} + +wl_resource* WaylandDisplayOutput::GetOutputResourceForClient( + wl_client* client) { + auto iter = output_ids_.find(client); + if (iter == output_ids_.end()) + return nullptr; + return iter->second; +} + } // namespace wayland } // namespace exo diff --git a/chromium/components/exo/wayland/wayland_display_output.h b/chromium/components/exo/wayland/wayland_display_output.h index 711fef685f1..eeae307b040 100644 --- a/chromium/components/exo/wayland/wayland_display_output.h +++ b/chromium/components/exo/wayland/wayland_display_output.h @@ -7,9 +7,12 @@ #include <stdint.h> +#include "base/containers/flat_map.h" #include "base/macros.h" +struct wl_client; struct wl_global; +struct wl_resource; namespace exo { namespace wayland { @@ -18,15 +21,23 @@ namespace wayland { // and associated with a global. class WaylandDisplayOutput { public: - explicit WaylandDisplayOutput(int64_t id); + explicit WaylandDisplayOutput(int64_t display_id); ~WaylandDisplayOutput(); int64_t id() const; void set_global(wl_global* global); + // Register/Unregister output resources, which will be used to + // notify surface when enter/leave the output. + void UnregisterOutput(wl_resource* output_resource); + void RegisterOutput(wl_resource* output_resource); + + wl_resource* GetOutputResourceForClient(wl_client* client); + private: const int64_t id_; wl_global* global_ = nullptr; + base::flat_map<wl_client*, wl_resource*> output_ids_; DISALLOW_COPY_AND_ASSIGN(WaylandDisplayOutput); }; diff --git a/chromium/components/exo/wayland/wayland_positioner.cc b/chromium/components/exo/wayland/wayland_positioner.cc index 625fad4a5e8..65b9ec694e0 100644 --- a/chromium/components/exo/wayland/wayland_positioner.cc +++ b/chromium/components/exo/wayland/wayland_positioner.cc @@ -4,28 +4,129 @@ #include "components/exo/wayland/wayland_positioner.h" +#include <xdg-shell-unstable-v6-server-protocol.h> + namespace exo { namespace wayland { namespace { -// Represents the 1-dimensional projection of the gravity/anchor values. -enum Direction { kNegative = -1, kNeutral = 0, kPositive = 1 }; +std::pair<WaylandPositioner::Direction, WaylandPositioner::Direction> +DecomposeUnstableAnchor(uint32_t anchor) { + WaylandPositioner::Direction x, y; + + if (anchor & ZXDG_POSITIONER_V6_ANCHOR_LEFT) { + x = WaylandPositioner::Direction::kNegative; + } else if (anchor & ZXDG_POSITIONER_V6_ANCHOR_RIGHT) { + x = WaylandPositioner::Direction::kPositive; + } else { + x = WaylandPositioner::Direction::kNeutral; + } + + if (anchor & ZXDG_POSITIONER_V6_ANCHOR_TOP) { + y = WaylandPositioner::Direction::kNegative; + } else if (anchor & ZXDG_POSITIONER_V6_ANCHOR_BOTTOM) { + y = WaylandPositioner::Direction::kPositive; + } else { + y = WaylandPositioner::Direction::kNeutral; + } + + return std::make_pair(x, y); +} + +std::pair<WaylandPositioner::Direction, WaylandPositioner::Direction> +DecomposeStableAnchor(uint32_t anchor) { + switch (anchor) { + default: + case XDG_POSITIONER_ANCHOR_NONE: + return std::make_pair(WaylandPositioner::Direction::kNeutral, + WaylandPositioner::Direction::kNeutral); + case XDG_POSITIONER_ANCHOR_TOP: + return std::make_pair(WaylandPositioner::Direction::kNeutral, + WaylandPositioner::Direction::kNegative); + case XDG_POSITIONER_ANCHOR_BOTTOM: + return std::make_pair(WaylandPositioner::Direction::kNeutral, + WaylandPositioner::Direction::kPositive); + case XDG_POSITIONER_ANCHOR_LEFT: + return std::make_pair(WaylandPositioner::Direction::kNegative, + WaylandPositioner::Direction::kNeutral); + case XDG_POSITIONER_ANCHOR_RIGHT: + return std::make_pair(WaylandPositioner::Direction::kPositive, + WaylandPositioner::Direction::kNeutral); + case XDG_POSITIONER_ANCHOR_TOP_LEFT: + return std::make_pair(WaylandPositioner::Direction::kNegative, + WaylandPositioner::Direction::kNegative); + case XDG_POSITIONER_ANCHOR_TOP_RIGHT: + return std::make_pair(WaylandPositioner::Direction::kPositive, + WaylandPositioner::Direction::kNegative); + case XDG_POSITIONER_ANCHOR_BOTTOM_LEFT: + return std::make_pair(WaylandPositioner::Direction::kNegative, + WaylandPositioner::Direction::kPositive); + case XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT: + return std::make_pair(WaylandPositioner::Direction::kPositive, + WaylandPositioner::Direction::kPositive); + } +} + +std::pair<WaylandPositioner::Direction, WaylandPositioner::Direction> +DecomposeUnstableGravity(uint32_t gravity) { + WaylandPositioner::Direction x, y; + + if (gravity & ZXDG_POSITIONER_V6_GRAVITY_LEFT) { + x = WaylandPositioner::Direction::kNegative; + } else if (gravity & ZXDG_POSITIONER_V6_GRAVITY_RIGHT) { + x = WaylandPositioner::Direction::kPositive; + } else { + x = WaylandPositioner::Direction::kNeutral; + } + + if (gravity & ZXDG_POSITIONER_V6_GRAVITY_TOP) { + y = WaylandPositioner::Direction::kNegative; + } else if (gravity & ZXDG_POSITIONER_V6_GRAVITY_BOTTOM) { + y = WaylandPositioner::Direction::kPositive; + } else { + y = WaylandPositioner::Direction::kNeutral; + } -static Direction Flip(Direction d) { - return (Direction)-d; + return std::make_pair(x, y); +} + +std::pair<WaylandPositioner::Direction, WaylandPositioner::Direction> +DecomposeStableGravity(uint32_t gravity) { + switch (gravity) { + default: + case XDG_POSITIONER_GRAVITY_NONE: + return std::make_pair(WaylandPositioner::Direction::kNeutral, + WaylandPositioner::Direction::kNeutral); + case XDG_POSITIONER_GRAVITY_TOP: + return std::make_pair(WaylandPositioner::Direction::kNeutral, + WaylandPositioner::Direction::kNegative); + case XDG_POSITIONER_GRAVITY_BOTTOM: + return std::make_pair(WaylandPositioner::Direction::kNeutral, + WaylandPositioner::Direction::kPositive); + case XDG_POSITIONER_GRAVITY_LEFT: + return std::make_pair(WaylandPositioner::Direction::kNegative, + WaylandPositioner::Direction::kNeutral); + case XDG_POSITIONER_GRAVITY_RIGHT: + return std::make_pair(WaylandPositioner::Direction::kPositive, + WaylandPositioner::Direction::kNeutral); + case XDG_POSITIONER_GRAVITY_TOP_LEFT: + return std::make_pair(WaylandPositioner::Direction::kNegative, + WaylandPositioner::Direction::kNegative); + case XDG_POSITIONER_GRAVITY_TOP_RIGHT: + return std::make_pair(WaylandPositioner::Direction::kPositive, + WaylandPositioner::Direction::kNegative); + case XDG_POSITIONER_GRAVITY_BOTTOM_LEFT: + return std::make_pair(WaylandPositioner::Direction::kNegative, + WaylandPositioner::Direction::kPositive); + case XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT: + return std::make_pair(WaylandPositioner::Direction::kPositive, + WaylandPositioner::Direction::kPositive); + } } -// Decodes a masked anchor/gravity bitfield to the direction. -Direction MaskToDirection(uint32_t field, - uint32_t negative_mask, - uint32_t positive_mask) { - DCHECK(!((field & negative_mask) && (field & positive_mask))); - if (field & negative_mask) - return kNegative; - if (field & positive_mask) - return kPositive; - return kNeutral; +static WaylandPositioner::Direction Flip(WaylandPositioner::Direction d) { + return (WaylandPositioner::Direction)-d; } // Represents the possible/actual positioner adjustments for this window. @@ -63,8 +164,8 @@ Range1D Calculate(const ConstraintAdjustment& adjustments, Range1D anchor_range, uint32_t size, int32_t offset, - Direction anchor, - Direction gravity) { + WaylandPositioner::Direction anchor, + WaylandPositioner::Direction gravity) { if (adjustments.flip) { return Calculate({/*flip=*/false, adjustments.slide, adjustments.resize}, work_size, anchor_range, size, -offset, Flip(anchor), @@ -91,25 +192,25 @@ Range1D Calculate(const ConstraintAdjustment& adjustments, int32_t start = offset; switch (anchor) { - case Direction::kNegative: + case WaylandPositioner::Direction::kNegative: start += anchor_range.start; break; - case Direction::kNeutral: + case WaylandPositioner::Direction::kNeutral: start += anchor_range.center(); break; - case Direction::kPositive: + case WaylandPositioner::Direction::kPositive: start += anchor_range.end; break; } switch (gravity) { - case Direction::kNegative: + case WaylandPositioner::Direction::kNegative: start -= size; break; - case Direction::kNeutral: + case WaylandPositioner::Direction::kNeutral: start -= size / 2; break; - case Direction::kPositive: + case WaylandPositioner::Direction::kPositive: break; } return {start, start + size}; @@ -124,8 +225,8 @@ std::pair<Range1D, ConstraintAdjustment> DetermineBestConstraintAdjustment( const Range1D& anchor_range, uint32_t size, int32_t offset, - Direction anchor, - Direction gravity, + WaylandPositioner::Direction anchor, + WaylandPositioner::Direction gravity, const ConstraintAdjustment& valid_adjustments) { if (work_area.start != 0) { int32_t shift = -work_area.start; @@ -174,29 +275,47 @@ std::pair<Range1D, ConstraintAdjustment> DetermineBestConstraintAdjustment( } // namespace +void WaylandPositioner::SetAnchor(uint32_t anchor) { + std::pair<WaylandPositioner::Direction, WaylandPositioner::Direction> + decompose; + if (version_ == UNSTABLE) { + decompose = DecomposeUnstableAnchor(anchor); + } else { + decompose = DecomposeStableAnchor(anchor); + } + anchor_x_ = decompose.first; + anchor_y_ = decompose.second; +} + +void WaylandPositioner::SetGravity(uint32_t gravity) { + std::pair<WaylandPositioner::Direction, WaylandPositioner::Direction> + decompose; + if (version_ == UNSTABLE) { + decompose = DecomposeUnstableGravity(gravity); + } else { + decompose = DecomposeStableGravity(gravity); + } + gravity_x_ = decompose.first; + gravity_y_ = decompose.second; +} + WaylandPositioner::Result WaylandPositioner::CalculatePosition( const gfx::Rect& work_area, bool flip_x, bool flip_y) const { - Direction anchor_x = MaskToDirection(anchor_, ZXDG_POSITIONER_V6_ANCHOR_LEFT, - ZXDG_POSITIONER_V6_ANCHOR_RIGHT); - Direction anchor_y = MaskToDirection(anchor_, ZXDG_POSITIONER_V6_ANCHOR_TOP, - ZXDG_POSITIONER_V6_ANCHOR_BOTTOM); - Direction gravity_x = - MaskToDirection(gravity_, ZXDG_POSITIONER_V6_GRAVITY_LEFT, - ZXDG_POSITIONER_V6_GRAVITY_RIGHT); - Direction gravity_y = - MaskToDirection(gravity_, ZXDG_POSITIONER_V6_GRAVITY_TOP, - ZXDG_POSITIONER_V6_GRAVITY_BOTTOM); + auto anchor_x = anchor_x_; + auto anchor_y = anchor_y_; + auto gravity_x = gravity_x_; + auto gravity_y = gravity_y_; ConstraintAdjustment adjustments_x = MaskToConstraintAdjustment( - adjustment_, ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_X, - ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_X, - ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_X); + adjustment_, XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X, + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X, + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X); ConstraintAdjustment adjustments_y = MaskToConstraintAdjustment( - adjustment_, ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_Y, - ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_Y, - ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_Y); + adjustment_, XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y, + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y, + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y); int32_t offset_x = offset_.x(); int32_t offset_y = offset_.y(); diff --git a/chromium/components/exo/wayland/wayland_positioner.h b/chromium/components/exo/wayland/wayland_positioner.h index a623e0d4326..ccd5427838f 100644 --- a/chromium/components/exo/wayland/wayland_positioner.h +++ b/chromium/components/exo/wayland/wayland_positioner.h @@ -5,7 +5,7 @@ #ifndef COMPONENTS_EXO_WAYLAND_WAYLAND_POSITIONER_H_ #define COMPONENTS_EXO_WAYLAND_WAYLAND_POSITIONER_H_ -#include <xdg-shell-unstable-v6-server-protocol.h> +#include <xdg-shell-server-protocol.h> #include "base/macros.h" #include "ui/gfx/geometry/point.h" @@ -26,7 +26,14 @@ class WaylandPositioner { bool y_flipped; }; - WaylandPositioner() = default; + // Represents the 1-dimensional projection of the gravity/anchor values. + enum Direction { kNegative = -1, kNeutral = 0, kPositive = 1 }; + + // Controls whether anchor and gravity are set using the unstable bitfields or + // the stable enums. + enum Version { UNSTABLE, STABLE }; + + WaylandPositioner(Version v) : version_(v) {} // Calculate and return position from current state. Result CalculatePosition(const gfx::Rect& work_area, @@ -39,27 +46,31 @@ class WaylandPositioner { anchor_rect_ = std::move(anchor_rect); } - void SetAnchor(uint32_t anchor) { anchor_ = anchor; } + void SetAnchor(uint32_t anchor); - void SetGravity(uint32_t gravity) { gravity_ = gravity; } + void SetGravity(uint32_t gravity); void SetAdjustment(uint32_t adjustment) { adjustment_ = adjustment; } void SetOffset(gfx::Vector2d offset) { offset_ = std::move(offset); } private: + Version version_; + gfx::Size size_; gfx::Rect anchor_rect_; - uint32_t anchor_ = ZXDG_POSITIONER_V6_ANCHOR_NONE; + Direction anchor_x_ = kNeutral; + Direction anchor_y_ = kNeutral; - uint32_t gravity_ = ZXDG_POSITIONER_V6_GRAVITY_NONE; + Direction gravity_x_ = kNeutral; + Direction gravity_y_ = kNeutral; // A bitmask that defines the subset of modifications to the position/size // that are allowed, see zxdg_positioner.constraint_adjustment() for more // details. - uint32_t adjustment_ = ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_NONE; + uint32_t adjustment_ = XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_NONE; // Defines an absolute translation (i.e. unaffected by flipping, scaling or // resizing) for the placement of the window relative to the |anchor_rect_|. diff --git a/chromium/components/exo/wayland/wayland_positioner_unittest.cc b/chromium/components/exo/wayland/wayland_positioner_unittest.cc index 035f3b91f90..c30ea99d7db 100644 --- a/chromium/components/exo/wayland/wayland_positioner_unittest.cc +++ b/chromium/components/exo/wayland/wayland_positioner_unittest.cc @@ -5,6 +5,7 @@ #include "components/exo/wayland/wayland_positioner.h" #include "testing/gtest/include/gtest/gtest.h" +#include "xdg-shell-server-protocol.h" #include "xdg-shell-unstable-v6-server-protocol.h" namespace exo { @@ -21,7 +22,9 @@ class WaylandPositionerTest : public testing::Test { bool flip_x = false; bool flip_y = false; - TestCaseBuilder() { positioner.SetAnchorRect({2, 2, 1, 1}); } + explicit TestCaseBuilder(WaylandPositioner::Version v) : positioner(v) { + positioner.SetAnchorRect({2, 2, 1, 1}); + } TestCaseBuilder& SetFlipState(bool x, bool y) { flip_x = x; @@ -66,31 +69,35 @@ class WaylandPositionerTest : public testing::Test { }; }; -TEST_F(WaylandPositionerTest, UnconstrainedCases) { +// Tests for the unstable protocol. + +TEST_F(WaylandPositionerTest, UnconstrainedCasesUnstable) { // No gravity or anchor. - EXPECT_EQ(TestCaseBuilder().SetSize(1, 1).SolveToRect(), + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::UNSTABLE) + .SetSize(1, 1) + .SolveToRect(), gfx::Rect(2, 2, 1, 1)); // Anchor without gravity. - EXPECT_EQ(TestCaseBuilder() + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::UNSTABLE) .SetSize(2, 1) .SetAnchor(ZXDG_POSITIONER_V6_ANCHOR_RIGHT) .SolveToRect(), gfx::Rect(2, 2, 2, 1)); - EXPECT_EQ(TestCaseBuilder() + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::UNSTABLE) .SetSize(2, 1) .SetAnchor(ZXDG_POSITIONER_V6_ANCHOR_LEFT) .SolveToRect(), gfx::Rect(1, 2, 2, 1)); // Gravity without anchor. - EXPECT_EQ(TestCaseBuilder() + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::UNSTABLE) .SetSize(1, 2) .SetAnchorRect(2, 2, 0, 0) .SetGravity(ZXDG_POSITIONER_V6_GRAVITY_TOP) .SolveToRect(), gfx::Rect(2, 0, 1, 2)); - EXPECT_EQ(TestCaseBuilder() + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::UNSTABLE) .SetSize(1, 2) .SetAnchorRect(2, 2, 0, 0) .SetGravity(ZXDG_POSITIONER_V6_GRAVITY_BOTTOM) @@ -98,7 +105,7 @@ TEST_F(WaylandPositionerTest, UnconstrainedCases) { gfx::Rect(2, 2, 1, 2)); // Gravity + anchor in the same direction. - EXPECT_EQ(TestCaseBuilder() + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::UNSTABLE) .SetSize(2, 2) .SetGravity(ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | ZXDG_POSITIONER_V6_GRAVITY_LEFT) @@ -108,7 +115,7 @@ TEST_F(WaylandPositionerTest, UnconstrainedCases) { gfx::Rect(0, 3, 2, 2)); // Gravity + anchor in opposing directions. - EXPECT_EQ(TestCaseBuilder() + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::UNSTABLE) .SetSize(2, 2) .SetGravity(ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | ZXDG_POSITIONER_V6_GRAVITY_LEFT) @@ -118,8 +125,8 @@ TEST_F(WaylandPositionerTest, UnconstrainedCases) { gfx::Rect(1, 2, 2, 2)); } -TEST_F(WaylandPositionerTest, FlipSlideResizePriority) { - TestCaseBuilder builder; +TEST_F(WaylandPositionerTest, FlipSlideResizePriorityUnstable) { + TestCaseBuilder builder{WaylandPositioner::Version::UNSTABLE}; builder.SetAnchorRect(4, 4, 0, 0) .SetSize(2, 2) .SetGravity(ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | @@ -150,10 +157,10 @@ TEST_F(WaylandPositionerTest, FlipSlideResizePriority) { gfx::Rect(4, 4, 1, 1)); } -TEST_F(WaylandPositionerTest, TriesToMaximizeArea) { +TEST_F(WaylandPositionerTest, TriesToMaximizeAreaUnstable) { // The size is too large to fit where the anchor is. WaylandPositioner::Result result = - TestCaseBuilder() + TestCaseBuilder(WaylandPositioner::Version::UNSTABLE) .SetAnchorRect(2, 4, 0, 0) .SetSize(4, 10) .SetGravity(ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | @@ -171,9 +178,9 @@ TEST_F(WaylandPositionerTest, TriesToMaximizeArea) { EXPECT_FALSE(result.y_flipped); } -TEST_F(WaylandPositionerTest, PropagatesAnInitialFlip) { +TEST_F(WaylandPositionerTest, PropagatesAnInitialFlipUnstable) { WaylandPositioner::Result result = - TestCaseBuilder() + TestCaseBuilder(WaylandPositioner::Version::UNSTABLE) .SetAnchorRect(3, 1, 0, 0) .SetSize(2, 2) .SetGravity(ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | @@ -195,8 +202,8 @@ TEST_F(WaylandPositionerTest, PropagatesAnInitialFlip) { // This is a common case for dropdown menus. In ChromeOS we do not let them // slide if they might occlude the anchor rectangle. For this case, x axis does // slide but the y axis resized instead. -TEST_F(WaylandPositionerTest, PreventsSlidingThatOccludesAnchorRect) { - EXPECT_EQ(TestCaseBuilder() +TEST_F(WaylandPositionerTest, PreventsSlidingThatOccludesAnchorRectUnstable) { + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::UNSTABLE) .SetSize(3, 3) .SetGravity(ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | ZXDG_POSITIONER_V6_GRAVITY_RIGHT) @@ -208,7 +215,7 @@ TEST_F(WaylandPositionerTest, PreventsSlidingThatOccludesAnchorRect) { // Here we ensure that the 4x4 popup does slide, which is allowed because // the anchor rect is already occluded. - EXPECT_EQ(TestCaseBuilder() + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::UNSTABLE) .SetSize(4, 4) .SetGravity(ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | ZXDG_POSITIONER_V6_GRAVITY_RIGHT) @@ -219,6 +226,145 @@ TEST_F(WaylandPositionerTest, PreventsSlidingThatOccludesAnchorRect) { gfx::Rect(1, 1, 4, 4)); } +// Tests for the stable protocol. + +TEST_F(WaylandPositionerTest, UnconstrainedCases) { + // No gravity or anchor. + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE) + .SetSize(1, 1) + .SolveToRect(), + gfx::Rect(2, 2, 1, 1)); + + // Anchor without gravity. + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE) + .SetSize(2, 1) + .SetAnchor(XDG_POSITIONER_ANCHOR_RIGHT) + .SolveToRect(), + gfx::Rect(2, 2, 2, 1)); + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE) + .SetSize(2, 1) + .SetAnchor(XDG_POSITIONER_ANCHOR_LEFT) + .SolveToRect(), + gfx::Rect(1, 2, 2, 1)); + + // Gravity without anchor. + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE) + .SetSize(1, 2) + .SetAnchorRect(2, 2, 0, 0) + .SetGravity(XDG_POSITIONER_GRAVITY_TOP) + .SolveToRect(), + gfx::Rect(2, 0, 1, 2)); + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE) + .SetSize(1, 2) + .SetAnchorRect(2, 2, 0, 0) + .SetGravity(XDG_POSITIONER_GRAVITY_BOTTOM) + .SolveToRect(), + gfx::Rect(2, 2, 1, 2)); + + // Gravity + anchor in the same direction. + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE) + .SetSize(2, 2) + .SetGravity(XDG_POSITIONER_GRAVITY_BOTTOM_LEFT) + .SetAnchor(XDG_POSITIONER_ANCHOR_BOTTOM_LEFT) + .SolveToRect(), + gfx::Rect(0, 3, 2, 2)); + + // Gravity + anchor in opposing directions. + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE) + .SetSize(2, 2) + .SetGravity(XDG_POSITIONER_GRAVITY_BOTTOM_LEFT) + .SetAnchor(XDG_POSITIONER_ANCHOR_TOP_RIGHT) + .SolveToRect(), + gfx::Rect(1, 2, 2, 2)); +} + +TEST_F(WaylandPositionerTest, FlipSlideResizePriority) { + TestCaseBuilder builder{WaylandPositioner::Version::STABLE}; + builder.SetAnchorRect(4, 4, 0, 0) + .SetSize(2, 2) + .SetGravity(XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT) + .SetAnchor(XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT); + // Flip is enabled, so the result will be at 2,2 (i.e. flipping a 2-wide + // square around 4,4). + EXPECT_EQ(builder.SetAdjustment(~XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_NONE) + .SolveToRect(), + gfx::Rect(2, 2, 2, 2)); + // If we cant flip on an axis, that axis will slide to 3 instead. + EXPECT_EQ(builder.SetAdjustment(~XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X) + .SolveToRect(), + gfx::Rect(3, 2, 2, 2)); + EXPECT_EQ(builder.SetAdjustment(~XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y) + .SolveToRect(), + gfx::Rect(2, 3, 2, 2)); + // If we cant flip or slide, we resize. + EXPECT_EQ(builder + .SetAdjustment(XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X | + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y) + .SolveToRect(), + gfx::Rect(4, 4, 1, 1)); +} + +TEST_F(WaylandPositionerTest, TriesToMaximizeArea) { + // The size is too large to fit where the anchor is. + WaylandPositioner::Result result = + TestCaseBuilder(WaylandPositioner::Version::STABLE) + .SetAnchorRect(2, 4, 0, 0) + .SetSize(4, 10) + .SetGravity(XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT) + .SetAnchor(XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT) + .SetAdjustment(~XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_NONE) + .Solve(); + // We can slide to 1 on x, but we must resize on y (after sliding to 0). + EXPECT_EQ(result.origin, gfx::Point(1, 0)); + // The x size will be preserved but y shrinks to the work area. + EXPECT_EQ(result.size, gfx::Size(4, 5)); + // Neither axis will be flipped. + EXPECT_FALSE(result.x_flipped); + EXPECT_FALSE(result.y_flipped); +} + +TEST_F(WaylandPositionerTest, PropagatesAnInitialFlip) { + WaylandPositioner::Result result = + TestCaseBuilder(WaylandPositioner::Version::STABLE) + .SetAnchorRect(3, 1, 0, 0) + .SetSize(2, 2) + .SetGravity(XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT) + .SetAnchor(XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT) + .SetAdjustment(~XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_NONE) + .SetFlipState(true, true) + .Solve(); + // With a propagated flip state: + // - Normally the x would not need to flip, but it propagates the flip. + // - Y also propagates, but that makes it constrained so it flips back. + EXPECT_EQ(result.origin, gfx::Point(1, 1)); + EXPECT_EQ(result.size, gfx::Size(2, 2)); + EXPECT_TRUE(result.x_flipped); + EXPECT_FALSE(result.y_flipped); +} + +// This is a common case for dropdown menus. In ChromeOS we do not let them +// slide if they might occlude the anchor rectangle. For this case, x axis does +// slide but the y axis resized instead. +TEST_F(WaylandPositionerTest, PreventsSlidingThatOccludesAnchorRect) { + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE) + .SetSize(3, 3) + .SetGravity(XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT) + .SetAnchor(XDG_POSITIONER_ANCHOR_BOTTOM_LEFT) + .SetAdjustment(~XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_NONE) + .SolveToRect(), + gfx::Rect(2, 3, 3, 2)); + + // Here we ensure that the 4x4 popup does slide, which is allowed because + // the anchor rect is already occluded. + EXPECT_EQ(TestCaseBuilder(WaylandPositioner::Version::STABLE) + .SetSize(4, 4) + .SetGravity(XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT) + .SetAnchor(XDG_POSITIONER_ANCHOR_TOP_LEFT) + .SetAdjustment(~XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_NONE) + .SolveToRect(), + gfx::Rect(1, 1, 4, 4)); +} + } // namespace } // namespace wayland } // namespace exo diff --git a/chromium/components/exo/wayland/wl_compositor.cc b/chromium/components/exo/wayland/wl_compositor.cc index 9096d2b87d6..0f01baee969 100644 --- a/chromium/components/exo/wayland/wl_compositor.cc +++ b/chromium/components/exo/wayland/wl_compositor.cc @@ -12,14 +12,17 @@ #include "components/exo/buffer.h" #include "components/exo/display.h" #include "components/exo/surface.h" +#include "components/exo/wayland/server.h" #include "components/exo/wayland/server_util.h" #include "third_party/skia/include/core/SkRegion.h" +#include "ui/display/types/display_constants.h" #if defined(OS_CHROMEOS) #include "components/exo/wayland/zwp_linux_explicit_synchronization.h" #endif namespace exo { +class Server; namespace wayland { namespace { @@ -202,8 +205,8 @@ const struct wl_surface_interface surface_implementation = { void compositor_create_surface(wl_client* client, wl_resource* resource, uint32_t id) { - std::unique_ptr<Surface> surface = - GetUserDataAs<Display>(resource)->CreateSurface(); + Display* display = GetUserDataAs<Display>(resource); + std::unique_ptr<Surface> surface = display->CreateSurface(); wl_resource* surface_resource = wl_resource_create( client, &wl_surface_interface, wl_resource_get_version(resource), id); diff --git a/chromium/components/exo/wayland/wl_data_device_manager.cc b/chromium/components/exo/wayland/wl_data_device_manager.cc index b4ee13f4f56..5d08696c85e 100644 --- a/chromium/components/exo/wayland/wl_data_device_manager.cc +++ b/chromium/components/exo/wayland/wl_data_device_manager.cc @@ -303,6 +303,11 @@ class WaylandDataDeviceDelegate : public DataDeviceDelegate { uint32_t serial) { base::Optional<wayland::SerialTracker::EventType> event_type = serial_tracker_->GetEventType(serial); + if (event_type == base::nullopt) { + LOG(ERROR) << "The serial passed to StartDrag does not exist."; + source->Cancelled(); + return; + } if (event_type == wayland::SerialTracker::EventType::POINTER_BUTTON_DOWN && serial_tracker_->GetPointerDownSerial() == serial) { data_device->StartDrag( @@ -314,8 +319,23 @@ class WaylandDataDeviceDelegate : public DataDeviceDelegate { source, origin, icon, ui::DragDropTypes::DragEventSource::DRAG_EVENT_SOURCE_TOUCH); } else { + LOG(ERROR) << "The serial passed to StartDrag does not match its " + "expected types."; + source->Cancelled(); + } + } + + void SetSelection(DataDevice* data_device, + DataSource* source, + uint32_t serial) { + base::Optional<wayland::SerialTracker::EventType> event_type = + serial_tracker_->GetEventType(serial); + if (event_type == base::nullopt) { + LOG(ERROR) << "The serial passed to SetSelection does not exist."; source->Cancelled(); + return; } + data_device->SetSelection(source); } private: @@ -347,10 +367,15 @@ void data_device_start_drag(wl_client* client, void data_device_set_selection(wl_client* client, wl_resource* resource, - wl_resource* data_source, + wl_resource* source_resource, uint32_t serial) { - GetUserDataAs<DataDevice>(resource)->SetSelection( - data_source ? GetUserDataAs<DataSource>(data_source) : nullptr, serial); + DataDevice* data_device = GetUserDataAs<DataDevice>(resource); + static_cast<WaylandDataDeviceDelegate*>(data_device->get_delegate()) + ->SetSelection(data_device, + source_resource + ? GetUserDataAs<DataSource>(source_resource) + : nullptr, + serial); } void data_device_release(wl_client* client, wl_resource* resource) { diff --git a/chromium/components/exo/wayland/wl_output.cc b/chromium/components/exo/wayland/wl_output.cc index b6b80bf515b..b95c1969890 100644 --- a/chromium/components/exo/wayland/wl_output.cc +++ b/chromium/components/exo/wayland/wl_output.cc @@ -28,15 +28,17 @@ void output_release(wl_client* client, wl_resource* resource) { const struct wl_output_interface output_implementation = {output_release}; -void bind_output(wl_client* client, void* data, uint32_t version, uint32_t id) { +void bind_output(wl_client* client, + void* data, + uint32_t version, + uint32_t output_id) { WaylandDisplayOutput* output = static_cast<WaylandDisplayOutput*>(data); - wl_resource* resource = wl_resource_create( - client, &wl_output_interface, std::min(version, kWlOutputVersion), id); - - SetImplementation( - resource, &output_implementation, - std::make_unique<WaylandDisplayObserver>(output->id(), resource)); + wl_resource* resource = + wl_resource_create(client, &wl_output_interface, + std::min(version, kWlOutputVersion), output_id); + SetImplementation(resource, &output_implementation, + std::make_unique<WaylandDisplayObserver>(output, resource)); } } // namespace wayland diff --git a/chromium/components/exo/wayland/xdg_shell.cc b/chromium/components/exo/wayland/xdg_shell.cc new file mode 100644 index 00000000000..8e8f0b4db68 --- /dev/null +++ b/chromium/components/exo/wayland/xdg_shell.cc @@ -0,0 +1,692 @@ +// Copyright 2018 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 "components/exo/wayland/xdg_shell.h" + +#include <wayland-server-core.h> +#include <wayland-server-protocol-core.h> +#include <xdg-shell-server-protocol.h> + +#include "ash/public/cpp/shell_window_ids.h" +#include "ash/public/cpp/window_properties.h" +#include "ash/public/cpp/window_state_type.h" +#include "base/bind.h" +#include "base/strings/utf_string_conversions.h" +#include "components/exo/display.h" +#include "components/exo/wayland/serial_tracker.h" +#include "components/exo/wayland/server_util.h" +#include "components/exo/wayland/wayland_positioner.h" +#include "components/exo/xdg_shell_surface.h" +#include "ui/aura/window_observer.h" +#include "ui/base/hit_test.h" +#include "ui/display/screen.h" +#include "ui/views/widget/widget.h" +#include "ui/wm/core/coordinate_conversion.h" + +namespace exo { +namespace wayland { +namespace { + +//////////////////////////////////////////////////////////////////////////////// +// xdg_positioner_interface: + +void xdg_positioner_destroy(wl_client* client, wl_resource* resource) { + wl_resource_destroy(resource); +} + +void xdg_positioner_set_size(wl_client* client, + wl_resource* resource, + int32_t width, + int32_t height) { + if (width < 1 || height < 1) { + wl_resource_post_error(resource, XDG_POSITIONER_ERROR_INVALID_INPUT, + "width and height must be positive and non-zero"); + return; + } + + GetUserDataAs<WaylandPositioner>(resource)->SetSize(gfx::Size(width, height)); +} + +void xdg_positioner_set_anchor_rect(wl_client* client, + wl_resource* resource, + int32_t x, + int32_t y, + int32_t width, + int32_t height) { + if (width < 1 || height < 1) { + wl_resource_post_error(resource, XDG_POSITIONER_ERROR_INVALID_INPUT, + "width and height must be positive and non-zero"); + return; + } + + GetUserDataAs<WaylandPositioner>(resource)->SetAnchorRect( + gfx::Rect(x, y, width, height)); +} + +void xdg_positioner_set_anchor(wl_client* client, + wl_resource* resource, + uint32_t anchor) { + if (((anchor & XDG_POSITIONER_ANCHOR_LEFT) && + (anchor & XDG_POSITIONER_ANCHOR_RIGHT)) || + ((anchor & XDG_POSITIONER_ANCHOR_TOP) && + (anchor & XDG_POSITIONER_ANCHOR_BOTTOM))) { + wl_resource_post_error(resource, XDG_POSITIONER_ERROR_INVALID_INPUT, + "same-axis values are not allowed"); + return; + } + + GetUserDataAs<WaylandPositioner>(resource)->SetAnchor(anchor); +} + +void xdg_positioner_set_gravity(wl_client* client, + wl_resource* resource, + uint32_t gravity) { + if (((gravity & XDG_POSITIONER_GRAVITY_LEFT) && + (gravity & XDG_POSITIONER_GRAVITY_RIGHT)) || + ((gravity & XDG_POSITIONER_GRAVITY_TOP) && + (gravity & XDG_POSITIONER_GRAVITY_BOTTOM))) { + wl_resource_post_error(resource, XDG_POSITIONER_ERROR_INVALID_INPUT, + "same-axis values are not allowed"); + return; + } + + GetUserDataAs<WaylandPositioner>(resource)->SetGravity(gravity); +} + +void xdg_positioner_set_constraint_adjustment(wl_client* client, + wl_resource* resource, + uint32_t adjustment) { + GetUserDataAs<WaylandPositioner>(resource)->SetAdjustment(adjustment); +} + +void xdg_positioner_set_offset(wl_client* client, + wl_resource* resource, + int32_t x, + int32_t y) { + GetUserDataAs<WaylandPositioner>(resource)->SetOffset(gfx::Vector2d(x, y)); +} + +const struct xdg_positioner_interface xdg_positioner_implementation = { + xdg_positioner_destroy, xdg_positioner_set_size, + xdg_positioner_set_anchor_rect, xdg_positioner_set_anchor, + xdg_positioner_set_gravity, xdg_positioner_set_constraint_adjustment, + xdg_positioner_set_offset}; + +//////////////////////////////////////////////////////////////////////////////// +// xdg_toplevel_interface: + +int XdgToplevelResizeComponent(uint32_t edges) { + switch (edges) { + case XDG_TOPLEVEL_RESIZE_EDGE_TOP: + return HTTOP; + case XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM: + return HTBOTTOM; + case XDG_TOPLEVEL_RESIZE_EDGE_LEFT: + return HTLEFT; + case XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT: + return HTTOPLEFT; + case XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT: + return HTBOTTOMLEFT; + case XDG_TOPLEVEL_RESIZE_EDGE_RIGHT: + return HTRIGHT; + case XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT: + return HTTOPRIGHT; + case XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT: + return HTBOTTOMRIGHT; + default: + return HTBOTTOMRIGHT; + } +} + +using XdgSurfaceConfigureCallback = + base::RepeatingCallback<void(const gfx::Size& size, + ash::WindowStateType state_type, + bool resizing, + bool activated)>; + +uint32_t HandleXdgSurfaceConfigureCallback( + wl_resource* resource, + SerialTracker* serial_tracker, + const XdgSurfaceConfigureCallback& callback, + const gfx::Size& size, + ash::WindowStateType state_type, + bool resizing, + bool activated, + const gfx::Vector2d& origin_offset) { + uint32_t serial = + serial_tracker->GetNextSerial(SerialTracker::EventType::OTHER_EVENT); + callback.Run(size, state_type, resizing, activated); + xdg_surface_send_configure(resource, serial); + wl_client_flush(wl_resource_get_client(resource)); + return serial; +} + +struct WaylandXdgSurface { + WaylandXdgSurface(std::unique_ptr<XdgShellSurface> shell_surface, + SerialTracker* const serial_tracker) + : shell_surface(std::move(shell_surface)), + serial_tracker(serial_tracker) {} + + std::unique_ptr<XdgShellSurface> shell_surface; + + // Owned by Server, which always outlives this surface. + SerialTracker* const serial_tracker; + + DISALLOW_COPY_AND_ASSIGN(WaylandXdgSurface); +}; + +// Wrapper around shell surface that allows us to handle the case where the +// xdg surface resource is destroyed before the toplevel resource. +class WaylandToplevel : public aura::WindowObserver { + public: + WaylandToplevel(wl_resource* resource, wl_resource* surface_resource) + : resource_(resource), + shell_surface_data_( + GetUserDataAs<WaylandXdgSurface>(surface_resource)) { + shell_surface_data_->shell_surface->host_window()->AddObserver(this); + shell_surface_data_->shell_surface->set_close_callback(base::BindRepeating( + &WaylandToplevel::OnClose, weak_ptr_factory_.GetWeakPtr())); + shell_surface_data_->shell_surface->set_configure_callback( + base::BindRepeating( + &HandleXdgSurfaceConfigureCallback, surface_resource, + shell_surface_data_->serial_tracker, + base::BindRepeating(&WaylandToplevel::OnConfigure, + weak_ptr_factory_.GetWeakPtr()))); + } + ~WaylandToplevel() override { + if (shell_surface_data_) + shell_surface_data_->shell_surface->host_window()->RemoveObserver(this); + } + + // Overridden from aura::WindowObserver: + void OnWindowDestroying(aura::Window* window) override { + shell_surface_data_ = nullptr; + } + + void SetParent(WaylandToplevel* parent) { + if (!shell_surface_data_) + return; + + if (!parent) { + shell_surface_data_->shell_surface->SetParent(nullptr); + return; + } + + // This is a no-op if parent is not mapped. + if (parent->shell_surface_data_ && + parent->shell_surface_data_->shell_surface->GetWidget()) + shell_surface_data_->shell_surface->SetParent( + parent->shell_surface_data_->shell_surface.get()); + } + + void SetTitle(const base::string16& title) { + if (shell_surface_data_) + shell_surface_data_->shell_surface->SetTitle(title); + } + + void SetApplicationId(const char* application_id) { + if (shell_surface_data_) + shell_surface_data_->shell_surface->SetApplicationId(application_id); + } + + void Move() { + if (shell_surface_data_) + shell_surface_data_->shell_surface->StartMove(); + } + + void Resize(int component) { + if (!shell_surface_data_) + return; + + if (component != HTNOWHERE) + shell_surface_data_->shell_surface->StartResize(component); + } + + void SetMaximumSize(const gfx::Size& size) { + if (shell_surface_data_) + shell_surface_data_->shell_surface->SetMaximumSize(size); + } + + void SetMinimumSize(const gfx::Size& size) { + if (shell_surface_data_) + shell_surface_data_->shell_surface->SetMinimumSize(size); + } + + void Maximize() { + if (shell_surface_data_) + shell_surface_data_->shell_surface->Maximize(); + } + + void Restore() { + if (shell_surface_data_) + shell_surface_data_->shell_surface->Restore(); + } + + void SetFullscreen(bool fullscreen) { + if (shell_surface_data_) + shell_surface_data_->shell_surface->SetFullscreen(fullscreen); + } + + void Minimize() { + if (shell_surface_data_) + shell_surface_data_->shell_surface->Minimize(); + } + + private: + void OnClose() { + xdg_toplevel_send_close(resource_); + wl_client_flush(wl_resource_get_client(resource_)); + } + + static void AddState(wl_array* states, xdg_toplevel_state state) { + xdg_toplevel_state* value = static_cast<xdg_toplevel_state*>( + wl_array_add(states, sizeof(xdg_toplevel_state))); + DCHECK(value); + *value = state; + } + + void OnConfigure(const gfx::Size& size, + ash::WindowStateType state_type, + bool resizing, + bool activated) { + wl_array states; + wl_array_init(&states); + if (state_type == ash::WindowStateType::kMaximized) + AddState(&states, XDG_TOPLEVEL_STATE_MAXIMIZED); + if (state_type == ash::WindowStateType::kFullscreen) + AddState(&states, XDG_TOPLEVEL_STATE_FULLSCREEN); + if (resizing) + AddState(&states, XDG_TOPLEVEL_STATE_RESIZING); + if (activated) + AddState(&states, XDG_TOPLEVEL_STATE_ACTIVATED); + xdg_toplevel_send_configure(resource_, size.width(), size.height(), + &states); + wl_array_release(&states); + } + + wl_resource* const resource_; + WaylandXdgSurface* shell_surface_data_; + base::WeakPtrFactory<WaylandToplevel> weak_ptr_factory_{this}; + + DISALLOW_COPY_AND_ASSIGN(WaylandToplevel); +}; + +void xdg_toplevel_destroy(wl_client* client, wl_resource* resource) { + wl_resource_destroy(resource); +} + +void xdg_toplevel_set_parent(wl_client* client, + wl_resource* resource, + wl_resource* parent) { + WaylandToplevel* parent_surface = nullptr; + if (parent) + parent_surface = GetUserDataAs<WaylandToplevel>(parent); + + GetUserDataAs<WaylandToplevel>(resource)->SetParent(parent_surface); +} + +void xdg_toplevel_set_title(wl_client* client, + wl_resource* resource, + const char* title) { + GetUserDataAs<WaylandToplevel>(resource)->SetTitle( + base::string16(base::UTF8ToUTF16(title))); +} + +void xdg_toplevel_set_app_id(wl_client* client, + wl_resource* resource, + const char* app_id) { + GetUserDataAs<WaylandToplevel>(resource)->SetApplicationId(app_id); +} + +void xdg_toplevel_show_window_menu(wl_client* client, + wl_resource* resource, + wl_resource* seat, + uint32_t serial, + int32_t x, + int32_t y) { + NOTIMPLEMENTED(); +} + +void xdg_toplevel_move(wl_client* client, + wl_resource* resource, + wl_resource* seat, + uint32_t serial) { + GetUserDataAs<WaylandToplevel>(resource)->Move(); +} + +void xdg_toplevel_resize(wl_client* client, + wl_resource* resource, + wl_resource* seat, + uint32_t serial, + uint32_t edges) { + GetUserDataAs<WaylandToplevel>(resource)->Resize( + XdgToplevelResizeComponent(edges)); +} + +void xdg_toplevel_set_max_size(wl_client* client, + wl_resource* resource, + int32_t width, + int32_t height) { + GetUserDataAs<WaylandToplevel>(resource)->SetMaximumSize( + gfx::Size(width, height)); +} + +void xdg_toplevel_set_min_size(wl_client* client, + wl_resource* resource, + int32_t width, + int32_t height) { + GetUserDataAs<WaylandToplevel>(resource)->SetMinimumSize( + gfx::Size(width, height)); +} + +void xdg_toplevel_set_maximized(wl_client* client, wl_resource* resource) { + GetUserDataAs<WaylandToplevel>(resource)->Maximize(); +} + +void xdg_toplevel_unset_maximized(wl_client* client, wl_resource* resource) { + GetUserDataAs<WaylandToplevel>(resource)->Restore(); +} + +void xdg_toplevel_set_fullscreen(wl_client* client, + wl_resource* resource, + wl_resource* output) { + GetUserDataAs<WaylandToplevel>(resource)->SetFullscreen(true); +} + +void xdg_toplevel_unset_fullscreen(wl_client* client, wl_resource* resource) { + GetUserDataAs<WaylandToplevel>(resource)->SetFullscreen(false); +} + +void xdg_toplevel_set_minimized(wl_client* client, wl_resource* resource) { + GetUserDataAs<WaylandToplevel>(resource)->Minimize(); +} + +const struct xdg_toplevel_interface xdg_toplevel_implementation = { + xdg_toplevel_destroy, xdg_toplevel_set_parent, + xdg_toplevel_set_title, xdg_toplevel_set_app_id, + xdg_toplevel_show_window_menu, xdg_toplevel_move, + xdg_toplevel_resize, xdg_toplevel_set_max_size, + xdg_toplevel_set_min_size, xdg_toplevel_set_maximized, + xdg_toplevel_unset_maximized, xdg_toplevel_set_fullscreen, + xdg_toplevel_unset_fullscreen, xdg_toplevel_set_minimized}; + +//////////////////////////////////////////////////////////////////////////////// +// xdg_popup_interface: + +// Wrapper around shell surface that allows us to handle the case where the +// xdg surface resource is destroyed before the popup resource. +class WaylandPopup : aura::WindowObserver { + public: + WaylandPopup(wl_resource* resource, wl_resource* surface_resource) + : resource_(resource), + shell_surface_data_( + GetUserDataAs<WaylandXdgSurface>(surface_resource)) { + shell_surface_data_->shell_surface->host_window()->AddObserver(this); + shell_surface_data_->shell_surface->set_close_callback(base::BindRepeating( + &WaylandPopup::OnClose, weak_ptr_factory_.GetWeakPtr())); + shell_surface_data_->shell_surface->set_configure_callback( + base::BindRepeating( + &HandleXdgSurfaceConfigureCallback, surface_resource, + shell_surface_data_->serial_tracker, + base::BindRepeating(&WaylandPopup::OnConfigure, + weak_ptr_factory_.GetWeakPtr()))); + } + ~WaylandPopup() override { + if (shell_surface_data_) + shell_surface_data_->shell_surface->host_window()->RemoveObserver(this); + } + + void Grab() { + if (!shell_surface_data_) { + wl_resource_post_error(resource_, XDG_POPUP_ERROR_INVALID_GRAB, + "the surface has already been destroyed"); + return; + } + if (shell_surface_data_->shell_surface->GetWidget()) { + wl_resource_post_error(resource_, XDG_POPUP_ERROR_INVALID_GRAB, + "grab must be called before construction"); + return; + } + shell_surface_data_->shell_surface->Grab(); + } + + // Overridden from aura::WindowObserver: + void OnWindowDestroying(aura::Window* window) override { + shell_surface_data_ = nullptr; + } + + private: + void OnClose() { + xdg_popup_send_popup_done(resource_); + wl_client_flush(wl_resource_get_client(resource_)); + } + + void OnConfigure(const gfx::Size& size, + ash::WindowStateType state_type, + bool resizing, + bool activated) { + // Nothing to do here as popups don't have additional configure state. + } + + wl_resource* const resource_; + WaylandXdgSurface* shell_surface_data_; + base::WeakPtrFactory<WaylandPopup> weak_ptr_factory_{this}; + + DISALLOW_COPY_AND_ASSIGN(WaylandPopup); +}; + +void xdg_popup_destroy(wl_client* client, wl_resource* resource) { + wl_resource_destroy(resource); +} + +void xdg_popup_grab(wl_client* client, + wl_resource* resource, + wl_resource* seat, + uint32_t serial) { + GetUserDataAs<WaylandPopup>(resource)->Grab(); +} + +const struct xdg_popup_interface xdg_popup_implementation = {xdg_popup_destroy, + xdg_popup_grab}; + +//////////////////////////////////////////////////////////////////////////////// +// xdg_surface_interface: + +void xdg_surface_destroy(wl_client* client, wl_resource* resource) { + wl_resource_destroy(resource); +} + +void xdg_surface_get_toplevel(wl_client* client, + wl_resource* resource, + uint32_t id) { + auto* shell_surface_data = GetUserDataAs<WaylandXdgSurface>(resource); + if (shell_surface_data->shell_surface->GetEnabled()) { + wl_resource_post_error(resource, XDG_SURFACE_ERROR_ALREADY_CONSTRUCTED, + "surface has already been constructed"); + return; + } + + shell_surface_data->shell_surface->SetCanMinimize(true); + shell_surface_data->shell_surface->SetEnabled(true); + + wl_resource* xdg_toplevel_resource = + wl_resource_create(client, &xdg_toplevel_interface, 1, id); + + SetImplementation( + xdg_toplevel_resource, &xdg_toplevel_implementation, + std::make_unique<WaylandToplevel>(xdg_toplevel_resource, resource)); +} + +void xdg_surface_get_popup(wl_client* client, + wl_resource* resource, + uint32_t id, + wl_resource* parent_resource, + wl_resource* positioner_resource) { + auto* shell_surface_data = GetUserDataAs<WaylandXdgSurface>(resource); + if (shell_surface_data->shell_surface->GetEnabled()) { + wl_resource_post_error(resource, XDG_SURFACE_ERROR_ALREADY_CONSTRUCTED, + "surface has already been constructed"); + return; + } + + if (!parent_resource) { + wl_resource_post_error(resource, XDG_SURFACE_ERROR_NOT_CONSTRUCTED, + "popup parent not supplied"); + return; + } + + auto* parent_data = GetUserDataAs<WaylandXdgSurface>(parent_resource); + if (!parent_data->shell_surface->GetWidget()) { + wl_resource_post_error(resource, XDG_SURFACE_ERROR_NOT_CONSTRUCTED, + "popup parent not constructed"); + return; + } + + if (shell_surface_data->shell_surface->GetWidget()) { + wl_resource_post_error(resource, XDG_SURFACE_ERROR_ALREADY_CONSTRUCTED, + "get_popup is called after constructed"); + return; + } + + display::Display display = + display::Screen::GetScreen()->GetDisplayNearestWindow( + parent_data->shell_surface->GetWidget()->GetNativeWindow()); + gfx::Rect work_area = display.work_area(); + wm::ConvertRectFromScreen( + parent_data->shell_surface->GetWidget()->GetNativeWindow(), &work_area); + + // Try layout using parent's flip state. + WaylandPositioner* positioner = + GetUserDataAs<WaylandPositioner>(positioner_resource); + WaylandPositioner::Result position = positioner->CalculatePosition( + work_area, parent_data->shell_surface->x_flipped(), + parent_data->shell_surface->y_flipped()); + + // Remember the new flip state for its child popups. + shell_surface_data->shell_surface->set_x_flipped(position.x_flipped); + shell_surface_data->shell_surface->set_y_flipped(position.y_flipped); + + // |position| is relative to the parent's contents view origin, and |origin| + // is in screen coordinates. + gfx::Point origin = position.origin; + views::View::ConvertPointToScreen(parent_data->shell_surface->GetWidget() + ->widget_delegate() + ->GetContentsView(), + &origin); + shell_surface_data->shell_surface->SetOrigin(origin); + shell_surface_data->shell_surface->SetSize(position.size); + shell_surface_data->shell_surface->DisableMovement(); + shell_surface_data->shell_surface->SetActivatable(false); + shell_surface_data->shell_surface->SetCanMinimize(false); + shell_surface_data->shell_surface->SetParent( + parent_data->shell_surface.get()); + shell_surface_data->shell_surface->SetPopup(); + shell_surface_data->shell_surface->SetEnabled(true); + + wl_resource* xdg_popup_resource = + wl_resource_create(client, &xdg_popup_interface, 1, id); + + SetImplementation( + xdg_popup_resource, &xdg_popup_implementation, + std::make_unique<WaylandPopup>(xdg_popup_resource, resource)); + + // We send the configure event here as this event needs x,y coordinates + // relative to the parent window. + xdg_popup_send_configure(xdg_popup_resource, position.origin.x(), + position.origin.y(), position.size.width(), + position.size.height()); +} + +void xdg_surface_set_window_geometry(wl_client* client, + wl_resource* resource, + int32_t x, + int32_t y, + int32_t width, + int32_t height) { + GetUserDataAs<WaylandXdgSurface>(resource)->shell_surface->SetGeometry( + gfx::Rect(x, y, width, height)); +} + +void xdg_surface_ack_configure(wl_client* client, + wl_resource* resource, + uint32_t serial) { + GetUserDataAs<WaylandXdgSurface>(resource) + ->shell_surface->AcknowledgeConfigure(serial); +} + +const struct xdg_surface_interface xdg_surface_implementation = { + xdg_surface_destroy, xdg_surface_get_toplevel, xdg_surface_get_popup, + xdg_surface_set_window_geometry, xdg_surface_ack_configure}; + +//////////////////////////////////////////////////////////////////////////////// +// xdg_wm_base_interface: + +void xdg_wm_base_destroy(wl_client* client, wl_resource* resource) { + // Nothing to do here. +} + +void xdg_wm_base_create_positioner(wl_client* client, + wl_resource* resource, + uint32_t id) { + wl_resource* positioner_resource = + wl_resource_create(client, &xdg_positioner_interface, 1, id); + + SetImplementation( + positioner_resource, &xdg_positioner_implementation, + std::make_unique<WaylandPositioner>(WaylandPositioner::Version::STABLE)); +} + +void xdg_wm_base_get_xdg_surface(wl_client* client, + wl_resource* resource, + uint32_t id, + wl_resource* surface) { + auto* data = GetUserDataAs<WaylandXdgShell>(resource); + std::unique_ptr<XdgShellSurface> shell_surface = + data->display->CreateXdgShellSurface(GetUserDataAs<Surface>(surface)); + if (!shell_surface) { + wl_resource_post_error(resource, XDG_WM_BASE_ERROR_ROLE, + "surface has already been assigned a role"); + return; + } + + // Xdg shell surfaces are initially disabled and needs to be explicitly mapped + // before they are enabled and can become visible. + shell_surface->SetEnabled(false); + + std::unique_ptr<WaylandXdgSurface> wayland_shell_surface = + std::make_unique<WaylandXdgSurface>(std::move(shell_surface), + data->serial_tracker); + + wl_resource* xdg_surface_resource = + wl_resource_create(client, &xdg_surface_interface, 1, id); + + SetImplementation(xdg_surface_resource, &xdg_surface_implementation, + std::move(wayland_shell_surface)); +} + +void xdg_wm_base_pong(wl_client* client, + wl_resource* resource, + uint32_t serial) { + NOTIMPLEMENTED(); +} + +const struct xdg_wm_base_interface xdg_wm_base_implementation = { + xdg_wm_base_destroy, xdg_wm_base_create_positioner, + xdg_wm_base_get_xdg_surface, xdg_wm_base_pong}; + +} // namespace + +void bind_xdg_shell(wl_client* client, + void* data, + uint32_t version, + uint32_t id) { + wl_resource* resource = + wl_resource_create(client, &xdg_wm_base_interface, 1, id); + + wl_resource_set_implementation(resource, &xdg_wm_base_implementation, data, + nullptr); +} + +} // namespace wayland +} // namespace exo diff --git a/chromium/components/exo/wayland/xdg_shell.h b/chromium/components/exo/wayland/xdg_shell.h new file mode 100644 index 00000000000..9619efdea12 --- /dev/null +++ b/chromium/components/exo/wayland/xdg_shell.h @@ -0,0 +1,41 @@ +// Copyright 2018 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 COMPONENTS_EXO_WAYLAND_XDG_SHELL_H_ +#define COMPONENTS_EXO_WAYLAND_XDG_SHELL_H_ + +#include <stdint.h> + +#include "base/macros.h" + +struct wl_client; + +namespace exo { +class Display; + +namespace wayland { +class SerialTracker; + +struct WaylandXdgShell { + WaylandXdgShell(Display* display, SerialTracker* serial_tracker) + : display(display), serial_tracker(serial_tracker) {} + + // Owned by WaylandServerController, which always outlives xdg_shell. + Display* const display; + + // Owned by Server, which always outlives xdg_shell. + SerialTracker* const serial_tracker; + + DISALLOW_COPY_AND_ASSIGN(WaylandXdgShell); +}; + +void bind_xdg_shell(wl_client* client, + void* data, + uint32_t version, + uint32_t id); + +} // namespace wayland +} // namespace exo + +#endif // COMPONENTS_EXO_WAYLAND_XDG_SHELL_H_ diff --git a/chromium/components/exo/wayland/zaura_shell.cc b/chromium/components/exo/wayland/zaura_shell.cc index a3b0a7b6fcd..b80e9ebb559 100644 --- a/chromium/components/exo/wayland/zaura_shell.cc +++ b/chromium/components/exo/wayland/zaura_shell.cc @@ -224,7 +224,7 @@ void AuraSurface::OnSurfaceDestroying(Surface* surface) { } void AuraSurface::OnWindowOcclusionChanged(Surface* surface) { - if (!surface_ || !surface_->is_tracking_occlusion()) + if (!surface_ || !surface_->IsTrackingOcclusion()) return; auto* window = surface_->window(); ComputeAndSendOcclusionFraction(window->occlusion_state(), @@ -241,7 +241,7 @@ void AuraSurface::OnWindowActivating(ActivationReason reason, // Check if this surface is a child of a window that is losing focus. auto* widget = views::Widget::GetTopLevelWidgetForNativeView(window); if (!widget || losing_active != widget->GetNativeWindow() || - !surface_->is_tracking_occlusion()) + !surface_->IsTrackingOcclusion()) return; // Result may be changed by animated windows, so compute it explicitly. diff --git a/chromium/components/exo/wayland/zaura_shell_unittest.cc b/chromium/components/exo/wayland/zaura_shell_unittest.cc index 4a7218ca626..90db54f3845 100644 --- a/chromium/components/exo/wayland/zaura_shell_unittest.cc +++ b/chromium/components/exo/wayland/zaura_shell_unittest.cc @@ -11,6 +11,7 @@ #include "ash/wm/desks/desks_util.h" #include "ash/wm/window_util.h" #include "base/time/time.h" +#include "components/exo/buffer.h" #include "components/exo/test/exo_test_base.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/aura/window_occlusion_tracker.h" @@ -67,7 +68,13 @@ class ZAuraSurfaceTest : public test::ExoTestBase, void SetUp() override { test::ExoTestBase::SetUp(); + gfx::Size buffer_size(10, 10); + std::unique_ptr<Buffer> buffer( + new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size))); + surface_.reset(new Surface); + surface_->Attach(buffer.get()); + aura_surface_.reset(new TestAuraSurface(surface_.get())); gfx::Transform transform; @@ -128,8 +135,25 @@ class ZAuraSurfaceTest : public test::ExoTestBase, DISALLOW_COPY_AND_ASSIGN(ZAuraSurfaceTest); }; +TEST_F(ZAuraSurfaceTest, OcclusionTrackingStartsAfterCommit) { + surface().OnWindowOcclusionChanged(); + + EXPECT_EQ(-1.0f, aura_surface().last_sent_occlusion_fraction()); + EXPECT_EQ(0, aura_surface().num_occlusion_updates()); + EXPECT_FALSE(surface().IsTrackingOcclusion()); + + auto widget = CreateOpaqueWidget(gfx::Rect(0, 0, 10, 10)); + widget->Show(); + surface().Commit(); + + EXPECT_EQ(0.2f, aura_surface().last_sent_occlusion_fraction()); + EXPECT_EQ(1, aura_surface().num_occlusion_updates()); + EXPECT_TRUE(surface().IsTrackingOcclusion()); +} + TEST_F(ZAuraSurfaceTest, LosingActivationWithNoAnimatingWindowsSendsCorrectOcclusionFraction) { + surface().Commit(); EXPECT_EQ(0.0f, aura_surface().last_sent_occlusion_fraction()); EXPECT_EQ(1, aura_surface().num_occlusion_updates()); ::wm::ActivateWindow(parent_widget().GetNativeWindow()); @@ -146,6 +170,7 @@ TEST_F(ZAuraSurfaceTest, TEST_F(ZAuraSurfaceTest, LosingActivationWithAnimatingWindowsSendsTargetOcclusionFraction) { + surface().Commit(); EXPECT_EQ(0.0f, aura_surface().last_sent_occlusion_fraction()); EXPECT_EQ(1, aura_surface().num_occlusion_updates()); ::wm::ActivateWindow(parent_widget().GetNativeWindow()); @@ -194,6 +219,7 @@ TEST_F(ZAuraSurfaceTest, TEST_F(ZAuraSurfaceTest, LosingActivationByTriggeringTheLockScreenDoesNotSendOccludedFraction) { + surface().Commit(); EXPECT_EQ(0.0f, aura_surface().last_sent_occlusion_fraction()); EXPECT_EQ(1, aura_surface().num_occlusion_updates()); ::wm::ActivateWindow(parent_widget().GetNativeWindow()); @@ -227,9 +253,16 @@ TEST_F(ZAuraSurfaceTest, TEST_F(ZAuraSurfaceTest, OcclusionIncludesOffScreenArea) { UpdateDisplay("150x150"); + + gfx::Size buffer_size(80, 100); + std::unique_ptr<Buffer> buffer( + new Buffer(exo_test_helper()->CreateGpuMemoryBuffer(buffer_size))); // This is scaled by 1.5 - set the bounds to (-60, 75, 120, 150) in screen // coordinates so 75% of it is outside of the 100x100 screen. surface().window()->SetBounds(gfx::Rect(-40, 50, 80, 100)); + surface().Attach(buffer.get()); + surface().Commit(); + surface().OnWindowOcclusionChanged(); EXPECT_EQ(0.75f, aura_surface().last_sent_occlusion_fraction()); @@ -238,6 +271,7 @@ TEST_F(ZAuraSurfaceTest, OcclusionIncludesOffScreenArea) { TEST_F(ZAuraSurfaceTest, ZeroSizeWindowSendsZeroOcclusionFraction) { // Zero sized window should not be occluded. surface().window()->SetBounds(gfx::Rect(0, 0, 0, 0)); + surface().Commit(); surface().OnWindowOcclusionChanged(); EXPECT_EQ(0.0f, aura_surface().last_sent_occlusion_fraction()); } diff --git a/chromium/components/exo/wayland/zcr_gaming_input.cc b/chromium/components/exo/wayland/zcr_gaming_input.cc index ad9a7464fad..a8d61c04d6d 100644 --- a/chromium/components/exo/wayland/zcr_gaming_input.cc +++ b/chromium/components/exo/wayland/zcr_gaming_input.cc @@ -8,6 +8,8 @@ #include <wayland-server-core.h> #include <wayland-server-protocol-core.h> +#include <memory> + #include "base/feature_list.h" #include "base/macros.h" #include "components/exo/gamepad_delegate.h" @@ -64,27 +66,30 @@ class WaylandGamepadDelegate : public GamepadDelegate { wl_resource_set_user_data(gamepad_resource_, nullptr); delete this; } - void OnAxis(int axis, double value) override { + void OnAxis(int axis, double value, base::TimeTicks time_stamp) override { if (!gamepad_resource_) { return; } - zcr_gamepad_v2_send_axis(gamepad_resource_, NowInMilliseconds(), axis, + zcr_gamepad_v2_send_axis(gamepad_resource_, + TimeTicksToMilliseconds(time_stamp), axis, wl_fixed_from_double(value)); } - void OnButton(int button, bool pressed) override { + void OnButton(int button, bool pressed, base::TimeTicks time_stamp) override { if (!gamepad_resource_) { return; } uint32_t state = pressed ? ZCR_GAMEPAD_V2_BUTTON_STATE_PRESSED : ZCR_GAMEPAD_V2_BUTTON_STATE_RELEASED; - zcr_gamepad_v2_send_button(gamepad_resource_, NowInMilliseconds(), button, + zcr_gamepad_v2_send_button(gamepad_resource_, + TimeTicksToMilliseconds(time_stamp), button, state, wl_fixed_from_double(0)); } - void OnFrame() override { + void OnFrame(base::TimeTicks time_stamp) override { if (!gamepad_resource_) { return; } - zcr_gamepad_v2_send_frame(gamepad_resource_, NowInMilliseconds()); + zcr_gamepad_v2_send_frame(gamepad_resource_, + TimeTicksToMilliseconds(time_stamp)); wl_client_flush(client()); } diff --git a/chromium/components/exo/wayland/zwp_text_input_manager.cc b/chromium/components/exo/wayland/zwp_text_input_manager.cc index 35df45bb43a..3d115a5b528 100644 --- a/chromium/components/exo/wayland/zwp_text_input_manager.cc +++ b/chromium/components/exo/wayland/zwp_text_input_manager.cc @@ -71,6 +71,7 @@ class WaylandTextInputDelegate : public TextInput::Delegate { style = ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_SELECTION; break; case ui::ImeTextSpan::Type::kMisspellingSuggestion: + case ui::ImeTextSpan::Type::kAutocorrect: style = ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_INCORRECT; break; } diff --git a/chromium/components/exo/wayland/zxdg_shell.cc b/chromium/components/exo/wayland/zxdg_shell.cc index fde5aec2fbc..a4284484676 100644 --- a/chromium/components/exo/wayland/zxdg_shell.cc +++ b/chromium/components/exo/wayland/zxdg_shell.cc @@ -632,14 +632,15 @@ void xdg_shell_v6_create_positioner(wl_client* client, wl_resource_create(client, &zxdg_positioner_v6_interface, 1, id); SetImplementation(positioner_resource, &xdg_positioner_v6_implementation, - std::make_unique<WaylandPositioner>()); + std::make_unique<WaylandPositioner>( + WaylandPositioner::Version::UNSTABLE)); } void xdg_shell_v6_get_xdg_surface(wl_client* client, wl_resource* resource, uint32_t id, wl_resource* surface) { - auto* data = GetUserDataAs<WaylandXdgShell>(resource); + auto* data = GetUserDataAs<WaylandZxdgShell>(resource); std::unique_ptr<XdgShellSurface> shell_surface = data->display->CreateXdgShellSurface(GetUserDataAs<Surface>(surface)); if (!shell_surface) { @@ -675,10 +676,10 @@ const struct zxdg_shell_v6_interface xdg_shell_v6_implementation = { } // namespace -void bind_xdg_shell_v6(wl_client* client, - void* data, - uint32_t version, - uint32_t id) { +void bind_zxdg_shell_v6(wl_client* client, + void* data, + uint32_t version, + uint32_t id) { wl_resource* resource = wl_resource_create(client, &zxdg_shell_v6_interface, 1, id); diff --git a/chromium/components/exo/wayland/zxdg_shell.h b/chromium/components/exo/wayland/zxdg_shell.h index f5112d36170..026a8bbc690 100644 --- a/chromium/components/exo/wayland/zxdg_shell.h +++ b/chromium/components/exo/wayland/zxdg_shell.h @@ -17,8 +17,8 @@ class Display; namespace wayland { class SerialTracker; -struct WaylandXdgShell { - WaylandXdgShell(Display* display, SerialTracker* serial_tracker) +struct WaylandZxdgShell { + WaylandZxdgShell(Display* display, SerialTracker* serial_tracker) : display(display), serial_tracker(serial_tracker) {} // Owned by WaylandServerController, which always outlives zxdg_shell. @@ -27,13 +27,13 @@ struct WaylandXdgShell { // Owned by Server, which always outlives zxdg_shell. SerialTracker* const serial_tracker; - DISALLOW_COPY_AND_ASSIGN(WaylandXdgShell); + DISALLOW_COPY_AND_ASSIGN(WaylandZxdgShell); }; -void bind_xdg_shell_v6(wl_client* client, - void* data, - uint32_t version, - uint32_t id); +void bind_zxdg_shell_v6(wl_client* client, + void* data, + uint32_t version, + uint32_t id); } // namespace wayland } // namespace exo diff --git a/chromium/components/exo/wm_helper_chromeos.cc b/chromium/components/exo/wm_helper_chromeos.cc index 93fb49a93b9..4086f953a96 100644 --- a/chromium/components/exo/wm_helper_chromeos.cc +++ b/chromium/components/exo/wm_helper_chromeos.cc @@ -120,11 +120,10 @@ void WMHelperChromeOS::OnDragExited() { int WMHelperChromeOS::OnPerformDrop(const ui::DropTargetEvent& event, std::unique_ptr<ui::OSExchangeData> data) { + int valid_operation = ui::DragDropTypes::DRAG_NONE; for (DragDropObserver& observer : drag_drop_observers_) - observer.OnPerformDrop(event); - // TODO(hirono): Return the correct result instead of always returning - // DRAG_MOVE. - return ui::DragDropTypes::DRAG_MOVE; + valid_operation = valid_operation | observer.OnPerformDrop(event); + return valid_operation; } void WMHelperChromeOS::AddVSyncParameterObserver( diff --git a/chromium/components/exo/xdg_shell_surface.h b/chromium/components/exo/xdg_shell_surface.h index 6ac38df90aa..49f8e05088b 100644 --- a/chromium/components/exo/xdg_shell_surface.h +++ b/chromium/components/exo/xdg_shell_surface.h @@ -11,7 +11,6 @@ #include "ash/display/window_tree_host_manager.h" #include "ash/wm/window_state_observer.h" -#include "base/containers/circular_deque.h" #include "base/macros.h" #include "base/optional.h" #include "base/strings/string16.h" |