summaryrefslogtreecommitdiff
path: root/chromium/ui/ozone/platform/wayland
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2020-10-12 14:27:29 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2020-10-13 09:35:20 +0000
commitc30a6232df03e1efbd9f3b226777b07e087a1122 (patch)
treee992f45784689f373bcc38d1b79a239ebe17ee23 /chromium/ui/ozone/platform/wayland
parent7b5b123ac58f58ffde0f4f6e488bcd09aa4decd3 (diff)
downloadqtwebengine-chromium-85-based.tar.gz
BASELINE: Update Chromium to 85.0.4183.14085-based
Change-Id: Iaa42f4680837c57725b1344f108c0196741f6057 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/ui/ozone/platform/wayland')
-rw-r--r--chromium/ui/ozone/platform/wayland/BUILD.gn21
-rw-r--r--chromium/ui/ozone/platform/wayland/DEPS2
-rw-r--r--chromium/ui/ozone/platform/wayland/OWNERS1
-rw-r--r--chromium/ui/ozone/platform/wayland/common/data_util.cc218
-rw-r--r--chromium/ui/ozone/platform/wayland/common/data_util.h40
-rw-r--r--chromium/ui/ozone/platform/wayland/common/wayland_object.cc11
-rw-r--r--chromium/ui/ozone/platform/wayland/common/wayland_object.h14
-rw-r--r--chromium/ui/ozone/platform/wayland/gpu/drm_render_node_handle.cc2
-rw-r--r--chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc68
-rw-r--r--chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h17
-rw-r--r--chromium/ui/ozone/platform/wayland/gpu/gl_surface_egl_readback_wayland.cc3
-rw-r--r--chromium/ui/ozone/platform/wayland/gpu/wayland_surface_factory_unittest.cc4
-rw-r--r--chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_device.cc9
-rw-r--r--chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_device.h3
-rw-r--r--chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_device_manager.cc31
-rw-r--r--chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_device_manager.h26
-rw-r--r--chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_source.cc76
-rw-r--r--chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_source.h46
-rw-r--r--chromium/ui/ozone/platform/wayland/host/shell_object_factory.cc3
-rw-r--r--chromium/ui/ozone/platform/wayland/host/shell_popup_wrapper.cc3
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc192
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h1
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_clipboard.cc221
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_clipboard.h54
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_connection.cc112
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_connection.h83
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_cursor.cc1
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_data_device.cc478
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_data_device.h139
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_data_device_base.cc3
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_data_device_base.h1
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_data_device_manager.cc22
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_data_device_manager.h20
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_data_device_unittest.cc293
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller.cc355
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller.h136
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller_unittest.cc406
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_data_source.cc226
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_data_source.h126
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_data_source_base.cc21
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_data_source_base.h38
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_drm.cc1
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_event_source.cc54
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_event_source.h22
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_keyboard.cc26
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_keyboard.h21
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_pointer.cc11
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_pointer.h1
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_pointer_unittest.cc2
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_popup.cc4
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_shm_buffer.cc1
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_subsurface.cc10
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_surface.cc347
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_surface.h129
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc368
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.h131
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_window.cc63
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_window.h23
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller.cc325
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller.h126
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller_unittest.cc519
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_window_factory.cc6
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_window_manager.cc5
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_window_manager.h4
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_window_observer.cc2
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_window_observer.h3
-rw-r--r--chromium/ui/ozone/platform/wayland/host/wayland_window_unittest.cc204
-rw-r--r--chromium/ui/ozone/platform/wayland/host/xdg_popup_wrapper_impl.cc157
-rw-r--r--chromium/ui/ozone/platform/wayland/host/xdg_popup_wrapper_impl.h6
-rw-r--r--chromium/ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.cc2
-rw-r--r--chromium/ui/ozone/platform/wayland/ozone_platform_wayland.cc7
-rw-r--r--chromium/ui/ozone/platform/wayland/test/mock_wp_presentation.cc16
-rw-r--r--chromium/ui/ozone/platform/wayland/test/mock_wp_presentation.h5
-rw-r--r--chromium/ui/ozone/platform/wayland/test/scoped_wl_array.cc41
-rw-r--r--chromium/ui/ozone/platform/wayland/test/scoped_wl_array.h31
-rw-r--r--chromium/ui/ozone/platform/wayland/test/test_data_device.cc21
-rw-r--r--chromium/ui/ozone/platform/wayland/test/test_data_device.h15
-rw-r--r--chromium/ui/ozone/platform/wayland/test/test_data_source.cc18
-rw-r--r--chromium/ui/ozone/platform/wayland/test/test_data_source.h10
-rw-r--r--chromium/ui/ozone/platform/wayland/test/test_subsurface.cc2
-rw-r--r--chromium/ui/ozone/platform/wayland/test/wayland_test.cc35
-rw-r--r--chromium/ui/ozone/platform/wayland/test/wayland_test.h13
-rw-r--r--chromium/ui/ozone/platform/wayland/wayland_buffer_manager_unittest.cc322
83 files changed, 4380 insertions, 2255 deletions
diff --git a/chromium/ui/ozone/platform/wayland/BUILD.gn b/chromium/ui/ozone/platform/wayland/BUILD.gn
index b0ec8a82a5e..e0d30f2575c 100644
--- a/chromium/ui/ozone/platform/wayland/BUILD.gn
+++ b/chromium/ui/ozone/platform/wayland/BUILD.gn
@@ -18,6 +18,8 @@ source_set("wayland") {
sources = [
"client_native_pixmap_factory_wayland.cc",
"client_native_pixmap_factory_wayland.h",
+ "common/data_util.cc",
+ "common/data_util.h",
"common/wayland_object.cc",
"common/wayland_object.h",
"common/wayland_util.cc",
@@ -41,8 +43,6 @@ source_set("wayland") {
"host/gtk_primary_selection_device_manager.h",
"host/gtk_primary_selection_offer.cc",
"host/gtk_primary_selection_offer.h",
- "host/gtk_primary_selection_source.cc",
- "host/gtk_primary_selection_source.h",
"host/shell_object_factory.cc",
"host/shell_object_factory.h",
"host/shell_popup_wrapper.cc",
@@ -67,14 +67,14 @@ source_set("wayland") {
"host/wayland_data_device_base.h",
"host/wayland_data_device_manager.cc",
"host/wayland_data_device_manager.h",
+ "host/wayland_data_drag_controller.cc",
+ "host/wayland_data_drag_controller.h",
"host/wayland_data_offer.cc",
"host/wayland_data_offer.h",
"host/wayland_data_offer_base.cc",
"host/wayland_data_offer_base.h",
"host/wayland_data_source.cc",
"host/wayland_data_source.h",
- "host/wayland_data_source_base.cc",
- "host/wayland_data_source_base.h",
"host/wayland_drm.cc",
"host/wayland_drm.h",
"host/wayland_event_source.cc",
@@ -105,10 +105,14 @@ source_set("wayland") {
"host/wayland_subsurface.h",
"host/wayland_surface.cc",
"host/wayland_surface.h",
+ "host/wayland_toplevel_window.cc",
+ "host/wayland_toplevel_window.h",
"host/wayland_touch.cc",
"host/wayland_touch.h",
"host/wayland_window.cc",
"host/wayland_window.h",
+ "host/wayland_window_drag_controller.cc",
+ "host/wayland_window_drag_controller.h",
"host/wayland_window_factory.cc",
"host/wayland_window_manager.cc",
"host/wayland_window_manager.h",
@@ -140,6 +144,7 @@ source_set("wayland") {
"//skia",
"//third_party/wayland:wayland_client",
"//third_party/wayland-protocols:gtk_primary_selection_protocol",
+ "//third_party/wayland-protocols:keyboard_extension_protocol",
"//third_party/wayland-protocols:linux_dmabuf_protocol",
"//third_party/wayland-protocols:presentation_time_protocol",
"//third_party/wayland-protocols:text_input_protocol",
@@ -147,6 +152,8 @@ source_set("wayland") {
"//third_party/wayland-protocols:xdg_shell_protocol",
"//ui/base",
"//ui/base:buildflags",
+ "//ui/base/cursor",
+ "//ui/base/cursor:cursor_base",
"//ui/base/ime/linux",
"//ui/events",
"//ui/events:dom_keycode_converter",
@@ -242,6 +249,8 @@ source_set("test_support") {
"test/mock_zwp_linux_dmabuf.h",
"test/mock_zwp_text_input.cc",
"test/mock_zwp_text_input.h",
+ "test/scoped_wl_array.cc",
+ "test/scoped_wl_array.h",
"test/server_object.cc",
"test/server_object.h",
"test/test_compositor.cc",
@@ -306,12 +315,14 @@ source_set("wayland_unittests") {
"gpu/wayland_surface_factory_unittest.cc",
"host/wayland_connection_unittest.cc",
"host/wayland_data_device_unittest.cc",
+ "host/wayland_data_drag_controller_unittest.cc",
"host/wayland_event_source_unittest.cc",
"host/wayland_input_method_context_unittest.cc",
"host/wayland_keyboard_unittest.cc",
"host/wayland_pointer_unittest.cc",
"host/wayland_screen_unittest.cc",
"host/wayland_touch_unittest.cc",
+ "host/wayland_window_drag_controller_unittest.cc",
"host/wayland_window_manager_unittests.cc",
"host/wayland_window_unittest.cc",
"test/wayland_test.cc",
@@ -326,11 +337,13 @@ source_set("wayland_unittests") {
"//testing/gmock",
"//testing/gtest",
"//third_party/wayland:wayland_server",
+ "//third_party/wayland-protocols:keyboard_extension_protocol",
"//third_party/wayland-protocols:linux_dmabuf_protocol",
"//third_party/wayland-protocols:text_input_protocol",
"//third_party/wayland-protocols:xdg_shell_protocol",
"//ui/base",
"//ui/base:buildflags",
+ "//ui/base/cursor",
"//ui/base/ime/linux",
"//ui/events/ozone/layout",
"//ui/gfx/linux:drm",
diff --git a/chromium/ui/ozone/platform/wayland/DEPS b/chromium/ui/ozone/platform/wayland/DEPS
index d61519af80c..44ef7beabfd 100644
--- a/chromium/ui/ozone/platform/wayland/DEPS
+++ b/chromium/ui/ozone/platform/wayland/DEPS
@@ -8,5 +8,5 @@ include_rules = [
"+ui/base/dragdrop/drag_drop_types.h",
"+ui/base/dragdrop/file_info/file_info.h",
"+ui/base/dragdrop/os_exchange_data.h",
- "+ui/base/dragdrop/os_exchange_data_provider_aura.h",
+ "+ui/base/dragdrop/os_exchange_data_provider_non_backed.h",
]
diff --git a/chromium/ui/ozone/platform/wayland/OWNERS b/chromium/ui/ozone/platform/wayland/OWNERS
index ac018fd66db..1aa8e7e3f6f 100644
--- a/chromium/ui/ozone/platform/wayland/OWNERS
+++ b/chromium/ui/ozone/platform/wayland/OWNERS
@@ -1,2 +1,3 @@
msisov@igalia.com
+nickdiego@igalia.com
tonikitoo@igalia.com
diff --git a/chromium/ui/ozone/platform/wayland/common/data_util.cc b/chromium/ui/ozone/platform/wayland/common/data_util.cc
new file mode 100644
index 00000000000..55e594a07e3
--- /dev/null
+++ b/chromium/ui/ozone/platform/wayland/common/data_util.cc
@@ -0,0 +1,218 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/wayland/common/data_util.h"
+
+#include <vector>
+
+#include "base/check.h"
+#include "base/logging.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_split.h"
+#include "base/strings/utf_string_conversions.h"
+#include "ui/base/clipboard/clipboard_constants.h"
+#include "ui/base/dragdrop/file_info/file_info.h"
+#include "ui/base/dragdrop/os_exchange_data.h"
+#include "ui/base/dragdrop/os_exchange_data_provider_non_backed.h"
+#include "ui/ozone/public/platform_clipboard.h"
+#include "url/gurl.h"
+#include "url/url_canon.h"
+#include "url/url_util.h"
+
+namespace wl {
+
+namespace {
+
+using ui::OSExchangeData;
+using ui::PlatformClipboard;
+
+constexpr ui::FilenameToURLPolicy kFilenameToURLPolicy =
+ ui::FilenameToURLPolicy::CONVERT_FILENAMES;
+
+// Converts mime type string to OSExchangeData::Format, if supported, otherwise
+// 0 is returned.
+int MimeTypeToFormat(const std::string& mime_type) {
+ if (mime_type == ui::kMimeTypeText || mime_type == ui::kMimeTypeTextUtf8)
+ return OSExchangeData::STRING;
+ if (mime_type == ui::kMimeTypeURIList)
+ return OSExchangeData::FILE_NAME;
+ if (mime_type == ui::kMimeTypeMozillaURL)
+ return OSExchangeData::URL;
+ if (mime_type == ui::kMimeTypeHTML)
+ return OSExchangeData::HTML;
+ return 0;
+}
+
+// Converts raw data to either narrow or wide string.
+template <typename StringType>
+StringType BytesTo(const PlatformClipboard::Data& bytes) {
+ if (bytes.size() % sizeof(typename StringType::value_type) != 0U) {
+ // This is suspicious.
+ LOG(WARNING)
+ << "Data is possibly truncated, or a wrong conversion is requested.";
+ }
+
+ StringType result;
+ result.assign(reinterpret_cast<typename StringType::const_pointer>(&bytes[0]),
+ bytes.size() / sizeof(typename StringType::value_type));
+ return result;
+}
+
+void AddString(const PlatformClipboard::Data& data,
+ OSExchangeData* os_exchange_data) {
+ DCHECK(os_exchange_data);
+
+ if (data.empty())
+ return;
+
+ os_exchange_data->SetString(base::UTF8ToUTF16(BytesTo<std::string>(data)));
+}
+
+void AddHtml(const PlatformClipboard::Data& data,
+ OSExchangeData* os_exchange_data) {
+ DCHECK(os_exchange_data);
+
+ if (data.empty())
+ return;
+
+ os_exchange_data->SetHtml(base::UTF8ToUTF16(BytesTo<std::string>(data)),
+ GURL());
+}
+
+// Parses |data| as if it had text/uri-list format. Its brief spec is:
+// 1. Any lines beginning with the '#' character are comment lines.
+// 2. Non-comment lines shall be URIs (URNs or URLs).
+// 3. Lines are terminated with a CRLF pair.
+// 4. URL encoding is used.
+void AddFiles(const PlatformClipboard::Data& data,
+ OSExchangeData* os_exchange_data) {
+ DCHECK(os_exchange_data);
+
+ std::string data_as_string = BytesTo<std::string>(data);
+
+ const auto lines = base::SplitString(
+ data_as_string, "\r\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+ std::vector<ui::FileInfo> filenames;
+ for (const auto& line : lines) {
+ if (line.empty() || line[0] == '#')
+ continue;
+ GURL url(line);
+ if (!url.is_valid() || !url.SchemeIsFile()) {
+ LOG(WARNING) << "Invalid URI found: " << line;
+ continue;
+ }
+
+ std::string url_path = url.path();
+ url::RawCanonOutputT<base::char16> unescaped;
+ url::DecodeURLEscapeSequences(url_path.data(), url_path.size(),
+ url::DecodeURLMode::kUTF8OrIsomorphic,
+ &unescaped);
+
+ std::string path8;
+ base::UTF16ToUTF8(unescaped.data(), unescaped.length(), &path8);
+ const base::FilePath path(path8);
+ filenames.push_back({path, path.BaseName()});
+ }
+ if (filenames.empty())
+ return;
+
+ os_exchange_data->SetFilenames(filenames);
+}
+
+// Parses |data| as if it had text/x-moz-url format, which is basically
+// two lines separated with newline, where the first line is the URL and
+// the second one is page title. The unpleasant feature of text/x-moz-url is
+// that the URL has UTF-16 encoding.
+void AddUrl(const PlatformClipboard::Data& data,
+ OSExchangeData* os_exchange_data) {
+ DCHECK(os_exchange_data);
+
+ if (data.empty())
+ return;
+
+ base::string16 data_as_string16 = BytesTo<base::string16>(data);
+
+ const auto lines =
+ base::SplitString(data_as_string16, base::ASCIIToUTF16("\r\n"),
+ base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+ if (lines.size() != 2U) {
+ LOG(WARNING) << "Invalid data passed as text/x-moz-url; it must contain "
+ << "exactly 2 lines but has " << lines.size() << " instead.";
+ return;
+ }
+ GURL url(lines[0]);
+ if (!url.is_valid()) {
+ LOG(WARNING) << "Invalid data passed as text/x-moz-url; the first line "
+ << "must contain a valid URL but it doesn't.";
+ return;
+ }
+
+ os_exchange_data->SetURL(url, lines[1]);
+}
+
+} // namespace
+
+bool IsMimeTypeSupported(const std::string& mime_type) {
+ return MimeTypeToFormat(mime_type) != 0;
+}
+
+bool ContainsMimeType(const OSExchangeData& exchange_data,
+ const std::string& mime_type) {
+ DCHECK(IsMimeTypeSupported(mime_type));
+ return exchange_data.HasAnyFormat(MimeTypeToFormat(mime_type), {});
+}
+
+void AddToOSExchangeData(const PlatformClipboard::Data& data,
+ const std::string& mime_type,
+ OSExchangeData* exchange_data) {
+ DCHECK(IsMimeTypeSupported(mime_type));
+ DCHECK(exchange_data);
+ int format = MimeTypeToFormat(mime_type);
+ switch (format) {
+ case OSExchangeData::STRING:
+ AddString(data, exchange_data);
+ break;
+ case OSExchangeData::HTML:
+ AddHtml(data, exchange_data);
+ break;
+ case OSExchangeData::URL:
+ AddUrl(data, exchange_data);
+ break;
+ case OSExchangeData::FILE_NAME:
+ AddFiles(data, exchange_data);
+ break;
+ }
+}
+
+bool ExtractOSExchangeData(const OSExchangeData& exchange_data,
+ const std::string& mime_type,
+ std::string* out_content) {
+ DCHECK(out_content);
+ DCHECK(IsMimeTypeSupported(mime_type));
+
+ if (mime_type == ui::kMimeTypeMozillaURL &&
+ exchange_data.HasURL(kFilenameToURLPolicy)) {
+ GURL url;
+ base::string16 title;
+ exchange_data.GetURLAndTitle(kFilenameToURLPolicy, &url, &title);
+ out_content->append(url.spec());
+ return true;
+ }
+ if (mime_type == ui::kMimeTypeHTML && exchange_data.HasHtml()) {
+ base::string16 data;
+ GURL base_url;
+ exchange_data.GetHtml(&data, &base_url);
+ out_content->append(base::UTF16ToUTF8(data));
+ return true;
+ }
+ if (exchange_data.HasString()) {
+ base::string16 data;
+ exchange_data.GetString(&data);
+ out_content->append(base::UTF16ToUTF8(data));
+ return true;
+ }
+ return false;
+}
+
+} // namespace wl
diff --git a/chromium/ui/ozone/platform/wayland/common/data_util.h b/chromium/ui/ozone/platform/wayland/common/data_util.h
new file mode 100644
index 00000000000..d6c2d10e7f5
--- /dev/null
+++ b/chromium/ui/ozone/platform/wayland/common/data_util.h
@@ -0,0 +1,40 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_WAYLAND_COMMON_DATA_UTIL_H_
+#define UI_OZONE_PLATFORM_WAYLAND_COMMON_DATA_UTIL_H_
+
+#include <string>
+
+#include "ui/ozone/public/platform_clipboard.h"
+
+namespace ui {
+class OSExchangeData;
+} // namespace ui
+
+namespace wl {
+
+// Tells if |mime_type| is supported for Drag and Drop operations.
+bool IsMimeTypeSupported(const std::string& mime_type);
+
+// Tells if |exchange_data| contains |mime_type| content.
+bool ContainsMimeType(const ui::OSExchangeData& exchange_data,
+ const std::string& mime_type);
+
+// Add clipboard |data| content with |mime_type| format to the |exchange_data|.
+// |mime_type| is assumed to be supported (See IsMimeTypeSupported for more).
+void AddToOSExchangeData(const ui::PlatformClipboard::Data& data,
+ const std::string& mime_type,
+ ui::OSExchangeData* exchange_data);
+
+// Extract |exchange_data| of type |mime_type| and put it into |buffer|. If such
+// mime type is not present, false is returned and |buffer| keeps untouched.
+// |mime_type| is assumed to be supported (See IsMimeTypeSupported for more).
+bool ExtractOSExchangeData(const ui::OSExchangeData& exchange_data,
+ const std::string& mime_type,
+ std::string* buffer);
+
+} // namespace wl
+
+#endif // UI_OZONE_PLATFORM_WAYLAND_COMMON_DATA_UTIL_H_
diff --git a/chromium/ui/ozone/platform/wayland/common/wayland_object.cc b/chromium/ui/ozone/platform/wayland/common/wayland_object.cc
index d0db0924463..9f9efeae72e 100644
--- a/chromium/ui/ozone/platform/wayland/common/wayland_object.cc
+++ b/chromium/ui/ozone/platform/wayland/common/wayland_object.cc
@@ -5,6 +5,7 @@
#include "ui/ozone/platform/wayland/common/wayland_object.h"
#include <gtk-primary-selection-client-protocol.h>
+#include <keyboard-extension-unstable-v1-client-protocol.h>
#include <linux-dmabuf-unstable-v1-client-protocol.h>
#include <presentation-time-client-protocol.h>
#include <text-input-unstable-v1-client-protocol.h>
@@ -189,6 +190,16 @@ const wl_interface* ObjectTraits<xdg_positioner>::interface =
void (*ObjectTraits<xdg_positioner>::deleter)(xdg_positioner*) =
&xdg_positioner_destroy;
+const wl_interface* ObjectTraits<zcr_keyboard_extension_v1>::interface =
+ &zcr_keyboard_extension_v1_interface;
+void (*ObjectTraits<zcr_keyboard_extension_v1>::deleter)(
+ zcr_keyboard_extension_v1*) = &zcr_keyboard_extension_v1_destroy;
+
+const wl_interface* ObjectTraits<zcr_extended_keyboard_v1>::interface =
+ &zcr_extended_keyboard_v1_interface;
+void (*ObjectTraits<zcr_extended_keyboard_v1>::deleter)(
+ zcr_extended_keyboard_v1*) = &zcr_extended_keyboard_v1_destroy;
+
const wl_interface* ObjectTraits<zwp_linux_dmabuf_v1>::interface =
&zwp_linux_dmabuf_v1_interface;
void (*ObjectTraits<zwp_linux_dmabuf_v1>::deleter)(zwp_linux_dmabuf_v1*) =
diff --git a/chromium/ui/ozone/platform/wayland/common/wayland_object.h b/chromium/ui/ozone/platform/wayland/common/wayland_object.h
index 2d048ee3c5b..5c639f2db81 100644
--- a/chromium/ui/ozone/platform/wayland/common/wayland_object.h
+++ b/chromium/ui/ozone/platform/wayland/common/wayland_object.h
@@ -39,6 +39,8 @@ struct xdg_surface;
struct xdg_toplevel;
struct xdg_popup;
struct xdg_positioner;
+struct zcr_keyboard_extension_v1;
+struct zcr_extended_keyboard_v1;
struct zwp_linux_dmabuf_v1;
struct zxdg_shell_v6;
struct zxdg_surface_v6;
@@ -246,6 +248,18 @@ struct ObjectTraits<xdg_positioner> {
};
template <>
+struct ObjectTraits<zcr_keyboard_extension_v1> {
+ static const wl_interface* interface;
+ static void (*deleter)(zcr_keyboard_extension_v1*);
+};
+
+template <>
+struct ObjectTraits<zcr_extended_keyboard_v1> {
+ static const wl_interface* interface;
+ static void (*deleter)(zcr_extended_keyboard_v1*);
+};
+
+template <>
struct ObjectTraits<zwp_linux_dmabuf_v1> {
static const wl_interface* interface;
static void (*deleter)(zwp_linux_dmabuf_v1*);
diff --git a/chromium/ui/ozone/platform/wayland/gpu/drm_render_node_handle.cc b/chromium/ui/ozone/platform/wayland/gpu/drm_render_node_handle.cc
index a049ee5be98..87ec9b08f00 100644
--- a/chromium/ui/ozone/platform/wayland/gpu/drm_render_node_handle.cc
+++ b/chromium/ui/ozone/platform/wayland/gpu/drm_render_node_handle.cc
@@ -7,6 +7,8 @@
#include <fcntl.h>
#include <xf86drm.h>
+#include "base/logging.h"
+
namespace ui {
DrmRenderNodeHandle::DrmRenderNodeHandle() = default;
diff --git a/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc b/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc
index 95c840cbf77..734e8b2b699 100644
--- a/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc
+++ b/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc
@@ -18,12 +18,17 @@ namespace ui {
namespace {
-void WaitForFence(EGLDisplay display, EGLSyncKHR fence) {
+void WaitForEGLFence(EGLDisplay display, EGLSyncKHR fence) {
eglClientWaitSyncKHR(display, fence, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
EGL_FOREVER_KHR);
eglDestroySyncKHR(display, fence);
}
+void WaitForGpuFences(std::vector<std::unique_ptr<gfx::GpuFence>> fences) {
+ for (auto& fence : fences)
+ fence->Wait();
+}
+
} // namespace
GbmSurfacelessWayland::GbmSurfacelessWayland(
@@ -41,7 +46,7 @@ GbmSurfacelessWayland::GbmSurfacelessWayland(
void GbmSurfacelessWayland::QueueOverlayPlane(OverlayPlane plane,
uint32_t buffer_id) {
- planes_.push_back({std::move(plane), buffer_id});
+ unsubmitted_frames_.back()->planes.push_back({std::move(plane), buffer_id});
}
bool GbmSurfacelessWayland::ScheduleOverlayPlane(
@@ -87,7 +92,8 @@ void GbmSurfacelessWayland::SwapBuffersAsync(
TRACE_EVENT0("wayland", "GbmSurfacelessWayland::SwapBuffersAsync");
// If last swap failed, don't try to schedule new ones.
if (!last_swap_buffers_result_) {
- std::move(completion_callback).Run(gfx::SwapResult::SWAP_FAILED, nullptr);
+ std::move(completion_callback)
+ .Run(gfx::SwapCompletionResult(gfx::SwapResult::SWAP_FAILED));
// Notify the caller, the buffer is never presented on a screen.
std::move(presentation_callback).Run(gfx::PresentationFeedback::Failure());
return;
@@ -102,21 +108,35 @@ void GbmSurfacelessWayland::SwapBuffersAsync(
PendingFrame* frame = unsubmitted_frames_.back().get();
frame->completion_callback = std::move(completion_callback);
frame->presentation_callback = std::move(presentation_callback);
+ frame->ScheduleOverlayPlanes(widget_);
+
unsubmitted_frames_.push_back(std::make_unique<PendingFrame>());
- if (!use_egl_fence_sync_) {
+ if (!use_egl_fence_sync_ || !frame->schedule_planes_succeeded) {
frame->ready = true;
SubmitFrame();
return;
}
- // TODO: the following should be replaced by a per surface flush as it gets
- // implemented in GL drivers.
- EGLSyncKHR fence = InsertFence(has_implicit_external_sync_);
- CHECK_NE(fence, EGL_NO_SYNC_KHR) << "eglCreateSyncKHR failed";
+ std::vector<std::unique_ptr<gfx::GpuFence>> fences;
+ // Uset in-fences provided in the overlays. If there are none, we insert our
+ // own fence and wait.
+ for (auto& plane : frame->planes) {
+ if (plane.plane.gpu_fence)
+ fences.push_back(std::move(plane.plane.gpu_fence));
+ }
+
+ base::OnceClosure fence_wait_task;
+ if (!fences.empty()) {
+ fence_wait_task = base::BindOnce(&WaitForGpuFences, std::move(fences));
+ } else {
+ // TODO: the following should be replaced by a per surface flush as it gets
+ // implemented in GL drivers.
+ EGLSyncKHR fence = InsertFence(has_implicit_external_sync_);
+ CHECK_NE(fence, EGL_NO_SYNC_KHR) << "eglCreateSyncKHR failed";
- base::OnceClosure fence_wait_task =
- base::BindOnce(&WaitForFence, GetDisplay(), fence);
+ fence_wait_task = base::BindOnce(&WaitForEGLFence, GetDisplay(), fence);
+ }
base::OnceClosure fence_retired_callback = base::BindOnce(
&GbmSurfacelessWayland::FenceRetired, weak_factory_.GetWeakPtr(), frame);
@@ -181,12 +201,14 @@ GbmSurfacelessWayland::PendingFrame::PendingFrame() {}
GbmSurfacelessWayland::PendingFrame::~PendingFrame() {}
-bool GbmSurfacelessWayland::PendingFrame::ScheduleOverlayPlanes(
+void GbmSurfacelessWayland::PendingFrame::ScheduleOverlayPlanes(
gfx::AcceleratedWidget widget) {
- for (auto& overlay : overlays)
+ for (auto& overlay : overlays) {
if (!overlay.ScheduleOverlayPlane(widget))
- return false;
- return true;
+ return;
+ }
+ schedule_planes_succeeded = true;
+ return;
}
void GbmSurfacelessWayland::PendingFrame::Flush() {
@@ -201,14 +223,11 @@ void GbmSurfacelessWayland::SubmitFrame() {
submitted_frame_ = std::move(unsubmitted_frames_.front());
unsubmitted_frames_.erase(unsubmitted_frames_.begin());
- bool schedule_planes_succeeded =
- submitted_frame_->ScheduleOverlayPlanes(widget_);
-
- if (!schedule_planes_succeeded) {
+ if (!submitted_frame_->schedule_planes_succeeded) {
last_swap_buffers_result_ = false;
std::move(submitted_frame_->completion_callback)
- .Run(gfx::SwapResult::SWAP_FAILED, nullptr);
+ .Run(gfx::SwapCompletionResult(gfx::SwapResult::SWAP_FAILED));
// Notify the caller, the buffer is never presented on a screen.
std::move(submitted_frame_->presentation_callback)
.Run(gfx::PresentationFeedback::Failure());
@@ -217,11 +236,13 @@ void GbmSurfacelessWayland::SubmitFrame() {
return;
}
- submitted_frame_->buffer_id = planes_.back().buffer_id;
- buffer_manager_->CommitBuffer(widget_, submitted_frame_->buffer_id,
+ DCHECK_EQ(submitted_frame_->planes.size(), 1u);
+ submitted_frame_->buffer_id = submitted_frame_->planes.back().buffer_id;
+ buffer_manager_->CommitBuffer(widget_,
+ submitted_frame_->planes.back().buffer_id,
submitted_frame_->damage_region_);
- planes_.clear();
+ submitted_frame_->planes.clear();
}
}
@@ -247,7 +268,8 @@ void GbmSurfacelessWayland::OnSubmission(uint32_t buffer_id,
submitted_frame_->overlays.clear();
DCHECK_EQ(submitted_frame_->buffer_id, buffer_id);
- std::move(submitted_frame_->completion_callback).Run(swap_result, nullptr);
+ std::move(submitted_frame_->completion_callback)
+ .Run(gfx::SwapCompletionResult(swap_result));
pending_presentation_frames_.push_back(std::move(submitted_frame_));
diff --git a/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h b/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h
index 991d1718fad..137bea20432 100644
--- a/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h
+++ b/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h
@@ -71,11 +71,19 @@ class GbmSurfacelessWayland : public gl::SurfacelessEGL,
void OnPresentation(uint32_t buffer_id,
const gfx::PresentationFeedback& feedback) override;
+ struct PlaneData {
+ OverlayPlane plane;
+ // The id of the buffer, which represents buffer that backs this overlay
+ // plane.
+ const uint32_t buffer_id;
+ };
+
struct PendingFrame {
PendingFrame();
~PendingFrame();
- bool ScheduleOverlayPlanes(gfx::AcceleratedWidget widget);
+ // Queues overlay configs to |planes|.
+ void ScheduleOverlayPlanes(gfx::AcceleratedWidget widget);
void Flush();
bool ready = false;
@@ -90,11 +98,9 @@ class GbmSurfacelessWayland : public gl::SurfacelessEGL,
std::vector<gl::GLSurfaceOverlay> overlays;
SwapCompletionCallback completion_callback;
PresentationCallback presentation_callback;
- };
- struct PlaneData {
- OverlayPlane plane;
- const uint32_t buffer_id;
+ bool schedule_planes_succeeded = false;
+ std::vector<PlaneData> planes;
};
void SubmitFrame();
@@ -106,7 +112,6 @@ class GbmSurfacelessWayland : public gl::SurfacelessEGL,
void SetNoGLFlushForTests();
WaylandBufferManagerGpu* const buffer_manager_;
- std::vector<PlaneData> planes_;
// The native surface. Deleting this is allowed to free the EGLNativeWindow.
gfx::AcceleratedWidget widget_;
diff --git a/chromium/ui/ozone/platform/wayland/gpu/gl_surface_egl_readback_wayland.cc b/chromium/ui/ozone/platform/wayland/gpu/gl_surface_egl_readback_wayland.cc
index 60c70c7e82e..195d061926c 100644
--- a/chromium/ui/ozone/platform/wayland/gpu/gl_surface_egl_readback_wayland.cc
+++ b/chromium/ui/ozone/platform/wayland/gpu/gl_surface_egl_readback_wayland.cc
@@ -145,7 +145,8 @@ void GLSurfaceEglReadbackWayland::OnSubmission(
in_flight_pixel_buffers_.pop_front();
DCHECK(!completion_callbacks_.empty());
- std::move(completion_callbacks_.front()).Run(swap_result, nullptr);
+ std::move(completion_callbacks_.front())
+ .Run(gfx::SwapCompletionResult(swap_result));
completion_callbacks_.erase(completion_callbacks_.begin());
}
diff --git a/chromium/ui/ozone/platform/wayland/gpu/wayland_surface_factory_unittest.cc b/chromium/ui/ozone/platform/wayland/gpu/wayland_surface_factory_unittest.cc
index 585e5666283..aa649253a68 100644
--- a/chromium/ui/ozone/platform/wayland/gpu/wayland_surface_factory_unittest.cc
+++ b/chromium/ui/ozone/platform/wayland/gpu/wayland_surface_factory_unittest.cc
@@ -21,6 +21,7 @@
#include "ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h"
#include "ui/ozone/platform/wayland/host/wayland_window.h"
#include "ui/ozone/platform/wayland/test/mock_surface.h"
+#include "ui/ozone/platform/wayland/test/scoped_wl_array.h"
#include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h"
#include "ui/ozone/platform/wayland/test/test_zwp_linux_buffer_params.h"
#include "ui/ozone/platform/wayland/test/wayland_test.h"
@@ -126,8 +127,7 @@ class CallbacksHelper {
// way.
void FinishSwapBuffersAsync(uint32_t local_swap_id,
scoped_refptr<FakeGLImageNativePixmap> gl_image,
- gfx::SwapResult result,
- std::unique_ptr<gfx::GpuFence> gpu_fence) {
+ gfx::SwapCompletionResult result) {
last_finish_swap_id_ = pending_local_swap_ids_.front();
pending_local_swap_ids_.pop();
diff --git a/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_device.cc b/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_device.cc
index 23e2eaf306f..1a68f9da34e 100644
--- a/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_device.cc
+++ b/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_device.cc
@@ -8,6 +8,7 @@
#include "ui/ozone/platform/wayland/host/gtk_primary_selection_offer.h"
#include "ui/ozone/platform/wayland/host/wayland_connection.h"
+#include "ui/ozone/platform/wayland/host/wayland_data_source.h"
namespace ui {
@@ -25,6 +26,14 @@ GtkPrimarySelectionDevice::GtkPrimarySelectionDevice(
GtkPrimarySelectionDevice::~GtkPrimarySelectionDevice() = default;
+void GtkPrimarySelectionDevice::SetSelectionSource(
+ GtkPrimarySelectionSource* source) {
+ DCHECK(source);
+ gtk_primary_selection_device_set_selection(
+ data_device_.get(), source->data_source(), connection()->serial());
+ connection()->ScheduleFlush();
+}
+
// static
void GtkPrimarySelectionDevice::OnDataOffer(
void* data,
diff --git a/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_device.h b/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_device.h
index fc567936fe3..4bb586bffc3 100644
--- a/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_device.h
+++ b/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_device.h
@@ -13,6 +13,7 @@
#include "base/macros.h"
#include "ui/ozone/platform/wayland/common/wayland_object.h"
#include "ui/ozone/platform/wayland/host/wayland_data_device_base.h"
+#include "ui/ozone/platform/wayland/host/wayland_data_source.h"
struct gtk_primary_selection_device;
@@ -31,6 +32,8 @@ class GtkPrimarySelectionDevice : public WaylandDataDeviceBase {
return data_device_.get();
}
+ void SetSelectionSource(GtkPrimarySelectionSource* source);
+
private:
// gtk_primary_selection_device_listener callbacks
static void OnDataOffer(void* data,
diff --git a/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_device_manager.cc b/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_device_manager.cc
index 90a8225bd73..3d49d7a4576 100644
--- a/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_device_manager.cc
+++ b/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_device_manager.cc
@@ -6,33 +6,42 @@
#include <gtk-primary-selection-client-protocol.h>
-#include "ui/ozone/platform/wayland/host/gtk_primary_selection_source.h"
+#include <memory>
+
+#include "ui/ozone/platform/wayland/host/gtk_primary_selection_device.h"
#include "ui/ozone/platform/wayland/host/wayland_connection.h"
+#include "ui/ozone/platform/wayland/host/wayland_data_source.h"
namespace ui {
GtkPrimarySelectionDeviceManager::GtkPrimarySelectionDeviceManager(
gtk_primary_selection_device_manager* manager,
WaylandConnection* connection)
- : gtk_primary_selection_device_manager_(manager), connection_(connection) {
+ : device_manager_(manager), connection_(connection) {
DCHECK(connection_);
- DCHECK(gtk_primary_selection_device_manager_);
+ DCHECK(device_manager_);
}
GtkPrimarySelectionDeviceManager::~GtkPrimarySelectionDeviceManager() = default;
-gtk_primary_selection_device* GtkPrimarySelectionDeviceManager::GetDevice() {
+GtkPrimarySelectionDevice* GtkPrimarySelectionDeviceManager::GetDevice() {
DCHECK(connection_->seat());
- return gtk_primary_selection_device_manager_get_device(
- gtk_primary_selection_device_manager_.get(), connection_->seat());
+ if (!device_) {
+ device_ = std::make_unique<GtkPrimarySelectionDevice>(
+ connection_, gtk_primary_selection_device_manager_get_device(
+ device_manager_.get(), connection_->seat()));
+ }
+ DCHECK(device_);
+ return device_.get();
}
std::unique_ptr<GtkPrimarySelectionSource>
-GtkPrimarySelectionDeviceManager::CreateSource() {
- gtk_primary_selection_source* data_source =
- gtk_primary_selection_device_manager_create_source(
- gtk_primary_selection_device_manager_.get());
- return std::make_unique<GtkPrimarySelectionSource>(data_source, connection_);
+GtkPrimarySelectionDeviceManager::CreateSource(
+ GtkPrimarySelectionSource::Delegate* delegate) {
+ auto* data_source =
+ gtk_primary_selection_device_manager_create_source(device_manager_.get());
+ return std::make_unique<GtkPrimarySelectionSource>(data_source, connection_,
+ delegate);
}
} // namespace ui
diff --git a/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_device_manager.h b/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_device_manager.h
index ea05aa38882..b059c2c8621 100644
--- a/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_device_manager.h
+++ b/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_device_manager.h
@@ -7,34 +7,38 @@
#include <memory>
-#include "base/macros.h"
#include "ui/ozone/platform/wayland/common/wayland_object.h"
-
-struct gtk_primary_selection_device_manager;
-struct gtk_primary_selection_device;
+#include "ui/ozone/platform/wayland/host/wayland_data_source.h"
namespace ui {
-class GtkPrimarySelectionSource;
+class GtkPrimarySelectionDevice;
class WaylandConnection;
class GtkPrimarySelectionDeviceManager {
public:
+ using DataSource = GtkPrimarySelectionSource;
+ using DataDevice = GtkPrimarySelectionDevice;
+
GtkPrimarySelectionDeviceManager(
gtk_primary_selection_device_manager* manager,
WaylandConnection* connection);
+ GtkPrimarySelectionDeviceManager(const GtkPrimarySelectionDeviceManager&) =
+ delete;
+ GtkPrimarySelectionDeviceManager& operator=(
+ const GtkPrimarySelectionDeviceManager&) = delete;
~GtkPrimarySelectionDeviceManager();
- gtk_primary_selection_device* GetDevice();
- std::unique_ptr<GtkPrimarySelectionSource> CreateSource();
+ GtkPrimarySelectionDevice* GetDevice();
+ std::unique_ptr<GtkPrimarySelectionSource> CreateSource(
+ GtkPrimarySelectionSource::Delegate* delegate);
private:
- wl::Object<gtk_primary_selection_device_manager>
- gtk_primary_selection_device_manager_;
+ wl::Object<gtk_primary_selection_device_manager> device_manager_;
- WaylandConnection* connection_;
+ WaylandConnection* const connection_;
- DISALLOW_COPY_AND_ASSIGN(GtkPrimarySelectionDeviceManager);
+ std::unique_ptr<GtkPrimarySelectionDevice> device_;
};
} // namespace ui
diff --git a/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_source.cc b/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_source.cc
deleted file mode 100644
index 7160cdd83fa..00000000000
--- a/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_source.cc
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/ozone/platform/wayland/host/gtk_primary_selection_source.h"
-
-#include <gtk-primary-selection-client-protocol.h>
-
-#include "base/check.h"
-#include "base/files/file_util.h"
-#include "ui/base/clipboard/clipboard_constants.h"
-#include "ui/ozone/platform/wayland/host/wayland_connection.h"
-
-namespace ui {
-
-GtkPrimarySelectionSource::GtkPrimarySelectionSource(
- gtk_primary_selection_source* data_source,
- WaylandConnection* connection)
- : data_source_(data_source), connection_(connection) {
- DCHECK(connection_);
- DCHECK(data_source_);
-
- static const struct gtk_primary_selection_source_listener
- kDataSourceListener = {GtkPrimarySelectionSource::OnSend,
- GtkPrimarySelectionSource::OnCancelled};
- gtk_primary_selection_source_add_listener(data_source_.get(),
- &kDataSourceListener, this);
-}
-
-GtkPrimarySelectionSource::~GtkPrimarySelectionSource() = default;
-
-// static
-void GtkPrimarySelectionSource::OnSend(void* data,
- gtk_primary_selection_source* source,
- const char* mime_type,
- int32_t fd) {
- GtkPrimarySelectionSource* self =
- static_cast<GtkPrimarySelectionSource*>(data);
- std::string contents;
- base::Optional<std::vector<uint8_t>> mime_data;
- self->GetClipboardData(mime_type, &mime_data);
- if (!mime_data.has_value() && strcmp(mime_type, kMimeTypeTextUtf8) == 0)
- self->GetClipboardData(kMimeTypeText, &mime_data);
- contents.assign(mime_data->begin(), mime_data->end());
- bool result =
- base::WriteFileDescriptor(fd, contents.data(), contents.length());
- DCHECK(result);
- close(fd);
-}
-
-// static
-void GtkPrimarySelectionSource::OnCancelled(
- void* data,
- gtk_primary_selection_source* source) {
- GtkPrimarySelectionSource* self =
- static_cast<GtkPrimarySelectionSource*>(data);
- self->connection_->clipboard()->DataSourceCancelled(
- ClipboardBuffer::kSelection);
-}
-
-void GtkPrimarySelectionSource::WriteToClipboard(
- const PlatformClipboard::DataMap& data_map) {
- for (const auto& data : data_map) {
- gtk_primary_selection_source_offer(data_source_.get(), data.first.c_str());
- if (strcmp(data.first.c_str(), kMimeTypeText) == 0)
- gtk_primary_selection_source_offer(data_source_.get(), kMimeTypeTextUtf8);
- }
-
- gtk_primary_selection_device_set_selection(
- connection_->primary_selection_device(), data_source_.get(),
- connection_->serial());
-
- connection_->ScheduleFlush();
-}
-
-} // namespace ui
diff --git a/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_source.h b/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_source.h
deleted file mode 100644
index 994fffa25d3..00000000000
--- a/chromium/ui/ozone/platform/wayland/host/gtk_primary_selection_source.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_GTK_PRIMARY_SELECTION_SOURCE_H_
-#define UI_OZONE_PLATFORM_WAYLAND_HOST_GTK_PRIMARY_SELECTION_SOURCE_H_
-
-#include "base/macros.h"
-#include "ui/ozone/platform/wayland/common/wayland_object.h"
-#include "ui/ozone/platform/wayland/host/wayland_data_source_base.h"
-#include "ui/ozone/public/platform_clipboard.h"
-
-struct gtk_primary_selection_source;
-
-namespace ui {
-
-class WaylandConnection;
-
-class GtkPrimarySelectionSource : public WaylandDataSourceBase {
- public:
- // Takes ownership of data_source.
- GtkPrimarySelectionSource(gtk_primary_selection_source* data_source,
- WaylandConnection* connection);
- ~GtkPrimarySelectionSource() override;
-
- void WriteToClipboard(const PlatformClipboard::DataMap& data_map) override;
-
- private:
- // gtk_primary_selection_source_listener callbacks
- static void OnSend(void* data,
- gtk_primary_selection_source* source,
- const char* mime_type,
- int32_t fd);
- static void OnCancelled(void* data, gtk_primary_selection_source* source);
-
- // The gtk_primary_selection_source wrapped by this instance.
- wl::Object<gtk_primary_selection_source> data_source_;
-
- WaylandConnection* connection_ = nullptr;
-
- DISALLOW_COPY_AND_ASSIGN(GtkPrimarySelectionSource);
-};
-
-} // namespace ui
-
-#endif // UI_OZONE_PLATFORM_WAYLAND_HOST_GTK_PRIMARY_SELECTION_SOURCE_H_
diff --git a/chromium/ui/ozone/platform/wayland/host/shell_object_factory.cc b/chromium/ui/ozone/platform/wayland/host/shell_object_factory.cc
index 57383be20da..dea71480eb6 100644
--- a/chromium/ui/ozone/platform/wayland/host/shell_object_factory.cc
+++ b/chromium/ui/ozone/platform/wayland/host/shell_object_factory.cc
@@ -4,6 +4,7 @@
#include "ui/ozone/platform/wayland/host/shell_object_factory.h"
+#include "base/logging.h"
#include "ui/ozone/platform/wayland/host/wayland_connection.h"
#include "ui/ozone/platform/wayland/host/xdg_popup_wrapper_impl.h"
#include "ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.h"
@@ -44,4 +45,4 @@ std::unique_ptr<ShellPopupWrapper> ShellObjectFactory::CreateShellPopupWrapper(
return nullptr;
}
-} // namespace ui \ No newline at end of file
+} // namespace ui
diff --git a/chromium/ui/ozone/platform/wayland/host/shell_popup_wrapper.cc b/chromium/ui/ozone/platform/wayland/host/shell_popup_wrapper.cc
index 1dc1f4d4f76..f9f5aaa0825 100644
--- a/chromium/ui/ozone/platform/wayland/host/shell_popup_wrapper.cc
+++ b/chromium/ui/ozone/platform/wayland/host/shell_popup_wrapper.cc
@@ -4,6 +4,9 @@
#include "ui/ozone/platform/wayland/host/shell_popup_wrapper.h"
+#include "base/check_op.h"
+#include "base/notreached.h"
+
namespace ui {
constexpr uint32_t kAnchorDefaultWidth = 1;
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc b/chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc
index 96c4099fdb8..8320574a540 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc
@@ -16,6 +16,7 @@
#include "ui/ozone/platform/wayland/host/wayland_connection.h"
#include "ui/ozone/platform/wayland/host/wayland_drm.h"
#include "ui/ozone/platform/wayland/host/wayland_shm.h"
+#include "ui/ozone/platform/wayland/host/wayland_surface.h"
#include "ui/ozone/platform/wayland/host/wayland_window.h"
#include "ui/ozone/platform/wayland/host/wayland_zwp_linux_dmabuf.h"
@@ -55,10 +56,10 @@ std::string NumberToString(uint32_t number) {
class WaylandBufferManagerHost::Surface {
public:
- Surface(WaylandWindow* window,
+ Surface(WaylandSurface* wayland_surface,
WaylandConnection* connection,
WaylandBufferManagerHost* buffer_manager)
- : window_(window),
+ : wayland_surface_(wayland_surface),
connection_(connection),
buffer_manager_(buffer_manager) {}
~Surface() = default;
@@ -67,7 +68,7 @@ class WaylandBufferManagerHost::Surface {
DCHECK(!pending_buffer_);
// The window has already been destroyed.
- if (!window_)
+ if (!wayland_surface_)
return true;
WaylandBuffer* buffer = GetBuffer(buffer_id);
@@ -95,23 +96,9 @@ class WaylandBufferManagerHost::Surface {
if (buffer->attached && !buffer->wl_buffer)
return false;
- // This request may come earlier than the Wayland compositor has imported a
- // wl_buffer. Wait until the buffer is created. The wait takes place only
- // once. Though, the case when a request to attach a buffer comes earlier
- // than the wl_buffer is created does not happen often. 1) Depending on the
- // zwp linux dmabuf protocol version, the wl_buffer can be created
- // immediately without asynchronous wait 2) the wl_buffer can have been
- // created by this time.
- //
- // Another case, which always happen is waiting until the frame callback is
- // completed. Thus, wait here when the Wayland compositor fires the frame
- // callback.
- if (!buffer->wl_buffer || wl_frame_callback_) {
- pending_buffer_ = buffer;
- return true;
- }
-
- return CommitBufferInternal(buffer);
+ pending_buffer_ = buffer;
+ MaybeProcessPendingBuffer();
+ return true;
}
bool CreateBuffer(const gfx::Size& size, uint32_t buffer_id) {
@@ -140,7 +127,7 @@ class WaylandBufferManagerHost::Surface {
// the client about successful swap.
// If the window has already been destroyed, no need to complete the
// submission.
- if (buffer && !buffer->released && submitted_buffer_ && window_)
+ if (buffer && !buffer->released && submitted_buffer_ && wayland_surface_)
CompleteSubmission();
if (prev_submitted_buffer_ == buffer)
@@ -167,8 +154,8 @@ class WaylandBufferManagerHost::Surface {
if (buffer->wl_buffer)
SetupBufferReleaseListener(buffer);
- if (pending_buffer_ == buffer && !wl_frame_callback_)
- ProcessPendingBuffer();
+ if (pending_buffer_ == buffer)
+ MaybeProcessPendingBuffer();
}
void ClearState() {
@@ -186,17 +173,23 @@ class WaylandBufferManagerHost::Surface {
}
void ResetSurfaceContents() {
- if (!window_)
+ if (!wayland_surface_)
return;
- wl_surface_attach(window_->surface(), nullptr, 0, 0);
- wl_surface_commit(window_->surface());
+ wl_surface_attach(wayland_surface_->surface(), nullptr, 0, 0);
+ wl_surface_commit(wayland_surface_->surface());
// We cannot reset |prev_submitted_buffer_| here as long as the surface
// might have attached a new buffer and is about to receive a release
// callback. Check more comments below where the variable is declared.
contents_reset_ = true;
+ // ResetSurfaceContents happens upon WaylandWindow::Hide call, which
+ // destroyes xdg_surface, xdg_popup, etc. They are going to be reinitialized
+ // once WaylandWindow::Show is called. Thus, they will have to be configured
+ // once again before buffers can be attached.
+ configured_ = false;
+
connection_->ScheduleFlush();
}
@@ -207,8 +200,16 @@ class WaylandBufferManagerHost::Surface {
bool HasBuffers() const { return !buffers_.empty(); }
- void OnWindowRemoved() { window_ = nullptr; }
- bool HasWindow() const { return !!window_; }
+ void OnWindowRemoved() { wayland_surface_ = nullptr; }
+ bool HasWindow() const { return !!wayland_surface_; }
+
+ void OnWindowConfigured() {
+ if (configured_)
+ return;
+
+ configured_ = true;
+ MaybeProcessPendingBuffer();
+ }
private:
struct FeedbackInfo {
@@ -226,7 +227,7 @@ class WaylandBufferManagerHost::Surface {
using PresentationFeedbackQueue = std::vector<FeedbackInfo>;
bool CommitBufferInternal(WaylandBuffer* buffer) {
- DCHECK(buffer && window_);
+ DCHECK(buffer && wayland_surface_);
DCHECK(!pending_buffer_);
DCHECK(!submitted_buffer_);
@@ -274,7 +275,7 @@ class WaylandBufferManagerHost::Surface {
}
void DamageBuffer(WaylandBuffer* buffer) {
- DCHECK(window_);
+ DCHECK(wayland_surface_);
gfx::Rect pending_damage_region = std::move(buffer->damage_region);
// If the size of the damage region is empty, wl_surface_damage must be
@@ -290,10 +291,10 @@ class WaylandBufferManagerHost::Surface {
// https://bit.ly/2u00lv6 for details.
// We don't need to apply any scaling because pending_damage_region is
// already in buffer coordinates.
- wl_surface_damage_buffer(window_->surface(), pending_damage_region.x(),
- pending_damage_region.y(),
- pending_damage_region.width(),
- pending_damage_region.height());
+ wl_surface_damage_buffer(
+ wayland_surface_->surface(), pending_damage_region.x(),
+ pending_damage_region.y(), pending_damage_region.width(),
+ pending_damage_region.height());
} else {
// The calculation for damage region relies on two assumptions:
// 1) The buffer is always attached at surface location (0, 0)
@@ -304,8 +305,9 @@ class WaylandBufferManagerHost::Surface {
// Note: The damage region may not be an integer multiple of scale. To
// keep the implementation simple, the x() and y() coordinates round down,
// and the width() and height() calculations always add an extra pixel.
- int scale = window_->buffer_scale();
- wl_surface_damage(window_->surface(), pending_damage_region.x() / scale,
+ int scale = wayland_surface_->buffer_scale();
+ wl_surface_damage(wayland_surface_->surface(),
+ pending_damage_region.x() / scale,
pending_damage_region.y() / scale,
pending_damage_region.width() / scale + 1,
pending_damage_region.height() / scale + 1);
@@ -313,31 +315,32 @@ class WaylandBufferManagerHost::Surface {
}
void AttachBuffer(WaylandBuffer* buffer) {
- DCHECK(window_);
+ DCHECK(wayland_surface_ && configured_);
// The logic in DamageBuffer currently relies on attachment coordinates of
// (0, 0). If this changes, then the calculation in DamageBuffer will also
// need to be updated.
- wl_surface_attach(window_->surface(), buffer->wl_buffer.get(), 0, 0);
+ wl_surface_attach(wayland_surface_->surface(), buffer->wl_buffer.get(), 0,
+ 0);
}
void CommitSurface() {
- DCHECK(window_);
- wl_surface_commit(window_->surface());
+ DCHECK(wayland_surface_);
+ wl_surface_commit(wayland_surface_->surface());
}
void SetupFrameCallback() {
- DCHECK(window_);
+ DCHECK(wayland_surface_);
static const wl_callback_listener frame_listener = {
&Surface::FrameCallbackDone};
DCHECK(!wl_frame_callback_);
- wl_frame_callback_.reset(wl_surface_frame(window_->surface()));
+ wl_frame_callback_.reset(wl_surface_frame(wayland_surface_->surface()));
wl_callback_add_listener(wl_frame_callback_.get(), &frame_listener, this);
}
void SetupPresentationFeedback(uint32_t buffer_id) {
- DCHECK(window_);
+ DCHECK(wayland_surface_);
// Set up presentation feedback.
if (!connection_->presentation())
return;
@@ -348,7 +351,7 @@ class WaylandBufferManagerHost::Surface {
feedback_queue_.push_back(
{wl::Object<struct wp_presentation_feedback>(wp_presentation_feedback(
- connection_->presentation(), window_->surface())),
+ connection_->presentation(), wayland_surface_->surface())),
buffer_id, /*feedback=*/base::nullopt,
/*submission_completed=*/false});
wp_presentation_feedback_add_listener(
@@ -372,7 +375,7 @@ class WaylandBufferManagerHost::Surface {
DCHECK(wl_frame_callback_.get() == callback);
wl_frame_callback_.reset();
- ProcessPendingBuffer();
+ MaybeProcessPendingBuffer();
}
// wl_callback_listener
@@ -438,13 +441,13 @@ class WaylandBufferManagerHost::Surface {
prev_submitted_buffer_ = submitted_buffer_;
submitted_buffer_ = nullptr;
- if (!window_)
+ if (!wayland_surface_)
return;
// We can now complete the latest submission. We had to wait for this
// release because SwapCompletionCallback indicates to the client that the
// previous buffer is available for reuse.
- buffer_manager_->OnSubmission(window_->GetWidget(), id,
+ buffer_manager_->OnSubmission(wayland_surface_->GetRootWidget(), id,
gfx::SwapResult::SWAP_ACK);
// If presentation feedback is not supported, use a fake feedback. This
@@ -452,7 +455,7 @@ class WaylandBufferManagerHost::Surface {
if (!connection_->presentation()) {
DCHECK(feedback_queue_.empty());
buffer_manager_->OnPresentation(
- window_->GetWidget(), id,
+ wayland_surface_->GetWidget(), id,
gfx::PresentationFeedback(base::TimeTicks::Now(), base::TimeDelta(),
GetPresentationKindFlags(0)));
} else {
@@ -468,13 +471,20 @@ class WaylandBufferManagerHost::Surface {
}
void OnPresentation(struct wp_presentation_feedback* wp_presentation_feedback,
- const gfx::PresentationFeedback& feedback) {
+ const gfx::PresentationFeedback& feedback,
+ bool discarded = false) {
FeedbackInfo* feedback_info = nullptr;
for (auto& info : feedback_queue_) {
if (info.wp_presentation_feedback.get() == wp_presentation_feedback) {
feedback_info = &info;
break;
- } else if (!info.feedback.has_value()) { // Feedback must come in order.
+ } else if (!info.feedback.has_value() && !discarded) {
+ // Feedback must come in order. However, if one of the feedbacks was
+ // discarded and the previous feedbacks haven't been received yet, don't
+ // mark previous feedbacks as failed as they will come later. For
+ // example, imagine you are waiting for f[0], f[1] and f[2]. f[2] gets
+ // discarded, previous ones mustn't be marked as failed as they will
+ // come later.
info.feedback = gfx::PresentationFeedback::Failure();
}
}
@@ -499,15 +509,15 @@ class WaylandBufferManagerHost::Surface {
// This function ensures that we send OnPresentation for each buffer that
// already has had OnSubmission called for it (condition #2).
void ProcessPresentationFeedbacks() {
- if (!window_)
+ if (!wayland_surface_)
return;
while (!feedback_queue_.empty()) {
const auto& info = feedback_queue_.front();
if (!info.submission_completed || !info.feedback.has_value())
break;
- buffer_manager_->OnPresentation(window_->GetWidget(), info.buffer_id,
- *info.feedback);
+ buffer_manager_->OnPresentation(wayland_surface_->GetWidget(),
+ info.buffer_id, *info.feedback);
feedback_queue_.erase(feedback_queue_.begin());
}
// This queue should be small - if not it's likely a bug.
@@ -546,11 +556,31 @@ class WaylandBufferManagerHost::Surface {
Surface* self = static_cast<Surface*>(data);
DCHECK(self);
self->OnPresentation(wp_presentation_feedback,
- gfx::PresentationFeedback::Failure());
+ gfx::PresentationFeedback::Failure(),
+ true /* discarded */);
}
- void ProcessPendingBuffer() {
- if (!pending_buffer_ || !window_)
+ void MaybeProcessPendingBuffer() {
+ // There is nothing to process if there is no pending buffer or the window
+ // has been destroyed.
+ if (!pending_buffer_ || !wayland_surface_)
+ return;
+
+ // This request may come earlier than the Wayland compositor has imported a
+ // wl_buffer. Wait until the buffer is created. The wait takes place only
+ // once. Though, the case when a request to attach a buffer comes earlier
+ // than the wl_buffer is created does not happen often. 1) Depending on the
+ // zwp linux dmabuf protocol version, the wl_buffer can be created
+ // immediately without asynchronous wait 2) the wl_buffer can have been
+ // created by this time.
+ //
+ // Another case, which always happen is waiting until the frame callback is
+ // completed. Thus, wait here when the Wayland compositor fires the frame
+ // callback.
+ //
+ // The third case happens if the window hasn't been configured until a
+ // request to attach a buffer to its surface is sent.
+ if (!pending_buffer_->wl_buffer || wl_frame_callback_ || !configured_)
return;
auto* buffer = pending_buffer_;
@@ -562,7 +592,7 @@ class WaylandBufferManagerHost::Surface {
// WaylandWindow.
// Non-owned. The window this helper surface stores and submits buffers for.
- const WaylandWindow* window_;
+ const WaylandSurface* wayland_surface_;
// Non-owned pointer to the connection.
WaylandConnection* const connection_;
@@ -598,6 +628,11 @@ class WaylandBufferManagerHost::Surface {
// a need to call submission callback manually.
bool contents_reset_ = false;
+ // If WaylandWindow has never been configured, do not try to attach
+ // buffers to its surface. Otherwise, Wayland server will drop the connection
+ // and send an error - "The surface has never been configured.".
+ bool configured_ = false;
+
DISALLOW_COPY_AND_ASSIGN(Surface);
};
@@ -619,7 +654,7 @@ WaylandBufferManagerHost::~WaylandBufferManagerHost() {
void WaylandBufferManagerHost::OnWindowAdded(WaylandWindow* window) {
DCHECK(window);
surfaces_[window->GetWidget()] =
- std::make_unique<Surface>(window, connection_, this);
+ std::make_unique<Surface>(window->wayland_surface(), connection_, this);
}
void WaylandBufferManagerHost::OnWindowRemoved(WaylandWindow* window) {
@@ -632,6 +667,13 @@ void WaylandBufferManagerHost::OnWindowRemoved(WaylandWindow* window) {
surfaces_.erase(it);
}
+void WaylandBufferManagerHost::OnWindowConfigured(WaylandWindow* window) {
+ DCHECK(window);
+ auto it = surfaces_.find(window->GetWidget());
+ DCHECK(it != surfaces_.end());
+ it->second->OnWindowConfigured();
+}
+
void WaylandBufferManagerHost::SetTerminateGpuCallback(
base::OnceCallback<void(std::string)> terminate_callback) {
terminate_gpu_cb_ = std::move(terminate_callback);
@@ -923,11 +965,10 @@ bool WaylandBufferManagerHost::ValidateBufferIdFromGpu(uint32_t buffer_id) {
return true;
}
-bool WaylandBufferManagerHost::ValidateDataFromGpu(
- const base::ScopedFD& fd,
- size_t length,
- const gfx::Size& size,
- uint32_t buffer_id) {
+bool WaylandBufferManagerHost::ValidateDataFromGpu(const base::ScopedFD& fd,
+ size_t length,
+ const gfx::Size& size,
+ uint32_t buffer_id) {
if (!ValidateBufferIdFromGpu(buffer_id))
return false;
@@ -952,21 +993,20 @@ bool WaylandBufferManagerHost::ValidateDataFromGpu(
void WaylandBufferManagerHost::OnCreateBufferComplete(
uint32_t buffer_id,
wl::Object<struct wl_buffer> new_buffer) {
- auto it = anonymous_buffers_.find(buffer_id);
- // It might have already been destroyed or stored by any of the surfaces.
- if (it != anonymous_buffers_.end()) {
- it->second->wl_buffer = std::move(new_buffer);
- } else {
- for (auto& surface : surfaces_) {
- if (surface.second->BufferExists(buffer_id)) {
- surface.second.get()->AttachWlBuffer(buffer_id,
- std::move(new_buffer));
- break;
- }
+ auto it = anonymous_buffers_.find(buffer_id);
+ // It might have already been destroyed or stored by any of the surfaces.
+ if (it != anonymous_buffers_.end()) {
+ it->second->wl_buffer = std::move(new_buffer);
+ } else {
+ for (auto& surface : surfaces_) {
+ if (surface.second->BufferExists(buffer_id)) {
+ surface.second.get()->AttachWlBuffer(buffer_id, std::move(new_buffer));
+ break;
}
}
- // There is no need for the buffer anymore. Let it go out of the scope and
- // be destroyed.
+ }
+ // There is no need for the buffer anymore. Let it go out of the scope and
+ // be destroyed.
}
void WaylandBufferManagerHost::OnSubmission(
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h b/chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h
index c0c6809cad2..b467835f726 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h
@@ -81,6 +81,7 @@ class WaylandBufferManagerHost : public ozone::mojom::WaylandBufferManagerHost,
// WaylandWindowObserver implements:
void OnWindowAdded(WaylandWindow* window) override;
void OnWindowRemoved(WaylandWindow* window) override;
+ void OnWindowConfigured(WaylandWindow* window) override;
void SetTerminateGpuCallback(
base::OnceCallback<void(std::string)> terminate_gpu_cb);
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_clipboard.cc b/chromium/ui/ozone/platform/wayland/host/wayland_clipboard.cc
index 11add83d1b0..a0e848a216d 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_clipboard.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_clipboard.cc
@@ -4,28 +4,137 @@
#include "ui/ozone/platform/wayland/host/wayland_clipboard.h"
+#include <memory>
#include <string>
+#include "base/check.h"
+#include "base/notreached.h"
+#include "ui/base/clipboard/clipboard_buffer.h"
+#include "ui/base/clipboard/clipboard_constants.h"
+#include "ui/ozone/platform/wayland/common/wayland_object.h"
#include "ui/ozone/platform/wayland/host/gtk_primary_selection_device.h"
#include "ui/ozone/platform/wayland/host/gtk_primary_selection_device_manager.h"
-#include "ui/ozone/platform/wayland/host/gtk_primary_selection_source.h"
+#include "ui/ozone/platform/wayland/host/wayland_connection.h"
#include "ui/ozone/platform/wayland/host/wayland_data_device.h"
#include "ui/ozone/platform/wayland/host/wayland_data_device_manager.h"
-#include "ui/ozone/platform/wayland/host/wayland_data_source_base.h"
+#include "ui/ozone/platform/wayland/host/wayland_data_source.h"
+#include "ui/ozone/public/platform_clipboard.h"
+
+namespace wl {
+
+// Internal Wayland Clipboard interface. A wl::Clipboard implementation handles
+// a single ui::ClipboardBuffer. With this common interface it is possible to
+// seamlessly support different clipboard buffers backed by different underlying
+// Wayland protocol objects.
+class Clipboard {
+ public:
+ virtual ~Clipboard() = default;
+
+ // Synchronously retrieves the mime types list currently available to be read.
+ virtual std::vector<std::string> ReadMimeTypes() = 0;
+
+ // Asynchronously reads clipboard content with |mime_type| format. The result
+ // data is expected to arrive through WaylandClipboard::SetData().
+ // TODO(nickdiego): Decouple DataDevice impls from WaylandClipboard.
+ virtual bool Read(const std::string& mime_type) = 0;
+
+ // Synchronously stores and announces |data| as available from this clipboard.
+ virtual void Write(const ui::PlatformClipboard::DataMap* data) = 0;
+
+ // Tells if this clipboard instance is the current selection owner.
+ virtual bool IsSelectionOwner() const = 0;
+};
+
+// Templated wl::Clipboard implementation. Whereas DataSource is the data source
+// class capable of creating data offers upon clipboard writes and communicates
+// events through DataSource::Delegate, and DataDevice is its device counterpart
+// providing read and write access to the underlying data selection-related
+// protocol objects. See *_data_{source,device}.h for more details.
+template <typename Manager,
+ typename DataSource = typename Manager::DataSource,
+ typename DataDevice = typename Manager::DataDevice>
+class ClipboardImpl final : public Clipboard, public DataSource::Delegate {
+ public:
+ explicit ClipboardImpl(Manager* manager) : manager_(manager) {}
+ ClipboardImpl(const ClipboardImpl&) = delete;
+ ClipboardImpl& operator=(const ClipboardImpl&) = delete;
+ virtual ~ClipboardImpl() = default;
+
+ virtual bool Read(const std::string& mime_type) override {
+ return GetDevice()->RequestSelectionData(mime_type);
+ }
+
+ std::vector<std::string> ReadMimeTypes() override {
+ return GetDevice()->GetAvailableMimeTypes();
+ }
+
+ virtual void Write(const ui::PlatformClipboard::DataMap* data) override {
+ if (!data || data->empty()) {
+ data_.clear();
+ source_.reset();
+ } else {
+ data_ = *data;
+ if (!source_)
+ source_ = manager_->CreateSource(this);
+ source_->Offer(GetMimeTypes());
+ GetDevice()->SetSelectionSource(source_.get());
+ }
+ }
+
+ bool IsSelectionOwner() const override { return !!source_; }
+
+ private:
+ DataDevice* GetDevice() { return manager_->GetDevice(); }
+
+ std::vector<std::string> GetMimeTypes() {
+ std::vector<std::string> mime_types;
+ for (const auto& data : data_) {
+ mime_types.push_back(data.first);
+ if (data.first == ui::kMimeTypeText)
+ mime_types.push_back(ui::kMimeTypeTextUtf8);
+ }
+ return mime_types;
+ }
+
+ // WaylandDataSource::Delegate:
+ void OnDataSourceFinish(bool completed) override {
+ if (!completed)
+ Write(nullptr);
+ }
+
+ void OnDataSourceSend(const std::string& mime_type,
+ std::string* contents) override {
+ DCHECK(contents);
+ auto it = data_.find(mime_type);
+ if (it == data_.end() && mime_type == ui::kMimeTypeTextUtf8)
+ it = data_.find(ui::kMimeTypeText);
+ if (it != data_.end())
+ contents->assign(it->second.begin(), it->second.end());
+ }
+
+ // The device manager used to access data device and create data sources.
+ Manager* const manager_;
+
+ // The current data source used to offer clipboard data.
+ std::unique_ptr<DataSource> source_;
+
+ // The data currently stored in a given clipboard buffer.
+ ui::PlatformClipboard::DataMap data_;
+};
+
+} // namespace wl
namespace ui {
-WaylandClipboard::WaylandClipboard(
- WaylandDataDeviceManager* data_device_manager,
- WaylandDataDevice* data_device,
- GtkPrimarySelectionDeviceManager* primary_selection_device_manager,
- GtkPrimarySelectionDevice* primary_selection_device)
- : data_device_manager_(data_device_manager),
- data_device_(data_device),
- primary_selection_device_manager_(primary_selection_device_manager),
- primary_selection_device_(primary_selection_device) {
- DCHECK(data_device_manager_);
- DCHECK(data_device_);
+WaylandClipboard::WaylandClipboard(WaylandConnection* connection,
+ WaylandDataDeviceManager* manager)
+ : connection_(connection),
+ copypaste_clipboard_(
+ std::make_unique<wl::ClipboardImpl<WaylandDataDeviceManager>>(
+ manager)) {
+ DCHECK(manager);
+ DCHECK(connection_);
+ DCHECK(copypaste_clipboard_);
}
WaylandClipboard::~WaylandClipboard() = default;
@@ -34,25 +143,8 @@ void WaylandClipboard::OfferClipboardData(
ClipboardBuffer buffer,
const PlatformClipboard::DataMap& data_map,
PlatformClipboard::OfferDataClosure callback) {
- WaylandDataSourceBase* data_source = nullptr;
- if (buffer == ClipboardBuffer::kCopyPaste) {
- if (!clipboard_data_source_)
- clipboard_data_source_ = data_device_manager_->CreateSource();
- data_source = clipboard_data_source_.get();
- } else {
- if (!IsPrimarySelectionSupported()) {
- std::move(callback).Run();
- return;
- }
- if (!primary_data_source_)
- primary_data_source_ = primary_selection_device_manager_->CreateSource();
- data_source = primary_data_source_.get();
- }
-
- DCHECK(data_source);
- data_source->WriteToClipboard(data_map);
- data_source->set_data_map(data_map);
-
+ if (auto* clipboard = GetClipboard(buffer))
+ clipboard->Write(&data_map);
std::move(callback).Run();
}
@@ -61,25 +153,18 @@ void WaylandClipboard::RequestClipboardData(
const std::string& mime_type,
PlatformClipboard::DataMap* data_map,
PlatformClipboard::RequestDataClosure callback) {
- read_clipboard_closure_ = std::move(callback);
DCHECK(data_map);
data_map_ = data_map;
- if (buffer == ClipboardBuffer::kCopyPaste) {
- if (!data_device_->RequestSelectionData(mime_type))
- SetData({}, mime_type);
- } else {
- if (!IsPrimarySelectionSupported() ||
- !primary_selection_device_->RequestSelectionData(mime_type)) {
- SetData({}, mime_type);
- }
- }
+ read_clipboard_closure_ = std::move(callback);
+ auto* clipboard = GetClipboard(buffer);
+ if (!clipboard || !clipboard->Read(mime_type))
+ SetData({}, mime_type);
}
bool WaylandClipboard::IsSelectionOwner(ClipboardBuffer buffer) {
- if (buffer == ClipboardBuffer::kCopyPaste)
- return !!clipboard_data_source_;
- else
- return !!primary_data_source_;
+ if (auto* clipboard = GetClipboard(buffer))
+ return clipboard->IsSelectionOwner();
+ return false;
}
void WaylandClipboard::SetSequenceNumberUpdateCb(
@@ -92,26 +177,10 @@ void WaylandClipboard::SetSequenceNumberUpdateCb(
void WaylandClipboard::GetAvailableMimeTypes(
ClipboardBuffer buffer,
PlatformClipboard::GetMimeTypesClosure callback) {
- if (buffer == ClipboardBuffer::kCopyPaste) {
- std::move(callback).Run(data_device_->GetAvailableMimeTypes());
- } else {
- std::move(callback).Run(
- IsPrimarySelectionSupported()
- ? primary_selection_device_->GetAvailableMimeTypes()
- : std::vector<std::string>{});
- }
-}
-
-void WaylandClipboard::DataSourceCancelled(ClipboardBuffer buffer) {
- if (buffer == ClipboardBuffer::kCopyPaste) {
- DCHECK(clipboard_data_source_);
- SetData({}, {});
- clipboard_data_source_.reset();
- } else {
- DCHECK(primary_data_source_);
- SetData({}, {});
- primary_data_source_.reset();
- }
+ std::vector<std::string> mime_types;
+ if (auto* clipboard = GetClipboard(buffer))
+ mime_types = clipboard->ReadMimeTypes();
+ std::move(callback).Run(mime_types);
}
void WaylandClipboard::SetData(const std::vector<uint8_t>& contents,
@@ -134,8 +203,24 @@ void WaylandClipboard::UpdateSequenceNumber(ClipboardBuffer buffer) {
update_sequence_cb_.Run(buffer);
}
-bool WaylandClipboard::IsPrimarySelectionSupported() const {
- return primary_selection_device_manager_ && primary_selection_device_;
+wl::Clipboard* WaylandClipboard::GetClipboard(ClipboardBuffer buffer) {
+ if (buffer == ClipboardBuffer::kCopyPaste)
+ return copypaste_clipboard_.get();
+
+ if (buffer == ClipboardBuffer::kSelection) {
+ if (auto* manager = connection_->primary_selection_device_manager()) {
+ if (!primary_selection_clipboard_) {
+ primary_selection_clipboard_ = std::make_unique<
+ wl::ClipboardImpl<GtkPrimarySelectionDeviceManager>>(manager);
+ }
+ return primary_selection_clipboard_.get();
+ }
+ // Primary selection extension not available.
+ return nullptr;
+ }
+
+ NOTREACHED() << "Unsupported clipboard buffer: " << static_cast<int>(buffer);
+ return nullptr;
}
} // namespace ui
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_clipboard.h b/chromium/ui/ozone/platform/wayland/host/wayland_clipboard.h
index a4482490071..d4641406e0c 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_clipboard.h
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_clipboard.h
@@ -5,33 +5,36 @@
#ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_CLIPBOARD_H_
#define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_CLIPBOARD_H_
+#include <memory>
#include <string>
#include <vector>
#include "base/callback.h"
+#include "base/containers/flat_map.h"
#include "base/macros.h"
-#include "ui/ozone/platform/wayland/host/wayland_data_source.h"
+#include "base/optional.h"
+#include "ui/base/clipboard/clipboard_buffer.h"
#include "ui/ozone/public/platform_clipboard.h"
+namespace wl {
+class Clipboard;
+} // namespace wl
+
namespace ui {
-class GtkPrimarySelectionDevice;
-class GtkPrimarySelectionDeviceManager;
-class GtkPrimarySelectionSource;
-class WaylandDataDevice;
+class WaylandConnection;
class WaylandDataDeviceManager;
// Handles clipboard operations.
//
-// Owned by WaylandConnection, which provides a data device and a data device
-// manager.
+// WaylandDataDeviceManager singleton is required to be up and running for
+// WaylandClipboard to be minimally functional.
class WaylandClipboard : public PlatformClipboard {
public:
- WaylandClipboard(
- WaylandDataDeviceManager* data_device_manager,
- WaylandDataDevice* data_device,
- GtkPrimarySelectionDeviceManager* primary_selection_device_manager,
- GtkPrimarySelectionDevice* primary_selection_device);
+ WaylandClipboard(WaylandConnection* connection,
+ WaylandDataDeviceManager* device_manager);
+ WaylandClipboard(const WaylandClipboard&) = delete;
+ WaylandClipboard& operator=(const WaylandClipboard&) = delete;
~WaylandClipboard() override;
// PlatformClipboard.
@@ -51,35 +54,34 @@ class WaylandClipboard : public PlatformClipboard {
void SetSequenceNumberUpdateCb(
PlatformClipboard::SequenceNumberUpdateCb cb) override;
- void DataSourceCancelled(ClipboardBuffer buffer);
+ // TODO(nickdiego): Get rid of these methods once DataDevice implementations
+ // are decoupled from WaylandClipboard.
void SetData(const std::vector<uint8_t>& contents,
const std::string& mime_type);
void UpdateSequenceNumber(ClipboardBuffer buffer);
private:
- bool IsPrimarySelectionSupported() const;
+ // Get the wl::Clipboard instance owning a given |buffer|. Can return null in
+ // case |buffer| is unsupported. E.g: primary selection is not available.
+ wl::Clipboard* GetClipboard(ClipboardBuffer buffer);
+
+ // WaylandConnection providing optional data device managers, e.g: gtk
+ // primary selection.
+ WaylandConnection* const connection_;
// Holds a temporary instance of the client's clipboard content
// so that we can asynchronously write to it.
PlatformClipboard::DataMap* data_map_ = nullptr;
- // Notifies whenever clipboard sequence number is changed. Can be empty if not
- // set.
+ // Notifies whenever clipboard sequence number is changed. Can be empty if
+ // not set.
PlatformClipboard::SequenceNumberUpdateCb update_sequence_cb_;
// Stores the callback to be invoked upon data reading from clipboard.
PlatformClipboard::RequestDataClosure read_clipboard_closure_;
- std::unique_ptr<WaylandDataSource> clipboard_data_source_;
- std::unique_ptr<GtkPrimarySelectionSource> primary_data_source_;
-
- // These four instances are owned by the connection.
- WaylandDataDeviceManager* const data_device_manager_;
- WaylandDataDevice* const data_device_;
- GtkPrimarySelectionDeviceManager* const primary_selection_device_manager_;
- GtkPrimarySelectionDevice* const primary_selection_device_;
-
- DISALLOW_COPY_AND_ASSIGN(WaylandClipboard);
+ const std::unique_ptr<wl::Clipboard> copypaste_clipboard_;
+ std::unique_ptr<wl::Clipboard> primary_selection_clipboard_;
};
} // namespace ui
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_connection.cc b/chromium/ui/ozone/platform/wayland/host/wayland_connection.cc
index d6c27854df9..0246bc58695 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_connection.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_connection.cc
@@ -9,7 +9,7 @@
#include <algorithm>
#include <memory>
-#include <utility>
+#include <vector>
#include "base/bind.h"
#include "base/logging.h"
@@ -17,12 +17,16 @@
#include "base/message_loop/message_loop_current.h"
#include "base/strings/string_util.h"
#include "base/threading/thread_task_runner_handle.h"
-#include "mojo/public/cpp/system/platform_handle.h"
#include "ui/events/ozone/layout/keyboard_layout_engine_manager.h"
-#include "ui/gfx/swap_result.h"
+#include "ui/gfx/geometry/point.h"
#include "ui/ozone/platform/wayland/common/wayland_object.h"
+#include "ui/ozone/platform/wayland/host/gtk_primary_selection_device_manager.h"
#include "ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h"
+#include "ui/ozone/platform/wayland/host/wayland_clipboard.h"
#include "ui/ozone/platform/wayland/host/wayland_cursor.h"
+#include "ui/ozone/platform/wayland/host/wayland_cursor_position.h"
+#include "ui/ozone/platform/wayland/host/wayland_data_device_manager.h"
+#include "ui/ozone/platform/wayland/host/wayland_data_drag_controller.h"
#include "ui/ozone/platform/wayland/host/wayland_drm.h"
#include "ui/ozone/platform/wayland/host/wayland_event_source.h"
#include "ui/ozone/platform/wayland/host/wayland_input_method_context.h"
@@ -32,6 +36,7 @@
#include "ui/ozone/platform/wayland/host/wayland_shm.h"
#include "ui/ozone/platform/wayland/host/wayland_touch.h"
#include "ui/ozone/platform/wayland/host/wayland_window.h"
+#include "ui/ozone/platform/wayland/host/wayland_window_drag_controller.h"
#include "ui/ozone/platform/wayland/host/wayland_zwp_linux_dmabuf.h"
namespace ui {
@@ -39,6 +44,7 @@ namespace ui {
namespace {
constexpr uint32_t kMaxCompositorVersion = 4;
constexpr uint32_t kMaxGtkPrimarySelectionDeviceManagerVersion = 1;
+constexpr uint32_t kMaxKeyboardExtensionVersion = 1;
constexpr uint32_t kMaxLinuxDmabufVersion = 3;
constexpr uint32_t kMaxSeatVersion = 4;
constexpr uint32_t kMaxShmVersion = 1;
@@ -128,39 +134,10 @@ void WaylandConnection::SetCursorBitmap(const std::vector<SkBitmap>& bitmaps,
cursor_->UpdateBitmap(bitmaps, location, serial_);
}
-void WaylandConnection::StartDrag(const ui::OSExchangeData& data,
- int operation) {
- if (!dragdrop_data_source_)
- dragdrop_data_source_ = data_device_manager_->CreateSource();
- dragdrop_data_source_->Offer(data);
- dragdrop_data_source_->SetAction(operation);
- data_device_->StartDrag(dragdrop_data_source_->data_source(), data);
-}
-
-void WaylandConnection::FinishDragSession(uint32_t dnd_action,
- WaylandWindow* source_window) {
- if (source_window)
- source_window->OnDragSessionClose(dnd_action);
- data_device_->ResetSourceData();
- dragdrop_data_source_.reset();
-}
-
-void WaylandConnection::DeliverDragData(const std::string& mime_type,
- std::string* buffer) {
- data_device_->DeliverDragData(mime_type, buffer);
-}
-
-void WaylandConnection::RequestDragData(
- const std::string& mime_type,
- base::OnceCallback<void(const std::vector<uint8_t>&)> callback) {
- data_device_->RequestDragData(mime_type, std::move(callback));
-}
-
-bool WaylandConnection::IsDragInProgress() {
- // |data_device_| can be null when running on headless weston.
- if (!data_device_)
- return false;
- return data_device_->IsDragEntered() || drag_data_source();
+bool WaylandConnection::IsDragInProgress() const {
+ // |data_drag_controller_| can be null when running on headless weston.
+ return data_drag_controller_ && data_drag_controller_->state() !=
+ WaylandDataDragController::State::kIdle;
}
void WaylandConnection::Flush() {
@@ -190,13 +167,8 @@ void WaylandConnection::UpdateInputDevices(wl_seat* seat,
if (!has_keyboard) {
keyboard_.reset();
- } else if (wl_keyboard* keyboard = wl_seat_get_keyboard(seat)) {
- auto* layout_engine =
- KeyboardLayoutEngineManager::GetKeyboardLayoutEngine();
- keyboard_ = std::make_unique<WaylandKeyboard>(keyboard, this, layout_engine,
- event_source());
- } else {
- LOG(ERROR) << "Failed to get wl_keyboard from seat";
+ } else if (!CreateKeyboard()) {
+ LOG(ERROR) << "Failed to create WaylandKeyboard";
}
if (!has_touch) {
@@ -208,21 +180,34 @@ void WaylandConnection::UpdateInputDevices(wl_seat* seat,
}
}
-void WaylandConnection::EnsureDataDevice() {
- if (!data_device_manager_ || !seat_)
- return;
- DCHECK(!data_device_);
- wl_data_device* data_device = data_device_manager_->GetDevice();
- data_device_ = std::make_unique<WaylandDataDevice>(this, data_device);
+bool WaylandConnection::CreateKeyboard() {
+ wl_keyboard* keyboard = wl_seat_get_keyboard(seat_.get());
+ if (!keyboard)
+ return false;
- if (primary_selection_device_manager_) {
- primary_selection_device_ = std::make_unique<GtkPrimarySelectionDevice>(
- this, primary_selection_device_manager_->GetDevice());
- }
+ auto* layout_engine = KeyboardLayoutEngineManager::GetKeyboardLayoutEngine();
+ // Make sure to destroy the old WaylandKeyboard (if it exists) before creating
+ // the new one.
+ keyboard_.reset();
+ keyboard_.reset(new WaylandKeyboard(keyboard, keyboard_extension_v1_.get(),
+ this, layout_engine, event_source()));
+ return true;
+}
- clipboard_ = std::make_unique<WaylandClipboard>(
- data_device_manager_.get(), data_device_.get(),
- primary_selection_device_manager_.get(), primary_selection_device_.get());
+void WaylandConnection::CreateDataObjectsIfReady() {
+ if (data_device_manager_ && seat_) {
+ DCHECK(!data_drag_controller_);
+ data_drag_controller_ = std::make_unique<WaylandDataDragController>(
+ this, data_device_manager_.get());
+
+ DCHECK(!window_drag_controller_);
+ window_drag_controller_ = std::make_unique<WaylandWindowDragController>(
+ this, data_device_manager_.get(), event_source());
+
+ DCHECK(!clipboard_);
+ clipboard_ =
+ std::make_unique<WaylandClipboard>(this, data_device_manager_.get());
+ }
}
// static
@@ -268,7 +253,7 @@ void WaylandConnection::Global(void* data,
return;
}
wl_seat_add_listener(connection->seat_.get(), &seat_listener, connection);
- connection->EnsureDataDevice();
+ connection->CreateDataObjectsIfReady();
} else if (!connection->shell_v6_ &&
strcmp(interface, "zxdg_shell_v6") == 0) {
// Check for zxdg_shell_v6 first.
@@ -323,7 +308,7 @@ void WaylandConnection::Global(void* data,
connection->data_device_manager_ =
std::make_unique<WaylandDataDeviceManager>(
data_device_manager.release(), connection);
- connection->EnsureDataDevice();
+ connection->CreateDataObjectsIfReady();
} else if (!connection->primary_selection_device_manager_ &&
strcmp(interface, "gtk_primary_selection_device_manager") == 0) {
wl::Object<gtk_primary_selection_device_manager> manager =
@@ -343,6 +328,17 @@ void WaylandConnection::Global(void* data,
(strcmp(interface, "wp_presentation") == 0)) {
connection->presentation_ =
wl::Bind<wp_presentation>(registry, name, kMaxWpPresentationVersion);
+ } else if (!connection->keyboard_extension_v1_ &&
+ strcmp(interface, "zcr_keyboard_extension_v1") == 0) {
+ connection->keyboard_extension_v1_ = wl::Bind<zcr_keyboard_extension_v1>(
+ registry, name, kMaxKeyboardExtensionVersion);
+ if (!connection->keyboard_extension_v1_) {
+ LOG(ERROR) << "Failed to bind zcr_keyboard_extension_v1";
+ return;
+ }
+ // CreateKeyboard may fail if we do not have keyboard seat capabilities yet.
+ // We will create the keyboard when get them in that case.
+ connection->CreateKeyboard();
} else if (!connection->text_input_manager_v1_ &&
strcmp(interface, "zwp_text_input_manager_v1") == 0) {
connection->text_input_manager_v1_ = wl::Bind<zwp_text_input_manager_v1>(
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_connection.h b/chromium/ui/ozone/platform/wayland/host/wayland_connection.h
index b3e0a8a6c3d..5c5754dd2fa 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_connection.h
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_connection.h
@@ -6,21 +6,19 @@
#define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_CONNECTION_H_
#include <memory>
-#include <string>
#include <vector>
-#include "ui/gfx/buffer_types.h"
-#include "ui/gfx/native_widget_types.h"
+#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/ozone/platform/wayland/common/wayland_object.h"
-#include "ui/ozone/platform/wayland/host/gtk_primary_selection_device.h"
-#include "ui/ozone/platform/wayland/host/gtk_primary_selection_device_manager.h"
#include "ui/ozone/platform/wayland/host/wayland_clipboard.h"
-#include "ui/ozone/platform/wayland/host/wayland_cursor_position.h"
-#include "ui/ozone/platform/wayland/host/wayland_data_device.h"
-#include "ui/ozone/platform/wayland/host/wayland_data_device_manager.h"
+#include "ui/ozone/platform/wayland/host/wayland_data_drag_controller.h"
#include "ui/ozone/platform/wayland/host/wayland_data_source.h"
#include "ui/ozone/platform/wayland/host/wayland_window_manager.h"
+namespace gfx {
+class Point;
+}
+
namespace ui {
class WaylandBufferManagerHost;
@@ -32,8 +30,11 @@ class WaylandOutputManager;
class WaylandPointer;
class WaylandShm;
class WaylandTouch;
-class WaylandWindow;
class WaylandZwpLinuxDmabuf;
+class WaylandDataDeviceManager;
+class WaylandCursorPosition;
+class WaylandWindowDragController;
+class GtkPrimarySelectionDeviceManager;
class WaylandConnection {
public:
@@ -54,10 +55,6 @@ class WaylandConnection {
xdg_wm_base* shell() const { return shell_.get(); }
zxdg_shell_v6* shell_v6() const { return shell_v6_.get(); }
wl_seat* seat() const { return seat_.get(); }
- wl_data_device* data_device() const { return data_device_->data_device(); }
- gtk_primary_selection_device* primary_selection_device() const {
- return primary_selection_device_->data_device();
- }
wp_presentation* presentation() const { return presentation_.get(); }
zwp_text_input_manager_v1* text_input_manager_v1() const {
return text_input_manager_v1_.get();
@@ -82,10 +79,6 @@ class WaylandConnection {
WaylandClipboard* clipboard() const { return clipboard_.get(); }
- WaylandDataSource* drag_data_source() const {
- return dragdrop_data_source_.get();
- }
-
WaylandOutputManager* wayland_output_manager() const {
return wayland_output_manager_.get();
}
@@ -109,35 +102,36 @@ class WaylandConnection {
return &wayland_window_manager_;
}
- WaylandDataDevice* wayland_data_device() const { return data_device_.get(); }
-
- // Starts drag with |data| to be delivered, |operation| supported by the
- // source side initiated the dragging.
- void StartDrag(const ui::OSExchangeData& data, int operation);
- // Finishes drag and drop session. It happens when WaylandDataSource gets
- // 'OnDnDFinished' or 'OnCancel', which means the drop is performed or
- // canceled on others.
- void FinishDragSession(uint32_t dnd_action, WaylandWindow* source_window);
- // Delivers the data owned by Chromium which initiates drag-and-drop. |buffer|
- // is an output parameter and it should be filled with the data corresponding
- // to mime_type.
- void DeliverDragData(const std::string& mime_type, std::string* buffer);
- // Requests the data to the platform when Chromium gets drag-and-drop started
- // by others. Once reading the data from platform is done, |callback| should
- // be called with the data.
- void RequestDragData(
- const std::string& mime_type,
- base::OnceCallback<void(const std::vector<uint8_t>&)> callback);
+ WaylandDataDeviceManager* data_device_manager() const {
+ return data_device_manager_.get();
+ }
+
+ GtkPrimarySelectionDeviceManager* primary_selection_device_manager() const {
+ return primary_selection_device_manager_.get();
+ }
+
+ WaylandDataDragController* data_drag_controller() const {
+ return data_drag_controller_.get();
+ }
+
+ WaylandWindowDragController* window_drag_controller() const {
+ return window_drag_controller_.get();
+ }
// Returns true when dragging is entered or started.
- bool IsDragInProgress();
+ bool IsDragInProgress() const;
private:
void Flush();
void UpdateInputDevices(wl_seat* seat, uint32_t capabilities);
- // Make sure data device is properly initialized
- void EnsureDataDevice();
+ // Initialize data-related objects if required protocol objects are already
+ // in place, i.e: wl_seat and wl_data_device_manager.
+ void CreateDataObjectsIfReady();
+
+ // Creates WaylandKeyboard with the currently acquired protocol objects, if
+ // possible. Returns true iff WaylandKeyboard was created.
+ bool CreateKeyboard();
// wl_registry_listener
static void Global(void* data,
@@ -166,10 +160,11 @@ class WaylandConnection {
wl::Object<xdg_wm_base> shell_;
wl::Object<zxdg_shell_v6> shell_v6_;
wl::Object<wp_presentation> presentation_;
+ wl::Object<zcr_keyboard_extension_v1> keyboard_extension_v1_;
wl::Object<zwp_text_input_manager_v1> text_input_manager_v1_;
- // Event source instance. Must be declared before input objects so it outlives
- // them so thus being able to properly handle their destruction.
+ // Event source instance. Must be declared before input objects so it
+ // outlives them so thus being able to properly handle their destruction.
std::unique_ptr<WaylandEventSource> event_source_;
// Input device objects.
@@ -179,9 +174,7 @@ class WaylandConnection {
std::unique_ptr<WaylandCursor> cursor_;
std::unique_ptr<WaylandDataDeviceManager> data_device_manager_;
- std::unique_ptr<WaylandDataDevice> data_device_;
std::unique_ptr<WaylandClipboard> clipboard_;
- std::unique_ptr<WaylandDataSource> dragdrop_data_source_;
std::unique_ptr<WaylandOutputManager> wayland_output_manager_;
std::unique_ptr<WaylandCursorPosition> wayland_cursor_position_;
std::unique_ptr<WaylandZwpLinuxDmabuf> zwp_dmabuf_;
@@ -191,7 +184,9 @@ class WaylandConnection {
std::unique_ptr<GtkPrimarySelectionDeviceManager>
primary_selection_device_manager_;
- std::unique_ptr<GtkPrimarySelectionDevice> primary_selection_device_;
+
+ std::unique_ptr<WaylandDataDragController> data_drag_controller_;
+ std::unique_ptr<WaylandWindowDragController> window_drag_controller_;
// Manages Wayland windows.
WaylandWindowManager wayland_window_manager_;
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_cursor.cc b/chromium/ui/ozone/platform/wayland/host/wayland_cursor.cc
index 0d495501f06..7ffd900fcae 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_cursor.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_cursor.cc
@@ -7,6 +7,7 @@
#include <memory>
#include <vector>
+#include "base/logging.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/gfx/skia_util.h"
#include "ui/ozone/platform/wayland/common/wayland_util.h"
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_device.cc b/chromium/ui/ozone/platform/wayland/host/wayland_data_device.cc
index 361ffa519f1..1edd356006b 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_data_device.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_device.cc
@@ -5,209 +5,62 @@
#include "ui/ozone/platform/wayland/host/wayland_data_device.h"
#include <memory>
+#include <string>
#include <utility>
-#include <vector>
#include "base/bind.h"
-#include "base/strings/string16.h"
-#include "base/strings/string_split.h"
-#include "base/strings/utf_string_conversions.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "third_party/skia/include/core/SkCanvas.h"
-#include "ui/base/clipboard/clipboard_constants.h"
-#include "ui/base/dragdrop/drag_drop_types.h"
-#include "ui/base/dragdrop/file_info/file_info.h"
-#include "ui/base/dragdrop/os_exchange_data.h"
-#include "ui/base/dragdrop/os_exchange_data_provider_aura.h"
+#include "base/files/scoped_file.h"
+#include "ui/base/clipboard/clipboard_buffer.h"
+#include "ui/gfx/geometry/point_f.h"
+#include "ui/ozone/platform/wayland/common/data_util.h"
+#include "ui/ozone/platform/wayland/common/wayland_object.h"
#include "ui/ozone/platform/wayland/common/wayland_util.h"
#include "ui/ozone/platform/wayland/host/wayland_connection.h"
#include "ui/ozone/platform/wayland/host/wayland_data_offer.h"
-#include "ui/ozone/platform/wayland/host/wayland_shm_buffer.h"
+#include "ui/ozone/platform/wayland/host/wayland_data_source.h"
#include "ui/ozone/platform/wayland/host/wayland_window.h"
-#include "url/gurl.h"
-#include "url/url_canon.h"
-#include "url/url_util.h"
namespace ui {
-namespace {
-
-constexpr FilenameToURLPolicy kFilenameToURLPolicy = CONVERT_FILENAMES;
-
-// Converts raw data to either narrow or wide string.
-template <typename StringType>
-StringType BytesTo(const PlatformClipboard::Data& bytes) {
- if (bytes.size() % sizeof(typename StringType::value_type) != 0U) {
- // This is suspicious.
- LOG(WARNING)
- << "Data is possibly truncated, or a wrong conversion is requested.";
- }
-
- StringType result;
- result.assign(reinterpret_cast<typename StringType::const_pointer>(&bytes[0]),
- bytes.size() / sizeof(typename StringType::value_type));
- return result;
-}
-
-// Returns actions possible with the given source and drag'n'drop actions.
-// Also converts enums: input params are wl_data_device_manager_dnd_action but
-// the result is ui::DragDropTypes.
-int GetPossibleActions(uint32_t source_actions, uint32_t dnd_action) {
- // If drag'n'drop action is set, use it but check for ASK action (see below).
- uint32_t action = dnd_action != WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE
- ? dnd_action
- : source_actions;
-
- // We accept any action except ASK (see below).
- int operation = DragDropTypes::DRAG_NONE;
- if (action & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY)
- operation |= DragDropTypes::DRAG_COPY;
- if (action & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE)
- operation |= DragDropTypes::DRAG_MOVE;
- if (action & WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK) {
- // This is very rare and non-standard. Chromium doesn't set this when
- // anything is dragged from it, neither it provides any UI for asking
- // the user about the desired drag'n'drop action when data is dragged
- // from an external source.
- // We are safe with not adding anything here. However, keep NOTIMPLEMENTED
- // for an (unlikely) event of this being hit in distant future.
- NOTIMPLEMENTED_LOG_ONCE();
- }
- return operation;
-}
-
-void AddString(const PlatformClipboard::Data& data,
- OSExchangeData* os_exchange_data) {
- DCHECK(os_exchange_data);
-
- if (data.empty())
- return;
-
- os_exchange_data->SetString(base::UTF8ToUTF16(BytesTo<std::string>(data)));
-}
-
-void AddHtml(const PlatformClipboard::Data& data,
- OSExchangeData* os_exchange_data) {
- DCHECK(os_exchange_data);
-
- if (data.empty())
- return;
-
- os_exchange_data->SetHtml(base::UTF8ToUTF16(BytesTo<std::string>(data)),
- GURL());
-}
-
-// Parses |data| as if it had text/uri-list format. Its brief spec is:
-// 1. Any lines beginning with the '#' character are comment lines.
-// 2. Non-comment lines shall be URIs (URNs or URLs).
-// 3. Lines are terminated with a CRLF pair.
-// 4. URL encoding is used.
-void AddFiles(const PlatformClipboard::Data& data,
- OSExchangeData* os_exchange_data) {
- DCHECK(os_exchange_data);
-
- std::string data_as_string = BytesTo<std::string>(data);
-
- const auto lines = base::SplitString(
- data_as_string, "\r\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
- std::vector<FileInfo> filenames;
- for (const auto& line : lines) {
- if (line.empty() || line[0] == '#')
- continue;
- GURL url(line);
- if (!url.is_valid() || !url.SchemeIsFile()) {
- LOG(WARNING) << "Invalid URI found: " << line;
- continue;
- }
-
- std::string url_path = url.path();
- url::RawCanonOutputT<base::char16> unescaped;
- url::DecodeURLEscapeSequences(url_path.data(), url_path.size(),
- url::DecodeURLMode::kUTF8OrIsomorphic,
- &unescaped);
-
- std::string path8;
- base::UTF16ToUTF8(unescaped.data(), unescaped.length(), &path8);
- const base::FilePath path(path8);
- filenames.push_back({path, path.BaseName()});
- }
- if (filenames.empty())
- return;
-
- os_exchange_data->SetFilenames(filenames);
-}
-
-// Parses |data| as if it had text/x-moz-url format, which is basically
-// two lines separated with newline, where the first line is the URL and
-// the second one is page title. The unpleasant feature of text/x-moz-url is
-// that the URL has UTF-16 encoding.
-void AddUrl(const PlatformClipboard::Data& data,
- OSExchangeData* os_exchange_data) {
- DCHECK(os_exchange_data);
-
- if (data.empty())
- return;
-
- base::string16 data_as_string16 = BytesTo<base::string16>(data);
-
- const auto lines =
- base::SplitString(data_as_string16, base::ASCIIToUTF16("\r\n"),
- base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
- if (lines.size() != 2U) {
- LOG(WARNING) << "Invalid data passed as text/x-moz-url; it must contain "
- << "exactly 2 lines but has " << lines.size() << " instead.";
- return;
- }
- GURL url(lines[0]);
- if (!url.is_valid()) {
- LOG(WARNING) << "Invalid data passed as text/x-moz-url; the first line "
- << "must contain a valid URL but it doesn't.";
- return;
- }
-
- os_exchange_data->SetURL(url, lines[1]);
-}
-
-void AddToOSExchangeData(const PlatformClipboard::Data& data,
- const std::string& mime_type,
- OSExchangeData* os_exchange_data) {
- DCHECK(os_exchange_data);
- if ((mime_type == kMimeTypeText || mime_type == kMimeTypeTextUtf8)) {
- DCHECK(!os_exchange_data->HasString());
- AddString(data, os_exchange_data);
- } else if (mime_type == kMimeTypeHTML) {
- DCHECK(!os_exchange_data->HasHtml());
- AddHtml(data, os_exchange_data);
- } else if (mime_type == kMimeTypeMozillaURL) {
- DCHECK(!os_exchange_data->HasURL(kFilenameToURLPolicy));
- AddUrl(data, os_exchange_data);
- } else if (mime_type == kMimeTypeURIList) {
- DCHECK(!os_exchange_data->HasFile());
- AddFiles(data, os_exchange_data);
- } else {
- LOG(WARNING) << "Unhandled MIME type: " << mime_type;
- }
-}
-
-} // namespace
-
-// static
WaylandDataDevice::WaylandDataDevice(WaylandConnection* connection,
wl_data_device* data_device)
: WaylandDataDeviceBase(connection), data_device_(data_device) {
static const struct wl_data_device_listener kDataDeviceListener = {
- WaylandDataDevice::OnDataOffer, WaylandDataDevice::OnEnter,
- WaylandDataDevice::OnLeave, WaylandDataDevice::OnMotion,
- WaylandDataDevice::OnDrop, WaylandDataDevice::OnSelection};
+ WaylandDataDevice::OnOffer, WaylandDataDevice::OnEnter,
+ WaylandDataDevice::OnLeave, WaylandDataDevice::OnMotion,
+ WaylandDataDevice::OnDrop, WaylandDataDevice::OnSelection};
wl_data_device_add_listener(data_device_.get(), &kDataDeviceListener, this);
}
WaylandDataDevice::~WaylandDataDevice() = default;
-void WaylandDataDevice::RequestDragData(
- const std::string& mime_type,
- base::OnceCallback<void(const PlatformClipboard::Data&)> callback) {
- base::ScopedFD fd = drag_offer_->Receive(mime_type);
+void WaylandDataDevice::StartDrag(const WaylandDataSource& data_source,
+ const WaylandWindow& origin_window,
+ wl_surface* icon_surface,
+ DragDelegate* delegate) {
+ DCHECK(delegate);
+ DCHECK(!drag_delegate_);
+ drag_delegate_ = delegate;
+
+ wl_data_device_start_drag(data_device_.get(), data_source.data_source(),
+ origin_window.surface(), icon_surface,
+ connection()->serial());
+ drag_delegate_->DrawIcon();
+ connection()->ScheduleFlush();
+}
+
+void WaylandDataDevice::ResetDragDelegate() {
+ DCHECK(drag_delegate_);
+ drag_delegate_ = nullptr;
+}
+
+void WaylandDataDevice::RequestData(WaylandDataOffer* offer,
+ const std::string& mime_type,
+ RequestDataCallback callback) {
+ DCHECK(offer);
+ DCHECK(wl::IsMimeTypeSupported(mime_type));
+
+ base::ScopedFD fd = offer->Receive(mime_type);
if (!fd.is_valid()) {
LOG(ERROR) << "Failed to open file descriptor.";
return;
@@ -221,55 +74,13 @@ void WaylandDataDevice::RequestDragData(
RegisterDeferredReadCallback();
}
-void WaylandDataDevice::DeliverDragData(const std::string& mime_type,
- std::string* buffer) {
- DCHECK(buffer);
- DCHECK(source_data_);
-
- if (mime_type == kMimeTypeMozillaURL &&
- source_data_->HasURL(kFilenameToURLPolicy)) {
- GURL url;
- base::string16 title;
- source_data_->GetURLAndTitle(kFilenameToURLPolicy, &url, &title);
- buffer->append(url.spec());
- } else if (mime_type == kMimeTypeHTML && source_data_->HasHtml()) {
- base::string16 data;
- GURL base_url;
- source_data_->GetHtml(&data, &base_url);
- buffer->append(base::UTF16ToUTF8(data));
- } else if (source_data_->HasString()) {
- base::string16 data;
- source_data_->GetString(&data);
- buffer->append(base::UTF16ToUTF8(data));
- } else {
- LOG(WARNING) << "Cannot deliver data of type " << mime_type
- << " and no text representation is available.";
- }
-}
-
-void WaylandDataDevice::StartDrag(wl_data_source* data_source,
- const ui::OSExchangeData& data) {
- DCHECK(data_source);
-
- WaylandWindow* window =
- connection()->wayland_window_manager()->GetCurrentFocusedWindow();
- if (!window) {
- LOG(ERROR) << "Failed to get focused window.";
- return;
- }
- const SkBitmap* icon = PrepareDragIcon(data);
- source_data_ = std::make_unique<ui::OSExchangeData>(data.provider().Clone());
- wl_data_device_start_drag(data_device_.get(), data_source, window->surface(),
- icon_surface_.get(), connection()->serial());
- if (icon)
- DrawDragIcon(icon);
+void WaylandDataDevice::SetSelectionSource(WaylandDataSource* source) {
+ DCHECK(source);
+ wl_data_device_set_selection(data_device_.get(), source->data_source(),
+ connection()->serial());
connection()->ScheduleFlush();
}
-void WaylandDataDevice::ResetSourceData() {
- source_data_.reset();
-}
-
void WaylandDataDevice::ReadDragDataFromFD(
base::ScopedFD fd,
base::OnceCallback<void(const PlatformClipboard::Data&)> callback) {
@@ -278,17 +89,10 @@ void WaylandDataDevice::ReadDragDataFromFD(
std::move(callback).Run(contents);
}
-void WaylandDataDevice::HandleDeferredLeaveIfNeeded() {
- if (!is_leaving_)
- return;
-
- OnLeave(this, data_device_.get());
-}
-
// static
-void WaylandDataDevice::OnDataOffer(void* data,
- wl_data_device* data_device,
- wl_data_offer* offer) {
+void WaylandDataDevice::OnOffer(void* data,
+ wl_data_device* data_device,
+ wl_data_offer* offer) {
auto* self = static_cast<WaylandDataDevice*>(data);
self->connection()->clipboard()->UpdateSequenceNumber(
@@ -305,40 +109,24 @@ void WaylandDataDevice::OnEnter(void* data,
wl_fixed_t x,
wl_fixed_t y,
wl_data_offer* offer) {
- WaylandWindow* window =
- static_cast<WaylandWindow*>(wl_surface_get_user_data(surface));
+ WaylandWindow* window = WaylandWindow::FromSurface(surface);
if (!window) {
LOG(ERROR) << "Failed to get window.";
return;
}
auto* self = static_cast<WaylandDataDevice*>(data);
+
+ // Null |drag_delegate_| here means that the DND session has been initiated by
+ // an external application. In this case, use the default data drag delegate.
+ if (!self->drag_delegate_)
+ self->drag_delegate_ = self->connection()->data_drag_controller();
+
DCHECK(self->new_offer_);
- DCHECK(!self->drag_offer_);
- self->drag_offer_ = std::move(self->new_offer_);
- self->window_ = window;
-
- // TODO(crbug.com/1004715): Set mime type the client can accept. Now it sets
- // all mime types offered because current implementation doesn't decide
- // action based on mime type.
- self->unprocessed_mime_types_.clear();
- for (auto mime : self->drag_offer_->mime_types()) {
- self->unprocessed_mime_types_.push_back(mime);
- self->drag_offer_->Accept(serial, mime);
- }
+ self->drag_delegate_->OnDragOffer(std::move(self->new_offer_));
gfx::PointF point(wl_fixed_to_double(x), wl_fixed_to_double(y));
-
- // If |source_data_| is set, it means that dragging is started from the
- // same window and it's not needed to read data through Wayland.
- std::unique_ptr<OSExchangeData> dragged_data;
- if (!self->IsDraggingExternalData())
- dragged_data = std::make_unique<OSExchangeData>(
- self->source_data_->provider().Clone());
- self->window_->OnDragEnter(
- point, std::move(dragged_data),
- GetPossibleActions(self->drag_offer_->source_actions(),
- self->drag_offer_->dnd_action()));
+ self->drag_delegate_->OnDragEnter(window, point, serial);
}
void WaylandDataDevice::OnMotion(void* data,
@@ -347,65 +135,31 @@ void WaylandDataDevice::OnMotion(void* data,
wl_fixed_t x,
wl_fixed_t y) {
auto* self = static_cast<WaylandDataDevice*>(data);
- if (!self->window_) {
- LOG(ERROR) << "Failed to get window.";
- return;
+ if (self->drag_delegate_) {
+ gfx::PointF point(wl_fixed_to_double(x), wl_fixed_to_double(y));
+ self->drag_delegate_->OnDragMotion(point);
}
-
- gfx::PointF point(wl_fixed_to_double(x), wl_fixed_to_double(y));
- int client_operation = self->window_->OnDragMotion(
- point, time,
- GetPossibleActions(self->drag_offer_->source_actions(),
- self->drag_offer_->dnd_action()));
- self->SetOperation(client_operation);
}
void WaylandDataDevice::OnDrop(void* data, wl_data_device* data_device) {
auto* self = static_cast<WaylandDataDevice*>(data);
- if (!self->window_) {
- LOG(ERROR) << "Failed to get window.";
- return;
- }
- if (self->IsDraggingExternalData()) {
- // We are about to accept data dragged from another application.
- // Reading all the data may take some time so we set
- // |is_handling_dropped_data_| that will postpone handling of OnLeave
- // until reading is completed.
- self->is_handling_dropped_data_ = true;
- self->received_data_ = std::make_unique<OSExchangeData>(
- std::make_unique<OSExchangeDataProviderAura>());
- self->HandleUnprocessedMimeTypes();
- } else {
- // If the drag session had been started internally by chromium,
- // |source_data_| already holds the data, and it is already forwarded to the
- // delegate through OnDragEnter, so here we short-cut the data transfer by
- // sending nullptr.
- self->HandleReceivedData(nullptr);
- }
+ if (self->drag_delegate_)
+ self->drag_delegate_->OnDragDrop();
}
void WaylandDataDevice::OnLeave(void* data, wl_data_device* data_device) {
- // While reading data, it could get OnLeave event. We don't handle OnLeave
- // event directly if |is_handling_dropped_data_| is set.
auto* self = static_cast<WaylandDataDevice*>(data);
- if (!self->window_) {
- LOG(ERROR) << "Failed to get window.";
- return;
- }
+ if (self->drag_delegate_) {
+ self->drag_delegate_->OnDragLeave();
- if (self->is_handling_dropped_data_) {
- self->is_leaving_ = true;
- return;
+ // When in a DND session initiated by an external application,
+ // |drag_delegate_| is set at OnEnter, and must be reset here to avoid
+ // potential use-after-free.
+ if (!self->drag_delegate_->IsDragSource())
+ self->drag_delegate_ = nullptr;
}
-
- self->window_->OnDragLeave();
- self->window_ = nullptr;
- self->drag_offer_.reset();
- self->is_handling_dropped_data_ = false;
- self->is_leaving_ = false;
}
-// static
void WaylandDataDevice::OnSelection(void* data,
wl_data_device* data_device,
wl_data_offer* offer) {
@@ -428,106 +182,4 @@ void WaylandDataDevice::OnSelection(void* data,
self->data_offer()->EnsureTextMimeTypeIfNeeded();
}
-const SkBitmap* WaylandDataDevice::PrepareDragIcon(const OSExchangeData& data) {
- const SkBitmap* icon_bitmap = data.provider().GetDragImage().bitmap();
- if (!icon_bitmap || icon_bitmap->empty())
- return nullptr;
- icon_surface_.reset(wl_compositor_create_surface(connection()->compositor()));
- DCHECK(icon_surface_);
- return icon_bitmap;
-}
-
-void WaylandDataDevice::DrawDragIcon(const SkBitmap* icon_bitmap) {
- DCHECK(icon_bitmap);
- DCHECK(!icon_bitmap->empty());
- gfx::Size size(icon_bitmap->width(), icon_bitmap->height());
-
- if (!shm_buffer_ || shm_buffer_->size() != size) {
- shm_buffer_ = std::make_unique<WaylandShmBuffer>(connection()->shm(), size);
- if (!shm_buffer_->IsValid()) {
- LOG(ERROR) << "Failed to create drag icon buffer.";
- return;
- }
- }
- wl::DrawBitmap(*icon_bitmap, shm_buffer_.get());
-
- wl_surface* surface = icon_surface_.get();
- wl_surface_attach(surface, shm_buffer_->get(), 0, 0);
- wl_surface_damage(surface, 0, 0, size.width(), size.height());
- wl_surface_commit(surface);
-}
-
-void WaylandDataDevice::HandleUnprocessedMimeTypes() {
- std::string mime_type = SelectNextMimeType();
- if (mime_type.empty()) {
- HandleReceivedData(std::move(received_data_));
- } else {
- RequestDragData(mime_type,
- base::BindOnce(&WaylandDataDevice::OnDragDataReceived,
- base::Unretained(this)));
- }
-}
-
-void WaylandDataDevice::OnDragDataReceived(
- const PlatformClipboard::Data& contents) {
- if (!contents.empty()) {
- AddToOSExchangeData(contents, unprocessed_mime_types_.front(),
- received_data_.get());
- }
-
- unprocessed_mime_types_.pop_front();
-
- // Continue reading data for other negotiated mime types.
- HandleUnprocessedMimeTypes();
-}
-
-void WaylandDataDevice::HandleReceivedData(
- std::unique_ptr<ui::OSExchangeData> received_data) {
- unprocessed_mime_types_.clear();
-
- window_->OnDragDrop(std::move(received_data));
- drag_offer_->FinishOffer();
- is_handling_dropped_data_ = false;
- HandleDeferredLeaveIfNeeded();
-}
-
-std::string WaylandDataDevice::SelectNextMimeType() {
- while (!unprocessed_mime_types_.empty()) {
- const std::string& mime_type = unprocessed_mime_types_.front();
- if ((mime_type == kMimeTypeText || mime_type == kMimeTypeTextUtf8) &&
- !received_data_->HasString()) {
- return mime_type;
- }
- if (mime_type == kMimeTypeURIList && !received_data_->HasFile()) {
- return mime_type;
- }
- if (mime_type == kMimeTypeMozillaURL &&
- !received_data_->HasURL(kFilenameToURLPolicy)) {
- return mime_type;
- }
- if (mime_type == kMimeTypeHTML && !received_data_->HasHtml()) {
- return mime_type;
- }
- unprocessed_mime_types_.pop_front();
- }
- return {};
-}
-
-void WaylandDataDevice::SetOperation(const int operation) {
- uint32_t dnd_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
- uint32_t preferred_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
-
- if (operation & DragDropTypes::DRAG_COPY) {
- dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
- preferred_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
- }
-
- if (operation & DragDropTypes::DRAG_MOVE) {
- dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
- if (preferred_action == WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE)
- preferred_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
- }
- drag_offer_->SetAction(dnd_actions, preferred_action);
-}
-
} // namespace ui
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_device.h b/chromium/ui/ozone/platform/wayland/host/wayland_data_device.h
index cf2e8cc7a65..5512f4c4480 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_data_device.h
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_device.h
@@ -7,73 +7,84 @@
#include <wayland-client.h>
-#include <list>
+#include <cstdint>
#include <memory>
#include <string>
#include "base/callback.h"
#include "base/files/scoped_file.h"
-#include "base/macros.h"
#include "ui/ozone/platform/wayland/common/wayland_object.h"
#include "ui/ozone/platform/wayland/host/wayland_data_device_base.h"
+#include "ui/ozone/platform/wayland/host/wayland_data_source.h"
#include "ui/ozone/public/platform_clipboard.h"
-class SkBitmap;
+namespace gfx {
+class PointF;
+} // namespace gfx
namespace ui {
-class OSExchangeData;
class WaylandDataOffer;
class WaylandConnection;
-class WaylandShmBuffer;
class WaylandWindow;
// This class provides access to inter-client data transfer mechanisms
// such as copy-and-paste and drag-and-drop mechanisms.
class WaylandDataDevice : public WaylandDataDeviceBase {
public:
+ using RequestDataCallback =
+ base::OnceCallback<void(const PlatformClipboard::Data&)>;
+
+ // DragDelegate is responsible for handling drag and drop sessions.
+ class DragDelegate {
+ public:
+ virtual bool IsDragSource() const = 0;
+ virtual void DrawIcon() = 0;
+ virtual void OnDragOffer(std::unique_ptr<WaylandDataOffer> offer) = 0;
+ virtual void OnDragEnter(WaylandWindow* window,
+ const gfx::PointF& location,
+ uint32_t serial) = 0;
+ virtual void OnDragMotion(const gfx::PointF& location) = 0;
+ virtual void OnDragLeave() = 0;
+ virtual void OnDragDrop() = 0;
+
+ protected:
+ virtual ~DragDelegate() = default;
+ };
+
WaylandDataDevice(WaylandConnection* connection, wl_data_device* data_device);
+ WaylandDataDevice(const WaylandDataDevice&) = delete;
+ WaylandDataDevice& operator=(const WaylandDataDevice&) = delete;
~WaylandDataDevice() override;
- // Requests the data to the platform when Chromium gets drag-and-drop started
- // by others. Once reading the data from platform is done, |callback| should
- // be called with the data.
- void RequestDragData(
- const std::string& mime_type,
- base::OnceCallback<void(const PlatformClipboard::Data&)> callback);
- // Delivers the data owned by Chromium which initiates drag-and-drop. |buffer|
- // is an output parameter and it should be filled with the data corresponding
- // to mime_type.
- void DeliverDragData(const std::string& mime_type, std::string* buffer);
- // Starts drag with |data| to be delivered, |operation| supported by the
- // source side initiated the dragging.
- void StartDrag(wl_data_source* data_source, const ui::OSExchangeData& data);
- // Resets |source_data_| when the dragging is finished.
- void ResetSourceData();
-
- wl_data_device* data_device() const { return data_device_.get(); }
+ // Starts a wayland drag and drop session, controlled by |delegate|.
+ void StartDrag(const WaylandDataSource& data_source,
+ const WaylandWindow& origin_window,
+ wl_surface* icon_surface,
+ DragDelegate* delegate);
- bool IsDragEntered() { return drag_offer_ != nullptr; }
+ // Reset the drag delegate, assuming there is one set. Any wl_data_device
+ // event received after this will be ignored until a new delegate is set.
+ void ResetDragDelegate();
- WaylandWindow* entered_window() const { return window_; }
+ // Requests data for an |offer| in a format specified by |mime_type|. The
+ // transfer happens asynchronously and |callback| is called when it is done.
+ void RequestData(WaylandDataOffer* offer,
+ const std::string& mime_type,
+ RequestDataCallback callback);
- private:
- void ReadDragDataFromFD(
- base::ScopedFD fd,
- base::OnceCallback<void(const PlatformClipboard::Data&)> callback);
+ // Returns the underlying wl_data_device singleton object.
+ wl_data_device* data_device() const { return data_device_.get(); }
- // If source_data_ is not set, data is being dragged from an external
- // application (non-chromium).
- bool IsDraggingExternalData() const { return !source_data_; }
+ void SetSelectionSource(WaylandDataSource* source);
- // If OnLeave event occurs while it's reading drag data, it defers handling
- // it. Once reading data is completed, it's handled.
- void HandleDeferredLeaveIfNeeded();
+ private:
+ void ReadDragDataFromFD(base::ScopedFD fd, RequestDataCallback callback);
// wl_data_device_listener callbacks
- static void OnDataOffer(void* data,
- wl_data_device* data_device,
- wl_data_offer* id);
+ static void OnOffer(void* data,
+ wl_data_device* data_device,
+ wl_data_offer* id);
static void OnEnter(void* data,
wl_data_device* data_device,
@@ -101,60 +112,18 @@ class WaylandDataDevice : public WaylandDataDeviceBase {
wl_data_device* data_device,
wl_data_offer* id);
- // Returns the drag icon bitmap and creates and wayland surface to draw it
- // on, if a valid drag image is present in |data|; otherwise returns null.
- const SkBitmap* PrepareDragIcon(const OSExchangeData& data);
- void DrawDragIcon(const SkBitmap* bitmap);
-
- void OnDragDataReceived(const PlatformClipboard::Data& contents);
-
- // HandleUnprocessedMimeTypes asynchronously request and read data for every
- // negotiated mime type, one after another (OnDragDataReceived calls back
- // HandleUnprocessedMimeTypes so it finish only when there's no more items in
- // unprocessed_mime_types_ vector). HandleReceivedData is called once the
- // process is finished.
- void HandleUnprocessedMimeTypes();
- void HandleReceivedData(std::unique_ptr<ui::OSExchangeData> received_data);
- // Returns the next MIME type to be received from the source process, or an
- // empty string if there are no more interesting MIME types left to process.
- std::string SelectNextMimeType();
-
- // Set drag operation decided by client.
- void SetOperation(const int operation);
-
// The wl_data_device wrapped by this WaylandDataDevice.
wl::Object<wl_data_device> data_device_;
+ DragDelegate* drag_delegate_ = nullptr;
+
// There are two separate data offers at a time, the drag offer and the
// selection offer, each with independent lifetimes. When we receive a new
- // offer, it is not immediately possible to know whether the new offer is the
- // drag offer or the selection offer. This variable is used to store ownership
- // of new data offers temporarily until its identity becomes known.
+ // offer, it is not immediately possible to know whether the new offer is
+ // the drag offer or the selection offer. This variable is used to store
+ // new data offers temporarily until it is possible to determine which kind
+ // session it belongs to.
std::unique_ptr<WaylandDataOffer> new_offer_;
-
- // Offer to receive data from another process via drag-and-drop, or null if no
- // drag-and-drop from another process is in progress.
- std::unique_ptr<WaylandDataOffer> drag_offer_;
-
- WaylandWindow* window_ = nullptr;
-
- bool is_handling_dropped_data_ = false;
- bool is_leaving_ = false;
-
- std::unique_ptr<WaylandShmBuffer> shm_buffer_;
- wl::Object<wl_surface> icon_surface_;
-
- // Mime types to be handled.
- std::list<std::string> unprocessed_mime_types_;
-
- // The data delivered from Wayland
- std::unique_ptr<ui::OSExchangeData> received_data_;
-
- // When dragging is started from Chromium, |source_data_| is forwarded to
- // Wayland when they are ready to get the data.
- std::unique_ptr<ui::OSExchangeData> source_data_;
-
- DISALLOW_COPY_AND_ASSIGN(WaylandDataDevice);
};
} // namespace ui
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_device_base.cc b/chromium/ui/ozone/platform/wayland/host/wayland_data_device_base.cc
index e61c9b53816..3e020a31134 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_data_device_base.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_device_base.cc
@@ -7,6 +7,7 @@
#include <utility>
#include "base/bind.h"
+#include "base/logging.h"
#include "ui/ozone/platform/wayland/common/wayland_util.h"
#include "ui/ozone/platform/wayland/host/wayland_connection.h"
@@ -64,7 +65,7 @@ void WaylandDataDeviceBase::RegisterDeferredReadCallback() {
deferred_read_callback_.reset(wl_display_sync(connection_->display()));
static const wl_callback_listener kListener = {
- GtkPrimarySelectionDevice::DeferredReadCallback};
+ WaylandDataDeviceBase::DeferredReadCallback};
wl_callback_add_listener(deferred_read_callback_.get(), &kListener, this);
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_device_base.h b/chromium/ui/ozone/platform/wayland/host/wayland_data_device_base.h
index 78abf99aec9..e331243fc5e 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_data_device_base.h
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_device_base.h
@@ -25,6 +25,7 @@ class WaylandDataDeviceBase {
// Returns MIME types given by the current data offer.
const std::vector<std::string>& GetAvailableMimeTypes() const;
+
// Extracts data of the specified MIME type from the data offer.
bool RequestSelectionData(const std::string& mime_type);
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_device_manager.cc b/chromium/ui/ozone/platform/wayland/host/wayland_data_device_manager.cc
index 5b4a79d7979..6c283794473 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_data_device_manager.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_device_manager.cc
@@ -4,7 +4,12 @@
#include "ui/ozone/platform/wayland/host/wayland_data_device_manager.h"
+#include <wayland-client-protocol.h>
+
+#include <memory>
+
#include "ui/ozone/platform/wayland/host/wayland_connection.h"
+#include "ui/ozone/platform/wayland/host/wayland_data_device.h"
#include "ui/ozone/platform/wayland/host/wayland_data_source.h"
namespace ui {
@@ -19,16 +24,23 @@ WaylandDataDeviceManager::WaylandDataDeviceManager(
WaylandDataDeviceManager::~WaylandDataDeviceManager() = default;
-wl_data_device* WaylandDataDeviceManager::GetDevice() {
+WaylandDataDevice* WaylandDataDeviceManager::GetDevice() {
DCHECK(connection_->seat());
- return wl_data_device_manager_get_data_device(device_manager_.get(),
- connection_->seat());
+ if (!device_) {
+ device_ = std::make_unique<WaylandDataDevice>(
+ connection_, wl_data_device_manager_get_data_device(
+ device_manager_.get(), connection_->seat()));
+ }
+ DCHECK(device_);
+ return device_.get();
}
-std::unique_ptr<WaylandDataSource> WaylandDataDeviceManager::CreateSource() {
+std::unique_ptr<WaylandDataSource> WaylandDataDeviceManager::CreateSource(
+ WaylandDataSource::Delegate* delegate) {
wl_data_source* data_source =
wl_data_device_manager_create_data_source(device_manager_.get());
- return std::make_unique<WaylandDataSource>(data_source, connection_);
+ return std::make_unique<WaylandDataSource>(data_source, connection_,
+ delegate);
}
} // namespace ui
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_device_manager.h b/chromium/ui/ozone/platform/wayland/host/wayland_data_device_manager.h
index ea37eee0ba3..7ed3a330ea3 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_data_device_manager.h
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_device_manager.h
@@ -5,33 +5,37 @@
#ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_DATA_DEVICE_MANAGER_H_
#define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_DATA_DEVICE_MANAGER_H_
-#include <wayland-client.h>
-
#include <memory>
-#include "base/macros.h"
#include "ui/ozone/platform/wayland/common/wayland_object.h"
+#include "ui/ozone/platform/wayland/host/wayland_data_source.h"
namespace ui {
class WaylandConnection;
-class WaylandDataSource;
+class WaylandDataDevice;
class WaylandDataDeviceManager {
public:
+ using DataSource = WaylandDataSource;
+ using DataDevice = WaylandDataDevice;
+
WaylandDataDeviceManager(wl_data_device_manager* device_manager,
WaylandConnection* connection);
+ WaylandDataDeviceManager(const WaylandDataDeviceManager&) = delete;
+ WaylandDataDeviceManager& operator=(const WaylandDataDeviceManager&) = delete;
~WaylandDataDeviceManager();
- wl_data_device* GetDevice();
- std::unique_ptr<WaylandDataSource> CreateSource();
+ WaylandDataDevice* GetDevice();
+ std::unique_ptr<WaylandDataSource> CreateSource(
+ WaylandDataSource::Delegate* delegate);
private:
wl::Object<wl_data_device_manager> device_manager_;
- WaylandConnection* connection_;
+ WaylandConnection* const connection_;
- DISALLOW_COPY_AND_ASSIGN(WaylandDataDeviceManager);
+ std::unique_ptr<WaylandDataDevice> device_;
};
} // namespace ui
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_device_unittest.cc b/chromium/ui/ozone/platform/wayland/host/wayland_data_device_unittest.cc
index 9195d876030..b4bedb59ac9 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_data_device_unittest.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_device_unittest.cc
@@ -14,9 +14,6 @@
#include "base/strings/utf_string_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/clipboard/clipboard_constants.h"
-#include "ui/base/dragdrop/drag_drop_types.h"
-#include "ui/base/dragdrop/file_info/file_info.h"
-#include "ui/base/dragdrop/os_exchange_data.h"
#include "ui/events/base_event_utils.h"
#include "ui/ozone/platform/wayland/test/constants.h"
#include "ui/ozone/platform/wayland/test/mock_surface.h"
@@ -27,8 +24,6 @@
#include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h"
#include "ui/ozone/platform/wayland/test/wayland_test.h"
#include "ui/ozone/public/platform_clipboard.h"
-#include "ui/platform_window/platform_window_handler/wm_drop_handler.h"
-#include "url/gurl.h"
using testing::_;
using testing::Mock;
@@ -37,8 +32,6 @@ namespace ui {
namespace {
-constexpr FilenameToURLPolicy kFilenameToURLPolicy = CONVERT_FILENAMES;
-
template <typename StringType>
ui::PlatformClipboard::Data ToClipboardData(const StringType& data_string) {
ui::PlatformClipboard::Data result;
@@ -52,39 +45,6 @@ ui::PlatformClipboard::Data ToClipboardData(const StringType& data_string) {
} // namespace
-class MockDropHandler : public WmDropHandler {
- public:
- MockDropHandler() = default;
- ~MockDropHandler() override {}
-
- MOCK_METHOD3(OnDragEnter,
- void(const gfx::PointF& point,
- std::unique_ptr<OSExchangeData> data,
- int operation));
- MOCK_METHOD2(OnDragMotion, int(const gfx::PointF& point, int operation));
- MOCK_METHOD0(MockOnDragDrop, void());
- MOCK_METHOD0(OnDragLeave, void());
-
- void SetOnDropClosure(base::RepeatingClosure closure) {
- on_drop_closure_ = closure;
- }
-
- ui::OSExchangeData* dropped_data() { return dropped_data_.get(); }
-
- protected:
- void OnDragDrop(std::unique_ptr<ui::OSExchangeData> data) override {
- dropped_data_ = std::move(data);
- MockOnDragDrop();
- on_drop_closure_.Run();
- on_drop_closure_.Reset();
- }
-
- private:
- base::RepeatingClosure on_drop_closure_;
-
- std::unique_ptr<ui::OSExchangeData> dropped_data_;
-};
-
// This class mocks how a real clipboard/ozone client would
// hook to PlatformClipboard, with one difference: real clients
// have no access to the WaylandConnection instance like this
@@ -143,15 +103,11 @@ class WaylandDataDeviceManagerTest : public WaylandTest {
clipboard_client_ =
std::make_unique<MockClipboardClient>(connection_.get());
-
- drop_handler_ = std::make_unique<MockDropHandler>();
- SetWmDropHandler(window_.get(), drop_handler_.get());
}
protected:
wl::TestDataDeviceManager* data_device_manager_;
std::unique_ptr<MockClipboardClient> clipboard_client_;
- std::unique_ptr<MockDropHandler> drop_handler_;
private:
DISALLOW_COPY_AND_ASSIGN(WaylandDataDeviceManagerTest);
@@ -231,255 +187,6 @@ TEST_P(WaylandDataDeviceManagerTest, IsSelectionOwner) {
ASSERT_FALSE(clipboard_client_->IsSelectionOwner());
}
-TEST_P(WaylandDataDeviceManagerTest, StartDrag) {
- bool restored_focus = window_->has_pointer_focus();
- window_->SetPointerFocus(true);
-
- // The client starts dragging.
- OSExchangeData os_exchange_data;
- int operation = DragDropTypes::DRAG_COPY | DragDropTypes::DRAG_MOVE;
- connection_->StartDrag(os_exchange_data, operation);
-
- WaylandDataSource::DragDataMap data;
- data[wl::kTextMimeTypeUtf8] = wl::kSampleTextForDragAndDrop;
- connection_->drag_data_source()->SetDragData(data);
- Sync();
-
- // The server reads the data and the callback gets it.
- base::RunLoop run_loop;
- auto callback = base::BindOnce(
- [](base::RunLoop* loop, PlatformClipboard::Data&& data) {
- std::string result(data.begin(), data.end());
- EXPECT_EQ(wl::kSampleTextForDragAndDrop, result);
- loop->Quit();
- },
- &run_loop);
- data_device_manager_->data_source()->ReadData(wl::kTextMimeTypeUtf8,
- std::move(callback));
- run_loop.Run();
- window_->SetPointerFocus(restored_focus);
-}
-
-TEST_P(WaylandDataDeviceManagerTest, StartDragWithWrongMimeType) {
- bool restored_focus = window_->has_pointer_focus();
- window_->SetPointerFocus(true);
-
- // The client starts dragging offering data with wl::kTextMimeTypeUtf8
- // mime type.
- OSExchangeData os_exchange_data;
- int operation = DragDropTypes::DRAG_COPY | DragDropTypes::DRAG_MOVE;
- connection_->StartDrag(os_exchange_data, operation);
-
- WaylandDataSource::DragDataMap data;
- data[wl::kTextMimeTypeUtf8] = wl::kSampleTextForDragAndDrop;
- connection_->drag_data_source()->SetDragData(data);
- Sync();
-
- // The server should get an empty data buffer in ReadData callback
- // when trying to read it.
- base::RunLoop run_loop;
- auto callback = base::BindOnce(
- [](base::RunLoop* loop, PlatformClipboard::Data&& data) {
- std::string result(data.begin(), data.end());
- EXPECT_EQ("", result);
- loop->Quit();
- },
- &run_loop);
- data_device_manager_->data_source()->ReadData(ui::kMimeTypeText,
- std::move(callback));
- run_loop.Run();
- window_->SetPointerFocus(restored_focus);
-}
-
-TEST_P(WaylandDataDeviceManagerTest, ReceiveDrag) {
- auto* data_offer = data_device_manager_->data_device()->OnDataOffer();
- data_offer->OnOffer(
- ui::kMimeTypeText,
- ToClipboardData(std::string(wl::kSampleTextForDragAndDrop)));
-
- gfx::Point entered_point(10, 10);
- // The server sends an enter event.
- data_device_manager_->data_device()->OnEnter(
- 1002, surface_->resource(), wl_fixed_from_int(entered_point.x()),
- wl_fixed_from_int(entered_point.y()), data_offer);
-
- int64_t time =
- (ui::EventTimeForNow() - base::TimeTicks()).InMilliseconds() & UINT32_MAX;
- gfx::Point motion_point(11, 11);
-
- // The server sends an motion event.
- data_device_manager_->data_device()->OnMotion(
- time, wl_fixed_from_int(motion_point.x()),
- wl_fixed_from_int(motion_point.y()));
-
- Sync();
-
- auto callback = base::BindOnce([](const PlatformClipboard::Data& contents) {
- std::string result;
- result.assign(reinterpret_cast<std::string::const_pointer>(&contents[0]),
- contents.size());
- EXPECT_EQ(wl::kSampleTextForDragAndDrop, result);
- });
-
- // The client requests the data and gets callback with it.
- connection_->RequestDragData(ui::kMimeTypeText, std::move(callback));
- Sync();
-
- data_device_manager_->data_device()->OnLeave();
-}
-
-TEST_P(WaylandDataDeviceManagerTest, DropSeveralMimeTypes) {
- auto* data_offer = data_device_manager_->data_device()->OnDataOffer();
- data_offer->OnOffer(
- ui::kMimeTypeText,
- ToClipboardData(std::string(wl::kSampleTextForDragAndDrop)));
- data_offer->OnOffer(
- ui::kMimeTypeMozillaURL,
- ToClipboardData(base::UTF8ToUTF16("https://sample.com/\r\n"
- "Sample")));
- data_offer->OnOffer(
- ui::kMimeTypeURIList,
- ToClipboardData(std::string("file:///home/user/file\r\n")));
-
- EXPECT_CALL(*drop_handler_, OnDragEnter(_, _, _)).Times(1);
- gfx::Point entered_point(10, 10);
- data_device_manager_->data_device()->OnEnter(
- 1002, surface_->resource(), wl_fixed_from_int(entered_point.x()),
- wl_fixed_from_int(entered_point.y()), data_offer);
- Sync();
- Mock::VerifyAndClearExpectations(drop_handler_.get());
-
- EXPECT_CALL(*drop_handler_, MockOnDragDrop()).Times(1);
- base::RunLoop loop;
- drop_handler_->SetOnDropClosure(loop.QuitClosure());
- data_device_manager_->data_device()->OnDrop();
-
- // Here we are expecting three data items, so there will be three roundtrips
- // to the Wayland and back. Hence Sync() three times.
- Sync();
- Sync();
- Sync();
- loop.Run();
- Mock::VerifyAndClearExpectations(drop_handler_.get());
-
- EXPECT_TRUE(drop_handler_->dropped_data()->HasString());
- EXPECT_TRUE(drop_handler_->dropped_data()->HasFile());
- EXPECT_TRUE(drop_handler_->dropped_data()->HasURL(kFilenameToURLPolicy));
-
- data_device_manager_->data_device()->OnLeave();
-}
-
-// Tests URI validation for text/uri-list MIME type. Log warnings rendered in
-// the console when this test is running are the expected and valid side effect.
-TEST_P(WaylandDataDeviceManagerTest, ValidateDroppedUriList) {
- const struct {
- std::string content;
- base::flat_set<std::string> expected_uris;
- } kCases[] = {{{}, {}},
- {"file:///home/user/file\r\n", {"/home/user/file"}},
- {"# Comment\r\n"
- "file:///home/user/file\r\n"
- "file:///home/guest/file\r\n"
- "not a filename at all\r\n"
- "https://valid.url/but/scheme/is/not/file/so/invalid\r\n",
- {"/home/user/file", "/home/guest/file"}}};
-
- for (const auto& kCase : kCases) {
- auto* data_offer = data_device_manager_->data_device()->OnDataOffer();
- data_offer->OnOffer(ui::kMimeTypeURIList, ToClipboardData(kCase.content));
-
- EXPECT_CALL(*drop_handler_, OnDragEnter(_, _, _)).Times(1);
- gfx::Point entered_point(10, 10);
- data_device_manager_->data_device()->OnEnter(
- 1002, surface_->resource(), wl_fixed_from_int(entered_point.x()),
- wl_fixed_from_int(entered_point.y()), data_offer);
- Sync();
- Mock::VerifyAndClearExpectations(drop_handler_.get());
-
- EXPECT_CALL(*drop_handler_, MockOnDragDrop()).Times(1);
- base::RunLoop loop;
- drop_handler_->SetOnDropClosure(loop.QuitClosure());
- data_device_manager_->data_device()->OnDrop();
-
- Sync();
- loop.Run();
- Mock::VerifyAndClearExpectations(drop_handler_.get());
-
- if (kCase.expected_uris.empty()) {
- EXPECT_FALSE(drop_handler_->dropped_data()->HasFile());
- } else {
- EXPECT_TRUE(drop_handler_->dropped_data()->HasFile());
- std::vector<FileInfo> filenames;
- EXPECT_TRUE(drop_handler_->dropped_data()->GetFilenames(&filenames));
- EXPECT_EQ(filenames.size(), kCase.expected_uris.size());
- for (const auto& filename : filenames)
- EXPECT_EQ(kCase.expected_uris.count(filename.path.AsUTF8Unsafe()), 1U);
- }
-
- EXPECT_CALL(*drop_handler_, OnDragLeave()).Times(1);
- data_device_manager_->data_device()->OnLeave();
- Sync();
- Mock::VerifyAndClearExpectations(drop_handler_.get());
- }
-}
-
-// Tests URI validation for text/x-moz-url MIME type. Log warnings rendered in
-// the console when this test is running are the expected and valid side effect.
-TEST_P(WaylandDataDeviceManagerTest, ValidateDroppedXMozUrl) {
- const struct {
- std::string content;
- std::string expected_url;
- std::string expected_title;
- } kCases[] = {
- {{}, {}, {}},
- {"http://sample.com/\r\nSample", "http://sample.com/", "Sample"},
- {"http://title.must.be.set/", {}, {}},
- {"url.must.be.valid/and/have.scheme\r\nInvalid URL", {}, {}},
- {"file:///files/are/ok\r\nThe policy allows that", "file:///files/are/ok",
- "The policy allows that"}};
-
- for (const auto& kCase : kCases) {
- auto* data_offer = data_device_manager_->data_device()->OnDataOffer();
- data_offer->OnOffer(ui::kMimeTypeMozillaURL,
- ToClipboardData(base::UTF8ToUTF16(kCase.content)));
-
- EXPECT_CALL(*drop_handler_, OnDragEnter(_, _, _)).Times(1);
- gfx::Point entered_point(10, 10);
- data_device_manager_->data_device()->OnEnter(
- 1002, surface_->resource(), wl_fixed_from_int(entered_point.x()),
- wl_fixed_from_int(entered_point.y()), data_offer);
- Sync();
- Mock::VerifyAndClearExpectations(drop_handler_.get());
-
- EXPECT_CALL(*drop_handler_, MockOnDragDrop()).Times(1);
- base::RunLoop loop;
- drop_handler_->SetOnDropClosure(loop.QuitClosure());
- data_device_manager_->data_device()->OnDrop();
-
- Sync();
- loop.Run();
- Mock::VerifyAndClearExpectations(drop_handler_.get());
-
- const auto* const dropped_data = drop_handler_->dropped_data();
- if (kCase.expected_url.empty()) {
- EXPECT_FALSE(dropped_data->HasURL(kFilenameToURLPolicy));
- } else {
- EXPECT_TRUE(dropped_data->HasURL(kFilenameToURLPolicy));
- GURL url;
- base::string16 title;
- EXPECT_TRUE(
- dropped_data->GetURLAndTitle(kFilenameToURLPolicy, &url, &title));
- EXPECT_EQ(url.spec(), kCase.expected_url);
- EXPECT_EQ(title, base::UTF8ToUTF16(kCase.expected_title));
- }
-
- EXPECT_CALL(*drop_handler_, OnDragLeave()).Times(1);
- data_device_manager_->data_device()->OnLeave();
- Sync();
- Mock::VerifyAndClearExpectations(drop_handler_.get());
- }
-}
-
INSTANTIATE_TEST_SUITE_P(XdgVersionStableTest,
WaylandDataDeviceManagerTest,
::testing::Values(kXdgShellStable));
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller.cc b/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller.cc
new file mode 100644
index 00000000000..ec14c3e3b7f
--- /dev/null
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller.cc
@@ -0,0 +1,355 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/wayland/host/wayland_data_drag_controller.h"
+
+#include <wayland-client-protocol.h>
+
+#include <cstdint>
+
+#include "base/check.h"
+#include "base/notreached.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/base/clipboard/clipboard_constants.h"
+#include "ui/base/dragdrop/drag_drop_types.h"
+#include "ui/base/dragdrop/os_exchange_data.h"
+#include "ui/base/dragdrop/os_exchange_data_provider_non_backed.h"
+#include "ui/gfx/geometry/point_f.h"
+#include "ui/ozone/platform/wayland/common/data_util.h"
+#include "ui/ozone/platform/wayland/common/wayland_util.h"
+#include "ui/ozone/platform/wayland/host/wayland_connection.h"
+#include "ui/ozone/platform/wayland/host/wayland_data_device_manager.h"
+#include "ui/ozone/platform/wayland/host/wayland_data_offer.h"
+#include "ui/ozone/platform/wayland/host/wayland_data_source.h"
+#include "ui/ozone/platform/wayland/host/wayland_shm_buffer.h"
+#include "ui/ozone/platform/wayland/host/wayland_window.h"
+#include "ui/ozone/platform/wayland/host/wayland_window_manager.h"
+
+namespace ui {
+
+namespace {
+
+// Returns actions possible with the given source and drag'n'drop actions.
+// Also converts enums: input params are wl_data_device_manager_dnd_action but
+// the result is ui::DragDropTypes.
+int GetPossibleActions(uint32_t source_actions, uint32_t dnd_action) {
+ // If drag'n'drop action is set, use it but check for ASK action (see below).
+ uint32_t action = dnd_action != WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE
+ ? dnd_action
+ : source_actions;
+
+ // We accept any action except ASK (see below).
+ int operation = DragDropTypes::DRAG_NONE;
+ if (action & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY)
+ operation |= DragDropTypes::DRAG_COPY;
+ if (action & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE)
+ operation |= DragDropTypes::DRAG_MOVE;
+ if (action & WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK) {
+ // This is very rare and non-standard. Chromium doesn't set this when
+ // anything is dragged from it, neither it provides any UI for asking
+ // the user about the desired drag'n'drop action when data is dragged
+ // from an external source.
+ // We are safe with not adding anything here. However, keep NOTIMPLEMENTED
+ // for an (unlikely) event of this being hit in distant future.
+ NOTIMPLEMENTED_LOG_ONCE();
+ }
+ return operation;
+}
+
+const SkBitmap* GetDragImage(const OSExchangeData& data) {
+ const SkBitmap* icon_bitmap = data.provider().GetDragImage().bitmap();
+ return icon_bitmap && !icon_bitmap->empty() ? icon_bitmap : nullptr;
+}
+
+} // namespace
+
+WaylandDataDragController::WaylandDataDragController(
+ WaylandConnection* connection,
+ WaylandDataDeviceManager* data_device_manager)
+ : connection_(connection),
+ data_device_manager_(data_device_manager),
+ data_device_(data_device_manager->GetDevice()),
+ window_manager_(connection->wayland_window_manager()) {
+ DCHECK(connection_);
+ DCHECK(window_manager_);
+ DCHECK(data_device_manager_);
+ DCHECK(data_device_);
+}
+
+WaylandDataDragController::~WaylandDataDragController() = default;
+
+void WaylandDataDragController::StartSession(const OSExchangeData& data,
+ int operation) {
+ DCHECK_EQ(state_, State::kIdle);
+ DCHECK(!origin_window_);
+
+ origin_window_ = window_manager_->GetCurrentFocusedWindow();
+ if (!origin_window_) {
+ LOG(ERROR) << "Failed to get focused window.";
+ return;
+ }
+
+ // Create new new data source and offers |data|.
+ if (!data_source_)
+ data_source_ = data_device_manager_->CreateSource(this);
+ Offer(data, operation);
+
+ // Create drag icon surface (if any) and store the data to be exchanged.
+ icon_surface_.reset(CreateIconSurfaceIfNeeded(data));
+ data_ = std::make_unique<OSExchangeData>(data.provider().Clone());
+
+ // Starts the wayland drag session setting |this| object as delegate.
+ state_ = State::kStarted;
+ data_device_->StartDrag(*data_source_, *origin_window_, icon_surface_.get(),
+ this);
+}
+
+// Sessions initiated from Chromium, will have |origin_window_| pointing to the
+// window where the drag started in. In such cases, |data_| is expected to be
+// non-null, which can be used to save some IO cycles.
+bool WaylandDataDragController::IsDragSource() const {
+ DCHECK(!origin_window_ || data_);
+ return !!origin_window_;
+}
+
+void WaylandDataDragController::DrawIcon() {
+ if (!icon_bitmap_)
+ return;
+
+ DCHECK(!icon_bitmap_->empty());
+ gfx::Size size(icon_bitmap_->width(), icon_bitmap_->height());
+
+ if (!shm_buffer_ || shm_buffer_->size() != size) {
+ shm_buffer_ = std::make_unique<WaylandShmBuffer>(connection_->shm(), size);
+ if (!shm_buffer_->IsValid()) {
+ LOG(ERROR) << "Failed to create drag icon buffer.";
+ return;
+ }
+ }
+ // TODO(crbug.com/1085418): Fix drag icon scaling
+ wl::DrawBitmap(*icon_bitmap_, shm_buffer_.get());
+ wl_surface_attach(icon_surface_.get(), shm_buffer_->get(), 0, 0);
+ wl_surface_damage(icon_surface_.get(), 0, 0, size.width(), size.height());
+ wl_surface_commit(icon_surface_.get());
+}
+
+void WaylandDataDragController::OnDragOffer(
+ std::unique_ptr<WaylandDataOffer> offer) {
+ DCHECK(!data_offer_);
+ data_offer_ = std::move(offer);
+}
+
+void WaylandDataDragController::OnDragEnter(WaylandWindow* window,
+ const gfx::PointF& location,
+ uint32_t serial) {
+ DCHECK(window);
+ window_ = window;
+
+ // TODO(crbug.com/1004715): Set mime type the client can accept. Now it sets
+ // all mime types offered because current implementation doesn't decide
+ // action based on mime type.
+ unprocessed_mime_types_.clear();
+ for (auto mime : data_offer_->mime_types()) {
+ unprocessed_mime_types_.push_back(mime);
+ data_offer_->Accept(serial, mime);
+ }
+
+ std::unique_ptr<OSExchangeData> dragged_data;
+ // If the DND session was initiated from a Chromium window, |data_| already
+ // holds the data to be exchanged, so no needed to read it through Wayland,
+ // thus just copy it here.
+ if (IsDragSource())
+ dragged_data = std::make_unique<OSExchangeData>(data_->provider().Clone());
+ window_->OnDragEnter(location, std::move(dragged_data),
+ GetPossibleActions(data_offer_->source_actions(),
+ data_offer_->dnd_action()));
+}
+
+void WaylandDataDragController::OnDragMotion(const gfx::PointF& location) {
+ if (!window_)
+ return;
+
+ int client_operation = window_->OnDragMotion(
+ location, GetPossibleActions(data_offer_->source_actions(),
+ data_offer_->dnd_action()));
+ SetOperation(client_operation);
+}
+
+void WaylandDataDragController::OnDragLeave() {
+ if (!window_)
+ return;
+
+ // Leave event can arrive while data is being transferred. As it cannot be
+ // handled right away, just mark it to be processed when the data is ready.
+ if (state_ == State::kTransferring) {
+ is_leave_pending_ = true;
+ return;
+ }
+
+ window_->OnDragLeave();
+ window_ = nullptr;
+ data_offer_.reset();
+ is_leave_pending_ = false;
+}
+
+void WaylandDataDragController::OnDragDrop() {
+ if (!window_)
+ return;
+
+ if (IsDragSource()) {
+ // This means the data is being exchanged between Chromium windows. In this
+ // case, data is supposed to have already been sent to the drop handler
+ // before (see OnDragEnter()), expecting to receive null at this stage.
+ OnDataTransferFinished(nullptr);
+ return;
+ }
+
+ // Otherwise, we are about to accept data dragged from another application.
+ // Reading the data may take some time so set |state_| to |kTrasfering|, which
+ // will make final "leave" event handling to be postponed until data is ready.
+ state_ = State::kTransferring;
+ received_data_ = std::make_unique<OSExchangeData>(
+ std::make_unique<OSExchangeDataProviderNonBacked>());
+ HandleUnprocessedMimeTypes();
+}
+
+void WaylandDataDragController::OnDataSourceFinish(bool completed) {
+ DCHECK(data_source_);
+ if (origin_window_)
+ origin_window_->OnDragSessionClose(data_source_->dnd_action());
+
+ origin_window_ = nullptr;
+ data_source_.reset();
+ data_offer_.reset();
+ data_.reset();
+ state_ = State::kIdle;
+}
+
+void WaylandDataDragController::OnDataSourceSend(const std::string& mime_type,
+ std::string* buffer) {
+ DCHECK(data_source_);
+ DCHECK(buffer);
+ DCHECK(data_);
+ if (!wl::ExtractOSExchangeData(*data_, mime_type, buffer)) {
+ LOG(WARNING) << "Cannot deliver data of type " << mime_type
+ << " and no text representation is available.";
+ }
+}
+
+void WaylandDataDragController::Offer(const OSExchangeData& data,
+ int operation) {
+ DCHECK(data_source_);
+
+ // Drag'n'drop manuals usually suggest putting data in order so the more
+ // specific a MIME type is, the earlier it occurs in the list. Wayland
+ // specs don't say anything like that, but here we follow that common
+ // practice: begin with URIs and end with plain text. Just in case.
+ std::vector<std::string> mime_types;
+ if (data.HasFile()) {
+ mime_types.push_back(kMimeTypeURIList);
+ }
+ if (data.HasURL(FilenameToURLPolicy::CONVERT_FILENAMES)) {
+ mime_types.push_back(kMimeTypeMozillaURL);
+ }
+ if (data.HasHtml()) {
+ mime_types.push_back(kMimeTypeHTML);
+ }
+ if (data.HasString()) {
+ mime_types.push_back(kMimeTypeTextUtf8);
+ mime_types.push_back(kMimeTypeText);
+ }
+
+ DCHECK(!mime_types.empty());
+ data_source_->Offer(mime_types);
+ data_source_->SetAction(operation);
+}
+
+wl_surface* WaylandDataDragController::CreateIconSurfaceIfNeeded(
+ const OSExchangeData& data) {
+ icon_bitmap_ = GetDragImage(data);
+ return icon_bitmap_ ? wl_compositor_create_surface(connection_->compositor())
+ : nullptr;
+}
+
+// Asynchronously requests and reads data for every negotiated/supported mime
+// type, one after another, OnMimeTypeDataTransferred calls back into this
+// function once it finishes reading data for each mime type, until there is no
+// more unprocessed mime types on the |unprocessed_mime_types_| queue. Once this
+// process is finished, OnDataTransferFinished is called to deliver the
+// |received_data_| to the drop handler.
+void WaylandDataDragController::HandleUnprocessedMimeTypes() {
+ DCHECK_EQ(state_, State::kTransferring);
+ std::string mime_type = GetNextUnprocessedMimeType();
+ if (mime_type.empty()) {
+ OnDataTransferFinished(std::move(received_data_));
+ } else {
+ DCHECK(data_offer_);
+ data_device_->RequestData(
+ data_offer_.get(), mime_type,
+ base::BindOnce(&WaylandDataDragController::OnMimeTypeDataTransferred,
+ weak_factory_.GetWeakPtr()));
+ }
+}
+
+void WaylandDataDragController::OnMimeTypeDataTransferred(
+ const PlatformClipboard::Data& contents) {
+ DCHECK_EQ(state_, State::kTransferring);
+ if (!contents.empty()) {
+ std::string mime_type = unprocessed_mime_types_.front();
+ wl::AddToOSExchangeData(contents, mime_type, received_data_.get());
+ }
+ unprocessed_mime_types_.pop_front();
+
+ // Continue reading data for other negotiated mime types.
+ HandleUnprocessedMimeTypes();
+}
+
+void WaylandDataDragController::OnDataTransferFinished(
+ std::unique_ptr<OSExchangeData> received_data) {
+ data_offer_->FinishOffer();
+ window_->OnDragDrop(std::move(received_data));
+
+ unprocessed_mime_types_.clear();
+ state_ = State::kIdle;
+
+ // If |is_leave_pending_| is set, it means a 'leave' event was fired while
+ // data was on transit, so process it here (See OnDragLeave for more context).
+ if (is_leave_pending_)
+ OnDragLeave();
+}
+
+// Returns the next MIME type to be received from the source process, or an
+// empty string if there are no more interesting MIME types left to process.
+std::string WaylandDataDragController::GetNextUnprocessedMimeType() {
+ while (!unprocessed_mime_types_.empty()) {
+ const std::string& mime_type = unprocessed_mime_types_.front();
+ // Skip unsupported or already processed mime types.
+ if (!wl::IsMimeTypeSupported(mime_type) ||
+ wl::ContainsMimeType(*received_data_, mime_type)) {
+ unprocessed_mime_types_.pop_front();
+ continue;
+ }
+ return mime_type;
+ }
+ return {};
+}
+
+void WaylandDataDragController::SetOperation(const int operation) {
+ uint32_t dnd_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
+ uint32_t preferred_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
+
+ if (operation & DragDropTypes::DRAG_COPY) {
+ dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
+ preferred_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
+ }
+
+ if (operation & DragDropTypes::DRAG_MOVE) {
+ dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
+ if (preferred_action == WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE)
+ preferred_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
+ }
+ data_offer_->SetAction(dnd_actions, preferred_action);
+}
+
+} // namespace ui
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller.h b/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller.h
new file mode 100644
index 00000000000..2d1e1b09d6d
--- /dev/null
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller.h
@@ -0,0 +1,136 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_DATA_DRAG_CONTROLLER_H_
+#define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_DATA_DRAG_CONTROLLER_H_
+
+#include <list>
+#include <memory>
+#include <string>
+
+#include "base/gtest_prod_util.h"
+#include "base/memory/weak_ptr.h"
+#include "ui/ozone/platform/wayland/common/wayland_object.h"
+#include "ui/ozone/platform/wayland/host/wayland_data_device.h"
+#include "ui/ozone/platform/wayland/host/wayland_data_source.h"
+
+struct wl_surface;
+class SkBitmap;
+
+namespace ui {
+
+class OSExchangeData;
+class WaylandConnection;
+class WaylandDataDeviceManager;
+class WaylandDataOffer;
+class WaylandWindow;
+class WaylandWindowManager;
+class WaylandShmBuffer;
+
+// WaylandDataDragController implements regular data exchanging between Chromium
+// and other client applications on top of the Wayland Drag and Drop protocol.
+// By implementing both DataDevice::DragDelegate and DataSource::Delegate,
+// it is responsible for handling both DND sessions initiated from Chromium
+// windows as well as those triggered by other clients.
+class WaylandDataDragController : public WaylandDataDevice::DragDelegate,
+ public WaylandDataSource::Delegate {
+ public:
+ enum class State { kIdle, kStarted, kTransferring };
+
+ WaylandDataDragController(WaylandConnection* connection,
+ WaylandDataDeviceManager* data_device_manager);
+ WaylandDataDragController(const WaylandDataDragController&) = delete;
+ WaylandDataDragController& operator=(const WaylandDataDragController&) =
+ delete;
+ ~WaylandDataDragController() override;
+
+ // Starts a data drag and drop session for |data|. Only one DND session can
+ // run at a given time.
+ void StartSession(const ui::OSExchangeData& data, int operation);
+
+ State state() const { return state_; }
+
+ // TODO(crbug.com/896640): Remove once focus is fixed during DND sessions.
+ WaylandWindow* entered_window() const { return window_; }
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(WaylandDataDragControllerTest, ReceiveDrag);
+ FRIEND_TEST_ALL_PREFIXES(WaylandDataDragControllerTest, StartDrag);
+ FRIEND_TEST_ALL_PREFIXES(WaylandDataDragControllerTest, StartDragWithText);
+ FRIEND_TEST_ALL_PREFIXES(WaylandDataDragControllerTest,
+ StartDragWithWrongMimeType);
+
+ // WaylandDataDevice::DragDelegate:
+ bool IsDragSource() const override;
+ void DrawIcon() override;
+ void OnDragOffer(std::unique_ptr<WaylandDataOffer> offer) override;
+ void OnDragEnter(WaylandWindow* window,
+ const gfx::PointF& location,
+ uint32_t serial) override;
+ void OnDragMotion(const gfx::PointF& location) override;
+ void OnDragLeave() override;
+ void OnDragDrop() override;
+
+ // WaylandDataSource::Delegate:
+ void OnDataSourceFinish(bool completed) override;
+ void OnDataSourceSend(const std::string& mime_type,
+ std::string* contents) override;
+
+ void Offer(const OSExchangeData& data, int operation);
+ wl_surface* CreateIconSurfaceIfNeeded(const OSExchangeData& data);
+ void HandleUnprocessedMimeTypes();
+ void OnMimeTypeDataTransferred(const PlatformClipboard::Data& contents);
+ void OnDataTransferFinished(
+ std::unique_ptr<ui::OSExchangeData> received_data);
+ std::string GetNextUnprocessedMimeType();
+ void SetOperation(const int operation);
+
+ WaylandConnection* const connection_;
+ WaylandDataDeviceManager* const data_device_manager_;
+ WaylandDataDevice* const data_device_;
+ WaylandWindowManager* const window_manager_;
+
+ State state_ = State::kIdle;
+
+ std::unique_ptr<WaylandDataSource> data_source_;
+
+ // When dragging is started from Chromium, |data_| holds the data to be sent
+ // through wl_data_device instance.
+ std::unique_ptr<ui::OSExchangeData> data_;
+
+ // Offer to receive data from another process via drag-and-drop, or null if
+ // no drag-and-drop from another process is in progress.
+ //
+ // The data offer from another Wayland client through wl_data_device, that
+ // triggered the current drag and drop session. If null, either there is no
+ // dnd session running or Chromium is the data source.
+ std::unique_ptr<WaylandDataOffer> data_offer_;
+
+ // Mime types to be handled.
+ std::list<std::string> unprocessed_mime_types_;
+
+ // The window that initiated the drag session. Can be null when the session
+ // has been started by an external Wayland client.
+ WaylandWindow* origin_window_ = nullptr;
+
+ // Current window under pointer.
+ WaylandWindow* window_ = nullptr;
+
+ // The data delivered from Wayland
+ std::unique_ptr<ui::OSExchangeData> received_data_;
+
+ // Set when 'leave' event is fired while data is being transferred.
+ bool is_leave_pending_ = false;
+
+ // Drag icon related variables.
+ wl::Object<wl_surface> icon_surface_;
+ std::unique_ptr<WaylandShmBuffer> shm_buffer_;
+ const SkBitmap* icon_bitmap_ = nullptr;
+
+ base::WeakPtrFactory<WaylandDataDragController> weak_factory_{this};
+};
+
+} // namespace ui
+
+#endif // UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_DATA_DRAG_CONTROLLER_H_
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller_unittest.cc b/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller_unittest.cc
new file mode 100644
index 00000000000..a320c70dd1d
--- /dev/null
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_drag_controller_unittest.cc
@@ -0,0 +1,406 @@
+// 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 <wayland-server.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/containers/flat_set.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/clipboard/clipboard_constants.h"
+#include "ui/base/dragdrop/drag_drop_types.h"
+#include "ui/base/dragdrop/file_info/file_info.h"
+#include "ui/base/dragdrop/os_exchange_data.h"
+#include "ui/events/base_event_utils.h"
+#include "ui/ozone/platform/wayland/common/data_util.h"
+#include "ui/ozone/platform/wayland/host/wayland_data_device.h"
+#include "ui/ozone/platform/wayland/host/wayland_data_device_manager.h"
+#include "ui/ozone/platform/wayland/host/wayland_data_drag_controller.h"
+#include "ui/ozone/platform/wayland/host/wayland_data_source.h"
+#include "ui/ozone/platform/wayland/test/constants.h"
+#include "ui/ozone/platform/wayland/test/mock_surface.h"
+#include "ui/ozone/platform/wayland/test/test_data_device.h"
+#include "ui/ozone/platform/wayland/test/test_data_device_manager.h"
+#include "ui/ozone/platform/wayland/test/test_data_offer.h"
+#include "ui/ozone/platform/wayland/test/test_data_source.h"
+#include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h"
+#include "ui/ozone/platform/wayland/test/wayland_test.h"
+#include "ui/ozone/public/platform_clipboard.h"
+#include "ui/platform_window/platform_window_handler/wm_drop_handler.h"
+#include "url/gurl.h"
+
+using testing::_;
+using testing::Mock;
+
+namespace ui {
+
+namespace {
+
+constexpr FilenameToURLPolicy kFilenameToURLPolicy =
+ FilenameToURLPolicy::CONVERT_FILENAMES;
+
+template <typename StringType>
+PlatformClipboard::Data ToClipboardData(const StringType& data_string) {
+ PlatformClipboard::Data result;
+ auto* begin =
+ reinterpret_cast<typename PlatformClipboard::Data::const_pointer>(
+ data_string.data());
+ result.assign(begin, begin + (data_string.size() *
+ sizeof(typename StringType::value_type)));
+ return result;
+}
+
+} // namespace
+
+class MockDropHandler : public WmDropHandler {
+ public:
+ MockDropHandler() = default;
+ ~MockDropHandler() override = default;
+
+ MOCK_METHOD3(OnDragEnter,
+ void(const gfx::PointF& point,
+ std::unique_ptr<OSExchangeData> data,
+ int operation));
+ MOCK_METHOD2(OnDragMotion, int(const gfx::PointF& point, int operation));
+ MOCK_METHOD0(MockOnDragDrop, void());
+ MOCK_METHOD0(OnDragLeave, void());
+
+ void SetOnDropClosure(base::RepeatingClosure closure) {
+ on_drop_closure_ = closure;
+ }
+
+ OSExchangeData* dropped_data() { return dropped_data_.get(); }
+
+ protected:
+ void OnDragDrop(std::unique_ptr<OSExchangeData> data) override {
+ dropped_data_ = std::move(data);
+ MockOnDragDrop();
+ on_drop_closure_.Run();
+ on_drop_closure_.Reset();
+ }
+
+ private:
+ base::RepeatingClosure on_drop_closure_;
+
+ std::unique_ptr<OSExchangeData> dropped_data_;
+};
+
+class WaylandDataDragControllerTest : public WaylandTest {
+ public:
+ WaylandDataDragControllerTest() = default;
+
+ void SetUp() override {
+ WaylandTest::SetUp();
+
+ Sync();
+
+ data_device_manager_ = server_.data_device_manager();
+ DCHECK(data_device_manager_);
+
+ drop_handler_ = std::make_unique<MockDropHandler>();
+ SetWmDropHandler(window_.get(), drop_handler_.get());
+ }
+
+ WaylandDataDragController* drag_controller() const {
+ return connection_->data_drag_controller();
+ }
+
+ WaylandDataDevice* data_device() const {
+ return connection_->data_device_manager()->GetDevice();
+ }
+
+ base::string16 sample_text_for_dnd() const {
+ static auto text = base::ASCIIToUTF16(wl::kSampleTextForDragAndDrop);
+ return text;
+ }
+
+ protected:
+ wl::TestDataDeviceManager* data_device_manager_;
+ std::unique_ptr<MockDropHandler> drop_handler_;
+};
+
+TEST_P(WaylandDataDragControllerTest, StartDrag) {
+ bool restored_focus = window_->has_pointer_focus();
+ window_->SetPointerFocus(true);
+
+ // The client starts dragging.
+ OSExchangeData os_exchange_data;
+ os_exchange_data.SetString(sample_text_for_dnd());
+ int operation = DragDropTypes::DRAG_COPY | DragDropTypes::DRAG_MOVE;
+ drag_controller()->StartSession(os_exchange_data, operation);
+ Sync();
+
+ // The server reads the data and the callback gets it.
+ base::RunLoop run_loop;
+ auto callback = base::BindOnce(
+ [](base::RunLoop* loop, PlatformClipboard::Data&& data) {
+ std::string result(data.begin(), data.end());
+ EXPECT_EQ(wl::kSampleTextForDragAndDrop, result);
+ loop->Quit();
+ },
+ &run_loop);
+ data_device_manager_->data_source()->ReadData(wl::kTextMimeTypeUtf8,
+ std::move(callback));
+ run_loop.Run();
+ window_->SetPointerFocus(restored_focus);
+}
+
+TEST_P(WaylandDataDragControllerTest, StartDragWithWrongMimeType) {
+ bool restored_focus = window_->has_pointer_focus();
+ window_->SetPointerFocus(true);
+
+ // The client starts dragging offering data with |kMimeTypeHTML|
+ OSExchangeData os_exchange_data;
+ os_exchange_data.SetHtml(sample_text_for_dnd(), {});
+ int operation = DragDropTypes::DRAG_COPY | DragDropTypes::DRAG_MOVE;
+ drag_controller()->StartSession(os_exchange_data, operation);
+ Sync();
+
+ // The server should get an empty data buffer in ReadData callback when trying
+ // to read it with a different mime type.
+ base::RunLoop run_loop;
+ auto callback = base::BindOnce(
+ [](base::RunLoop* loop, PlatformClipboard::Data&& data) {
+ std::string result(data.begin(), data.end());
+ EXPECT_TRUE(result.empty());
+ loop->Quit();
+ },
+ &run_loop);
+ data_device_manager_->data_source()->ReadData(kMimeTypeText,
+ std::move(callback));
+ run_loop.Run();
+ window_->SetPointerFocus(restored_focus);
+}
+
+TEST_P(WaylandDataDragControllerTest, StartDragWithText) {
+ bool restored_focus = window_->has_pointer_focus();
+ window_->SetPointerFocus(true);
+
+ // The client starts dragging offering text mime type.
+ OSExchangeData os_exchange_data;
+ os_exchange_data.SetString(sample_text_for_dnd());
+ int operation = DragDropTypes::DRAG_COPY | DragDropTypes::DRAG_MOVE;
+ drag_controller()->StartSession(os_exchange_data, operation);
+ Sync();
+
+ // The server should get a "text" representation in ReadData callback when
+ // trying to read it as mime type other than |kMimeTypeText| and
+ // |kTextMimeTypeUtf8|.
+ base::RunLoop run_loop;
+ auto callback = base::BindOnce(
+ [](base::RunLoop* loop, PlatformClipboard::Data&& data) {
+ std::string result(data.begin(), data.end());
+ EXPECT_EQ(wl::kSampleTextForDragAndDrop, result);
+ loop->Quit();
+ },
+ &run_loop);
+ data_device_manager_->data_source()->ReadData(kMimeTypeMozillaURL,
+ std::move(callback));
+ run_loop.Run();
+ window_->SetPointerFocus(restored_focus);
+}
+
+TEST_P(WaylandDataDragControllerTest, ReceiveDrag) {
+ auto* data_offer = data_device_manager_->data_device()->OnDataOffer();
+ data_offer->OnOffer(
+ kMimeTypeText,
+ ToClipboardData(std::string(wl::kSampleTextForDragAndDrop)));
+
+ gfx::Point entered_point(10, 10);
+ // The server sends an enter event.
+ data_device_manager_->data_device()->OnEnter(
+ 1002, surface_->resource(), wl_fixed_from_int(entered_point.x()),
+ wl_fixed_from_int(entered_point.y()), data_offer);
+
+ int64_t time =
+ (EventTimeForNow() - base::TimeTicks()).InMilliseconds() & UINT32_MAX;
+ gfx::Point motion_point(11, 11);
+
+ // The server sends an motion event.
+ data_device_manager_->data_device()->OnMotion(
+ time, wl_fixed_from_int(motion_point.x()),
+ wl_fixed_from_int(motion_point.y()));
+
+ Sync();
+
+ auto callback = base::BindOnce([](const PlatformClipboard::Data& contents) {
+ std::string result;
+ result.assign(reinterpret_cast<std::string::const_pointer>(&contents[0]),
+ contents.size());
+ EXPECT_EQ(wl::kSampleTextForDragAndDrop, result);
+ });
+
+ // The client requests the data and gets callback with it.
+ data_device()->RequestData(drag_controller()->data_offer_.get(),
+ kMimeTypeText, std::move(callback));
+ Sync();
+
+ data_device_manager_->data_device()->OnLeave();
+}
+
+TEST_P(WaylandDataDragControllerTest, DropSeveralMimeTypes) {
+ auto* data_offer = data_device_manager_->data_device()->OnDataOffer();
+ data_offer->OnOffer(
+ kMimeTypeText,
+ ToClipboardData(std::string(wl::kSampleTextForDragAndDrop)));
+ data_offer->OnOffer(kMimeTypeMozillaURL, ToClipboardData(base::UTF8ToUTF16(
+ "https://sample.com/\r\n"
+ "Sample")));
+ data_offer->OnOffer(
+ kMimeTypeURIList,
+ ToClipboardData(std::string("file:///home/user/file\r\n")));
+
+ EXPECT_CALL(*drop_handler_, OnDragEnter(_, _, _)).Times(1);
+ gfx::Point entered_point(10, 10);
+ data_device_manager_->data_device()->OnEnter(
+ 1002, surface_->resource(), wl_fixed_from_int(entered_point.x()),
+ wl_fixed_from_int(entered_point.y()), data_offer);
+ Sync();
+ Mock::VerifyAndClearExpectations(drop_handler_.get());
+
+ EXPECT_CALL(*drop_handler_, MockOnDragDrop()).Times(1);
+ base::RunLoop loop;
+ drop_handler_->SetOnDropClosure(loop.QuitClosure());
+ data_device_manager_->data_device()->OnDrop();
+
+ // Here we are expecting three data items, so there will be three roundtrips
+ // to the Wayland and back. Hence Sync() three times.
+ Sync();
+ Sync();
+ Sync();
+ loop.Run();
+ Mock::VerifyAndClearExpectations(drop_handler_.get());
+
+ EXPECT_TRUE(drop_handler_->dropped_data()->HasString());
+ EXPECT_TRUE(drop_handler_->dropped_data()->HasFile());
+ EXPECT_TRUE(drop_handler_->dropped_data()->HasURL(kFilenameToURLPolicy));
+
+ data_device_manager_->data_device()->OnLeave();
+}
+
+// Tests URI validation for text/uri-list MIME type. Log warnings rendered in
+// the console when this test is running are the expected and valid side effect.
+TEST_P(WaylandDataDragControllerTest, ValidateDroppedUriList) {
+ const struct {
+ std::string content;
+ base::flat_set<std::string> expected_uris;
+ } kCases[] = {{{}, {}},
+ {"file:///home/user/file\r\n", {"/home/user/file"}},
+ {"# Comment\r\n"
+ "file:///home/user/file\r\n"
+ "file:///home/guest/file\r\n"
+ "not a filename at all\r\n"
+ "https://valid.url/but/scheme/is/not/file/so/invalid\r\n",
+ {"/home/user/file", "/home/guest/file"}}};
+
+ for (const auto& kCase : kCases) {
+ auto* data_offer = data_device_manager_->data_device()->OnDataOffer();
+ data_offer->OnOffer(kMimeTypeURIList, ToClipboardData(kCase.content));
+
+ EXPECT_CALL(*drop_handler_, OnDragEnter(_, _, _)).Times(1);
+ gfx::Point entered_point(10, 10);
+ data_device_manager_->data_device()->OnEnter(
+ 1002, surface_->resource(), wl_fixed_from_int(entered_point.x()),
+ wl_fixed_from_int(entered_point.y()), data_offer);
+ Sync();
+ Mock::VerifyAndClearExpectations(drop_handler_.get());
+
+ EXPECT_CALL(*drop_handler_, MockOnDragDrop()).Times(1);
+ base::RunLoop loop;
+ drop_handler_->SetOnDropClosure(loop.QuitClosure());
+ data_device_manager_->data_device()->OnDrop();
+
+ Sync();
+ loop.Run();
+ Mock::VerifyAndClearExpectations(drop_handler_.get());
+
+ if (kCase.expected_uris.empty()) {
+ EXPECT_FALSE(drop_handler_->dropped_data()->HasFile());
+ } else {
+ EXPECT_TRUE(drop_handler_->dropped_data()->HasFile());
+ std::vector<FileInfo> filenames;
+ EXPECT_TRUE(drop_handler_->dropped_data()->GetFilenames(&filenames));
+ EXPECT_EQ(filenames.size(), kCase.expected_uris.size());
+ for (const auto& filename : filenames)
+ EXPECT_EQ(kCase.expected_uris.count(filename.path.AsUTF8Unsafe()), 1U);
+ }
+
+ EXPECT_CALL(*drop_handler_, OnDragLeave()).Times(1);
+ data_device_manager_->data_device()->OnLeave();
+ Sync();
+ Mock::VerifyAndClearExpectations(drop_handler_.get());
+ }
+}
+
+// Tests URI validation for text/x-moz-url MIME type. Log warnings rendered in
+// the console when this test is running are the expected and valid side effect.
+TEST_P(WaylandDataDragControllerTest, ValidateDroppedXMozUrl) {
+ const struct {
+ std::string content;
+ std::string expected_url;
+ std::string expected_title;
+ } kCases[] = {
+ {{}, {}, {}},
+ {"http://sample.com/\r\nSample", "http://sample.com/", "Sample"},
+ {"http://title.must.be.set/", {}, {}},
+ {"url.must.be.valid/and/have.scheme\r\nInvalid URL", {}, {}},
+ {"file:///files/are/ok\r\nThe policy allows that", "file:///files/are/ok",
+ "The policy allows that"}};
+
+ for (const auto& kCase : kCases) {
+ auto* data_offer = data_device_manager_->data_device()->OnDataOffer();
+ data_offer->OnOffer(kMimeTypeMozillaURL,
+ ToClipboardData(base::UTF8ToUTF16(kCase.content)));
+
+ EXPECT_CALL(*drop_handler_, OnDragEnter(_, _, _)).Times(1);
+ gfx::Point entered_point(10, 10);
+ data_device_manager_->data_device()->OnEnter(
+ 1002, surface_->resource(), wl_fixed_from_int(entered_point.x()),
+ wl_fixed_from_int(entered_point.y()), data_offer);
+ Sync();
+ Mock::VerifyAndClearExpectations(drop_handler_.get());
+
+ EXPECT_CALL(*drop_handler_, MockOnDragDrop()).Times(1);
+ base::RunLoop loop;
+ drop_handler_->SetOnDropClosure(loop.QuitClosure());
+ data_device_manager_->data_device()->OnDrop();
+
+ Sync();
+ loop.Run();
+ Mock::VerifyAndClearExpectations(drop_handler_.get());
+
+ const auto* const dropped_data = drop_handler_->dropped_data();
+ if (kCase.expected_url.empty()) {
+ EXPECT_FALSE(dropped_data->HasURL(kFilenameToURLPolicy));
+ } else {
+ EXPECT_TRUE(dropped_data->HasURL(kFilenameToURLPolicy));
+ GURL url;
+ base::string16 title;
+ EXPECT_TRUE(
+ dropped_data->GetURLAndTitle(kFilenameToURLPolicy, &url, &title));
+ EXPECT_EQ(url.spec(), kCase.expected_url);
+ EXPECT_EQ(title, base::UTF8ToUTF16(kCase.expected_title));
+ }
+
+ EXPECT_CALL(*drop_handler_, OnDragLeave()).Times(1);
+ data_device_manager_->data_device()->OnLeave();
+ Sync();
+ Mock::VerifyAndClearExpectations(drop_handler_.get());
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(XdgVersionStableTest,
+ WaylandDataDragControllerTest,
+ ::testing::Values(kXdgShellStable));
+
+INSTANTIATE_TEST_SUITE_P(XdgVersionV6Test,
+ WaylandDataDragControllerTest,
+ ::testing::Values(kXdgShellV6));
+
+} // namespace ui
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_source.cc b/chromium/ui/ozone/platform/wayland/host/wayland_data_source.cc
index 0b92ceaeba7..1d66a9f74cb 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_data_source.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_source.cc
@@ -4,156 +4,148 @@
#include "ui/ozone/platform/wayland/host/wayland_data_source.h"
+#include <gtk-primary-selection-client-protocol.h>
+#include <wayland-client-protocol.h>
+
+#include <cstdint>
#include <vector>
#include "base/files/file_util.h"
-#include "base/optional.h"
-#include "ui/base/clipboard/clipboard_constants.h"
#include "ui/base/dragdrop/drag_drop_types.h"
-#include "ui/base/dragdrop/os_exchange_data.h"
#include "ui/ozone/platform/wayland/host/wayland_connection.h"
-#include "ui/ozone/platform/wayland/host/wayland_window.h"
-namespace ui {
+namespace wl {
-WaylandDataSource::WaylandDataSource(wl_data_source* data_source,
- WaylandConnection* connection)
- : data_source_(data_source), connection_(connection) {
- static const struct wl_data_source_listener kDataSourceListener = {
- WaylandDataSource::OnTarget, WaylandDataSource::OnSend,
- WaylandDataSource::OnCancel, WaylandDataSource::OnDnDDropPerformed,
- WaylandDataSource::OnDnDFinished, WaylandDataSource::OnAction};
- wl_data_source_add_listener(data_source, &kDataSourceListener, this);
+template <typename T>
+DataSource<T>::DataSource(T* data_source,
+ ui::WaylandConnection* connection,
+ Delegate* delegate)
+ : data_source_(data_source), connection_(connection), delegate_(delegate) {
+ DCHECK(data_source_);
+ DCHECK(connection_);
+ DCHECK(delegate_);
+
+ Initialize();
}
-WaylandDataSource::~WaylandDataSource() = default;
+template <typename T>
+void DataSource<T>::HandleFinishEvent(bool completed) {
+ delegate_->OnDataSourceFinish(/*completed=*/false);
+}
-void WaylandDataSource::WriteToClipboard(
- const PlatformClipboard::DataMap& data_map) {
- for (const auto& data : data_map) {
- wl_data_source_offer(data_source_.get(), data.first.c_str());
- if (strcmp(data.first.c_str(), kMimeTypeText) == 0)
- wl_data_source_offer(data_source_.get(), kMimeTypeTextUtf8);
- }
- wl_data_device_set_selection(connection_->data_device(), data_source_.get(),
- connection_->serial());
+template <typename T>
+void DataSource<T>::HandleSendEvent(const std::string& mime_type, int32_t fd) {
+ std::string contents;
+ delegate_->OnDataSourceSend(mime_type, &contents);
+ bool done = base::WriteFileDescriptor(fd, contents.data(), contents.length());
+ DCHECK(done);
+ close(fd);
+}
- connection_->ScheduleFlush();
+// static
+template <typename T>
+void DataSource<T>::OnSend(void* data,
+ T* source,
+ const char* mime_type,
+ int32_t fd) {
+ auto* self = static_cast<DataSource<T>*>(data);
+ self->HandleSendEvent(mime_type, fd);
}
-void WaylandDataSource::Offer(const ui::OSExchangeData& data) {
- // Drag'n'drop manuals usually suggest putting data in order so the more
- // specific a MIME type is, the earlier it occurs in the list. Wayland specs
- // don't say anything like that, but here we follow that common practice:
- // begin with URIs and end with plain text. Just in case.
- std::vector<std::string> mime_types;
- if (data.HasFile()) {
- mime_types.push_back(kMimeTypeURIList);
- }
- if (data.HasURL(ui::CONVERT_FILENAMES)) {
- mime_types.push_back(kMimeTypeMozillaURL);
- }
- if (data.HasHtml()) {
- mime_types.push_back(kMimeTypeHTML);
- }
- if (data.HasString()) {
- mime_types.push_back(kMimeTypeTextUtf8);
- mime_types.push_back(kMimeTypeText);
- }
+template <typename T>
+void DataSource<T>::OnCancel(void* data, T* source) {
+ auto* self = static_cast<DataSource<T>*>(data);
+ self->HandleFinishEvent(/*completed=*/false);
+}
- source_window_ =
- connection_->wayland_window_manager()->GetCurrentFocusedWindow();
- for (auto& mime_type : mime_types)
- wl_data_source_offer(data_source_.get(), mime_type.data());
+template <typename T>
+void DataSource<T>::OnDnDFinished(void* data, T* source) {
+ auto* self = static_cast<DataSource<T>*>(data);
+ self->HandleFinishEvent(/*completed=*/true);
}
-void WaylandDataSource::SetDragData(const DragDataMap& data_map) {
- DCHECK(drag_data_map_.empty());
- drag_data_map_ = data_map;
+template <typename T>
+void DataSource<T>::OnAction(void* data, T* source, uint32_t dnd_action) {
+ auto* self = static_cast<DataSource<T>*>(data);
+ self->dnd_action_ = dnd_action;
}
-void WaylandDataSource::SetAction(int operation) {
- if (wl_data_source_get_version(data_source_.get()) >=
- WL_DATA_SOURCE_SET_ACTIONS_SINCE_VERSION) {
- uint32_t dnd_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
- if (operation & DragDropTypes::DRAG_COPY)
- dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
- if (operation & DragDropTypes::DRAG_MOVE)
- dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
- wl_data_source_set_actions(data_source_.get(), dnd_actions);
- }
+template <typename T>
+void DataSource<T>::OnTarget(void* data, T* source, const char* mime_type) {
+ NOTIMPLEMENTED_LOG_ONCE();
}
-// static
-void WaylandDataSource::OnTarget(void* data,
- wl_data_source* source,
- const char* mime_type) {
+template <typename T>
+void DataSource<T>::OnDnDDropPerformed(void* data, T* source) {
NOTIMPLEMENTED_LOG_ONCE();
}
-// static
-void WaylandDataSource::OnSend(void* data,
- wl_data_source* source,
- const char* mime_type,
- int32_t fd) {
- WaylandDataSource* self = static_cast<WaylandDataSource*>(data);
- std::string contents;
- if (self->source_window_) {
- // If |source_window_| is valid when OnSend() is called, it means that DnD
- // is working.
- self->GetDragData(mime_type, &contents);
- } else {
- base::Optional<std::vector<uint8_t>> mime_data;
- self->GetClipboardData(mime_type, &mime_data);
- if (!mime_data.has_value() && strcmp(mime_type, kMimeTypeTextUtf8) == 0)
- self->GetClipboardData(kMimeTypeText, &mime_data);
- contents.assign(mime_data->begin(), mime_data->end());
- }
- bool result =
- base::WriteFileDescriptor(fd, contents.data(), contents.length());
- DCHECK(result);
- close(fd);
+//////////////////////////////////////////////////////////////////////////////
+// wl_data_source specializations and instantiation
+//////////////////////////////////////////////////////////////////////////////
+
+template <>
+void DataSource<wl_data_source>::Initialize() {
+ static const struct wl_data_source_listener kDataSourceListener = {
+ DataSource<wl_data_source>::OnTarget,
+ DataSource<wl_data_source>::OnSend,
+ DataSource<wl_data_source>::OnCancel,
+ DataSource<wl_data_source>::OnDnDDropPerformed,
+ DataSource<wl_data_source>::OnDnDFinished,
+ DataSource<wl_data_source>::OnAction};
+ wl_data_source_add_listener(data_source_.get(), &kDataSourceListener, this);
}
-// static
-void WaylandDataSource::OnCancel(void* data, wl_data_source* source) {
- WaylandDataSource* self = static_cast<WaylandDataSource*>(data);
- if (self->source_window_) {
- // If it has |source_window_|, it is in the middle of 'drag and drop'. it
- // cancels 'drag and drop'.
- self->connection_->FinishDragSession(self->dnd_action_,
- self->source_window_);
- } else {
- self->connection_->clipboard()->DataSourceCancelled(
- ClipboardBuffer::kCopyPaste);
- }
+template <>
+void DataSource<wl_data_source>::Offer(
+ const std::vector<std::string>& mime_types) {
+ for (auto& mime_type : mime_types)
+ wl_data_source_offer(data_source_.get(), mime_type.c_str());
+ connection_->ScheduleFlush();
}
-void WaylandDataSource::OnDnDDropPerformed(void* data, wl_data_source* source) {
+template <typename T>
+void DataSource<T>::SetAction(int operation) {
NOTIMPLEMENTED_LOG_ONCE();
}
-void WaylandDataSource::OnDnDFinished(void* data, wl_data_source* source) {
- WaylandDataSource* self = static_cast<WaylandDataSource*>(data);
- self->connection_->FinishDragSession(self->dnd_action_, self->source_window_);
+template <>
+void DataSource<wl_data_source>::SetAction(int operation) {
+ if (wl_data_source_get_version(data_source_.get()) >=
+ WL_DATA_SOURCE_SET_ACTIONS_SINCE_VERSION) {
+ uint32_t dnd_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
+ if (operation & ui::DragDropTypes::DRAG_COPY)
+ dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
+ if (operation & ui::DragDropTypes::DRAG_MOVE)
+ dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
+ wl_data_source_set_actions(data_source_.get(), dnd_actions);
+ }
}
-void WaylandDataSource::OnAction(void* data,
- wl_data_source* source,
- uint32_t dnd_action) {
- WaylandDataSource* self = static_cast<WaylandDataSource*>(data);
- self->dnd_action_ = dnd_action;
-}
+template class DataSource<wl_data_source>;
-void WaylandDataSource::GetDragData(const std::string& mime_type,
- std::string* contents) {
- auto it = drag_data_map_.find(mime_type);
- if (it != drag_data_map_.end()) {
- *contents = it->second;
- return;
- }
+//////////////////////////////////////////////////////////////////////////////
+// gtk_primary_selection_source specializations and instantiation
+//////////////////////////////////////////////////////////////////////////////
+
+template <>
+void DataSource<gtk_primary_selection_source>::Initialize() {
+ static const struct gtk_primary_selection_source_listener
+ kDataSourceListener = {
+ DataSource<gtk_primary_selection_source>::OnSend,
+ DataSource<gtk_primary_selection_source>::OnCancel};
+ gtk_primary_selection_source_add_listener(data_source_.get(),
+ &kDataSourceListener, this);
+}
- connection_->DeliverDragData(mime_type, contents);
+template <>
+void DataSource<gtk_primary_selection_source>::Offer(
+ const std::vector<std::string>& mime_types) {
+ for (const auto& mime_type : mime_types)
+ gtk_primary_selection_source_offer(data_source_.get(), mime_type.c_str());
+ connection_->ScheduleFlush();
}
-} // namespace ui
+template class DataSource<gtk_primary_selection_source>;
+
+} // namespace wl
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_source.h b/chromium/ui/ozone/platform/wayland/host/wayland_data_source.h
index 4f66465a699..2bcee213cc5 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_data_source.h
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_data_source.h
@@ -5,76 +5,98 @@
#ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_DATA_SOURCE_H_
#define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_DATA_SOURCE_H_
-#include <wayland-client.h>
-
-#include <map>
+#include <cstdint>
#include <string>
+#include <vector>
-#include "base/logging.h"
#include "base/macros.h"
-#include "base/optional.h"
+#include "base/notreached.h"
#include "ui/ozone/platform/wayland/common/wayland_object.h"
-#include "ui/ozone/platform/wayland/host/wayland_data_source_base.h"
-#include "ui/ozone/public/platform_clipboard.h"
+
+struct wl_data_source;
+struct gtk_primary_selection_source;
+
+namespace wl {
+template <typename T>
+class DataSource;
+} // namespace wl
namespace ui {
-class OSExchangeData;
class WaylandConnection;
-class WaylandWindow;
-
-// The WaylandDataSource object represents the source side of a
-// WaylandDataOffer. It is created by the source client in a data
-// transfer and provides a way to describe the offered data
-// (wl_data_source_offer) // and a way to respond to requests to
-// transfer the data (OnSend listener).
-class WaylandDataSource : public WaylandDataSourceBase {
- public:
- using DragDataMap = std::map<std::string, std::string>;
- // Takes ownership of data_source.
- explicit WaylandDataSource(wl_data_source* data_source,
- WaylandConnection* connection);
- ~WaylandDataSource() override;
+// DataSource represents the source side of a DataOffer. It is created by the
+// source client in a data transfer and provides a way to describe the offered
+// data and a way to respond to requests to transfer the data. There are a few
+// variants of Wayland protocol objects and extensions supporting different
+// features. E.g: regular copy/paste and drag operations are implemented by
+// wl_data_source (along with its _device and _offer counterparts), etc.
+// Implementation wise, these variants are share a single class template, with
+// specializations defined for each underlying supported extensions. Below are
+// the type aliases for the variants currently supported.
+//
+// TODO(crbug.com/1088132): Support standard primary selection extension.
+
+using WaylandDataSource = wl::DataSource<wl_data_source>;
+
+using GtkPrimarySelectionSource = wl::DataSource<gtk_primary_selection_source>;
+
+} // namespace ui
- void set_connection(WaylandConnection* connection) {
- DCHECK(connection);
- connection_ = connection;
- }
+namespace wl {
- void WriteToClipboard(const PlatformClipboard::DataMap& data_map) override;
- void Offer(const ui::OSExchangeData& data);
+// Template class implementing DataSource, whereas T is the underlying source
+// type, e.g: wl_data_source, gtk_primary_selection_source, etc. This class
+// is not supposed to be used directly, instead use the aliases defined above.
+template <typename T>
+class DataSource {
+ public:
+ class Delegate {
+ public:
+ virtual void OnDataSourceFinish(bool completed) = 0;
+ virtual void OnDataSourceSend(const std::string& mime_type,
+ std::string* contents) = 0;
+
+ protected:
+ virtual ~Delegate() = default;
+ };
+
+ // Takes ownership of |data_source|.
+ DataSource(T* data_source,
+ ui::WaylandConnection* connection,
+ Delegate* delegate);
+ DataSource(const DataSource<T>&) = delete;
+ DataSource& operator=(const DataSource<T>&) = delete;
+ ~DataSource() = default;
+
+ void Initialize();
+ void Offer(const std::vector<std::string>& mime_types);
void SetAction(int operation);
- void SetDragData(const DragDataMap& data_map);
- wl_data_source* data_source() const { return data_source_.get(); }
+ uint32_t dnd_action() const { return dnd_action_; }
+ T* data_source() const { return data_source_.get(); }
private:
- static void OnTarget(void* data,
- wl_data_source* source,
- const char* mime_type);
- static void OnSend(void* data,
- wl_data_source* source,
- const char* mime_type,
- int32_t fd);
- static void OnCancel(void* data, wl_data_source* source);
- static void OnDnDDropPerformed(void* data, wl_data_source* source);
- static void OnDnDFinished(void* data, wl_data_source* source);
- static void OnAction(void* data, wl_data_source* source, uint32_t dnd_action);
-
- void GetDragData(const std::string& mime_type, std::string* contents);
-
- wl::Object<wl_data_source> data_source_;
- WaylandConnection* connection_ = nullptr;
- WaylandWindow* source_window_ = nullptr;
-
- DragDataMap drag_data_map_;
+ void HandleFinishEvent(bool completed);
+ void HandleSendEvent(const std::string& mime_type, int32_t fd);
+
+ static void OnSend(void* data, T* source, const char* mime_type, int32_t fd);
+ static void OnCancel(void* data, T* source);
+ static void OnDnDFinished(void* data, T* source);
+ static void OnAction(void* data, T* source, uint32_t dnd_action);
+ static void OnTarget(void* data, T* source, const char* mime_type);
+ static void OnDnDDropPerformed(void* data, T* source);
+
+ wl::Object<T> data_source_;
+
+ ui::WaylandConnection* const connection_;
+
+ Delegate* const delegate_;
+
// Action selected by the compositor
uint32_t dnd_action_;
-
- DISALLOW_COPY_AND_ASSIGN(WaylandDataSource);
};
-} // namespace ui
+} // namespace wl
#endif // UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_DATA_SOURCE_H_
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_source_base.cc b/chromium/ui/ozone/platform/wayland/host/wayland_data_source_base.cc
deleted file mode 100644
index c7bba1d2538..00000000000
--- a/chromium/ui/ozone/platform/wayland/host/wayland_data_source_base.cc
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/ozone/platform/wayland/host/wayland_data_source_base.h"
-
-namespace ui {
-
-WaylandDataSourceBase::WaylandDataSourceBase() = default;
-WaylandDataSourceBase::~WaylandDataSourceBase() = default;
-
-void WaylandDataSourceBase::GetClipboardData(
- const std::string& mime_type,
- base::Optional<std::vector<uint8_t>>* data) const {
- auto it = data_map_.find(mime_type);
- if (it == data_map_.end())
- return;
- data->emplace(it->second);
-}
-
-} // namespace ui
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_data_source_base.h b/chromium/ui/ozone/platform/wayland/host/wayland_data_source_base.h
deleted file mode 100644
index 4e4d66b1607..00000000000
--- a/chromium/ui/ozone/platform/wayland/host/wayland_data_source_base.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_DATA_SOURCE_BASE_H_
-#define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_DATA_SOURCE_BASE_H_
-
-#include "base/macros.h"
-#include "ui/ozone/public/platform_clipboard.h"
-
-namespace ui {
-
-// Implements high level (protocol-agnostic) interface to a Wayland data source.
-class WaylandDataSourceBase {
- public:
- WaylandDataSourceBase();
- virtual ~WaylandDataSourceBase();
-
- void set_data_map(const PlatformClipboard::DataMap& data_map) {
- data_map_ = data_map;
- }
-
- // Writes data to the system clipboard using the protocol-defined data source.
- virtual void WriteToClipboard(const PlatformClipboard::DataMap& data_map) = 0;
-
- protected:
- void GetClipboardData(const std::string& mime_type,
- base::Optional<std::vector<uint8_t>>* data) const;
-
- private:
- PlatformClipboard::DataMap data_map_;
-
- DISALLOW_COPY_AND_ASSIGN(WaylandDataSourceBase);
-};
-
-} // namespace ui
-
-#endif // UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_DATA_SOURCE_BASE_H_
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_drm.cc b/chromium/ui/ozone/platform/wayland/host/wayland_drm.cc
index 12a91eed83a..e387e48c686 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_drm.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_drm.cc
@@ -8,6 +8,7 @@
#include <xf86drm.h>
#include "base/files/scoped_file.h"
+#include "base/logging.h"
#include "ui/gfx/buffer_format_util.h"
#include "ui/gfx/linux/drm_util_linux.h"
#include "ui/ozone/platform/wayland/host/wayland_connection.h"
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_event_source.cc b/chromium/ui/ozone/platform/wayland/host/wayland_event_source.cc
index a83c0bfe87c..48d500ae5fb 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_event_source.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_event_source.cc
@@ -96,21 +96,21 @@ void WaylandEventSource::OnKeyboardModifiersChanged(int modifiers) {
keyboard_modifiers_ = modifiers;
}
-void WaylandEventSource::OnKeyboardKeyEvent(EventType type,
- DomCode dom_code,
- DomKey dom_key,
- KeyboardCode key_code,
- bool repeat,
- base::TimeTicks timestamp) {
+uint32_t WaylandEventSource::OnKeyboardKeyEvent(EventType type,
+ DomCode dom_code,
+ DomKey dom_key,
+ KeyboardCode key_code,
+ bool repeat,
+ base::TimeTicks timestamp) {
DCHECK(type == ET_KEY_PRESSED || type == ET_KEY_RELEASED);
if (!keyboard_)
- return;
+ return POST_DISPATCH_NONE;
// try to decode key, if not yet.
if (dom_key == DomKey::NONE &&
!keyboard_->Decode(dom_code, keyboard_modifiers_, &dom_key, &key_code)) {
LOG(ERROR) << "Failed to decode key event.";
- return;
+ return POST_DISPATCH_NONE;
}
if (!repeat) {
@@ -121,7 +121,7 @@ void WaylandEventSource::OnKeyboardKeyEvent(EventType type,
KeyEvent event(type, key_code, dom_code, keyboard_modifiers_, dom_key,
timestamp);
event.set_source_device_id(keyboard_->device_id());
- DispatchEvent(&event);
+ return DispatchEvent(&event);
}
void WaylandEventSource::OnPointerCreated(WaylandPointer* pointer) {
@@ -133,15 +133,13 @@ void WaylandEventSource::OnPointerDestroyed(WaylandPointer* pointer) {
DCHECK_EQ(pointer_, pointer);
// Clear focused window, if any.
- if (auto* focused_window = window_manager_->GetCurrentFocusedWindow())
- HandlePointerFocusChange(focused_window, false);
+ HandlePointerFocusChange(nullptr);
ResetPointerFlags();
pointer_ = nullptr;
}
void WaylandEventSource::OnPointerFocusChanged(WaylandWindow* window,
- bool focused,
const gfx::PointF& location) {
if (!pointer_)
return;
@@ -149,8 +147,9 @@ void WaylandEventSource::OnPointerFocusChanged(WaylandWindow* window,
// Save new pointer location.
pointer_location_ = location;
+ bool focused = !!window;
if (focused)
- HandlePointerFocusChange(window, focused);
+ HandlePointerFocusChange(window);
EventType type = focused ? ET_MOUSE_ENTERED : ET_MOUSE_EXITED;
MouseEvent event(type, location, location, EventTimeForNow(), pointer_flags_,
@@ -158,7 +157,7 @@ void WaylandEventSource::OnPointerFocusChanged(WaylandWindow* window,
DispatchEvent(&event);
if (!focused)
- HandlePointerFocusChange(window, focused);
+ HandlePointerFocusChange(nullptr);
}
void WaylandEventSource::OnPointerButtonEvent(EventType type,
@@ -172,6 +171,7 @@ void WaylandEventSource::OnPointerButtonEvent(EventType type,
pointer_flags_ = type == ET_MOUSE_PRESSED
? (pointer_flags_ | changed_button)
: (pointer_flags_ & ~changed_button);
+ last_pointer_button_pressed_ = changed_button;
// MouseEvent's flags should contain the button that was released too.
int flags = pointer_flags_ | keyboard_modifiers_ | changed_button;
MouseEvent event(type, pointer_location_, pointer_location_,
@@ -322,22 +322,16 @@ void WaylandEventSource::HandleKeyboardFocusChange(WaylandWindow* window,
window->set_keyboard_focus(focused);
}
-void WaylandEventSource::HandlePointerFocusChange(WaylandWindow* window,
- bool focused) {
- // window can be null on wl_pointer::leave events, for example.
- if (window)
- window->SetPointerFocus(focused);
-
- if (focused) {
- DCHECK(window);
- window_with_pointer_focus_ = window;
- } else {
- // Focused window might have been destroyed at this point (eg: context
- // menus), in this case, |window| is null, otherwise it must be equal to
- // |window_with_pointer_focus_|. In both cases, they must be equal.
- DCHECK_EQ(window_with_pointer_focus_, window);
- window_with_pointer_focus_ = nullptr;
- }
+void WaylandEventSource::HandlePointerFocusChange(WaylandWindow* window) {
+ // Focused window might have been destroyed at this point (eg: context menus),
+ // in this case, |window| is null.
+ if (window_with_pointer_focus_)
+ window_with_pointer_focus_->SetPointerFocus(false);
+
+ window_with_pointer_focus_ = window;
+
+ if (window_with_pointer_focus_)
+ window_with_pointer_focus_->SetPointerFocus(true);
}
void WaylandEventSource::HandleTouchFocusChange(WaylandWindow* window,
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_event_source.h b/chromium/ui/ozone/platform/wayland/host/wayland_event_source.h
index 7f1a565c613..7c9c409e702 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_event_source.h
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_event_source.h
@@ -55,6 +55,10 @@ class WaylandEventSource : public PlatformEventSource,
WaylandEventSource& operator=(const WaylandEventSource&) = delete;
~WaylandEventSource() override;
+ int last_pointer_button_pressed() const {
+ return last_pointer_button_pressed_;
+ }
+
// Starts polling for events from the wayland connection file descriptor.
// This method assumes connection is already estabilished and input objects
// are already bound and properly initialized.
@@ -76,18 +80,17 @@ class WaylandEventSource : public PlatformEventSource,
void OnKeyboardDestroyed(WaylandKeyboard* keyboard) override;
void OnKeyboardFocusChanged(WaylandWindow* window, bool focused) override;
void OnKeyboardModifiersChanged(int modifiers) override;
- void OnKeyboardKeyEvent(EventType type,
- DomCode dom_code,
- DomKey dom_key,
- KeyboardCode key_code,
- bool repeat,
- base::TimeTicks timestamp) override;
+ uint32_t OnKeyboardKeyEvent(EventType type,
+ DomCode dom_code,
+ DomKey dom_key,
+ KeyboardCode key_code,
+ bool repeat,
+ base::TimeTicks timestamp) override;
// WaylandPointer::Delegate
void OnPointerCreated(WaylandPointer* pointer) override;
void OnPointerDestroyed(WaylandPointer* pointer) override;
void OnPointerFocusChanged(WaylandWindow* window,
- bool focused,
const gfx::PointF& location) override;
void OnPointerButtonEvent(EventType evtype, int changed_button) override;
void OnPointerMotionEvent(const gfx::PointF& location) override;
@@ -117,7 +120,7 @@ class WaylandEventSource : public PlatformEventSource,
void UpdateKeyboardModifiers(int modifier, bool down);
void HandleKeyboardFocusChange(WaylandWindow* window, bool focused);
- void HandlePointerFocusChange(WaylandWindow* window, bool focused);
+ void HandlePointerFocusChange(WaylandWindow* window);
void HandleTouchFocusChange(WaylandWindow* window,
bool focused,
base::Optional<PointerId> id = base::nullopt);
@@ -133,6 +136,9 @@ class WaylandEventSource : public PlatformEventSource,
// Bitmask of EventFlags used to keep track of the the pointer state.
int pointer_flags_ = 0;
+ // Bitmask of EventFlags used to keep track of the last changed button.
+ int last_pointer_button_pressed_ = 0;
+
// Bitmask of EventFlags used to keep track of the the keyboard state.
int keyboard_modifiers_ = 0;
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_keyboard.cc b/chromium/ui/ozone/platform/wayland/host/wayland_keyboard.cc
index 0258d157776..7de0fe694d7 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_keyboard.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_keyboard.cc
@@ -34,10 +34,12 @@ const wl_callback_listener WaylandKeyboard::callback_listener_ = {
WaylandKeyboard::SyncCallback,
};
-WaylandKeyboard::WaylandKeyboard(wl_keyboard* keyboard,
- WaylandConnection* connection,
- KeyboardLayoutEngine* layout_engine,
- Delegate* delegate)
+WaylandKeyboard::WaylandKeyboard(
+ wl_keyboard* keyboard,
+ zcr_keyboard_extension_v1* keyboard_extension_v1,
+ WaylandConnection* connection,
+ KeyboardLayoutEngine* layout_engine,
+ Delegate* delegate)
: obj_(keyboard),
connection_(connection),
delegate_(delegate),
@@ -58,6 +60,10 @@ WaylandKeyboard::WaylandKeyboard(wl_keyboard* keyboard,
wl_keyboard_add_listener(obj_.get(), &listener, this);
// TODO(tonikitoo): Default auto-repeat to ON here?
+
+ if (keyboard_extension_v1)
+ extended_keyboard_v1_.reset(zcr_keyboard_extension_v1_get_extended_keyboard(
+ keyboard_extension_v1, obj_.get()));
}
WaylandKeyboard::~WaylandKeyboard() {
@@ -193,9 +199,15 @@ void WaylandKeyboard::DispatchKey(uint32_t key,
// Pass empty DomKey and KeyboardCode here so the delegate can pre-process
// and decode it when needed.
- delegate_->OnKeyboardKeyEvent(down ? ET_KEY_PRESSED : ET_KEY_RELEASED,
- dom_code, DomKey::NONE,
- KeyboardCode::VKEY_UNKNOWN, repeat, timestamp);
+ uint32_t result = delegate_->OnKeyboardKeyEvent(
+ down ? ET_KEY_PRESSED : ET_KEY_RELEASED, dom_code, DomKey::NONE,
+ KeyboardCode::VKEY_UNKNOWN, repeat, timestamp);
+
+ if (extended_keyboard_v1_) {
+ bool handled = result & POST_DISPATCH_STOP_PROPAGATION;
+ zcr_extended_keyboard_v1_ack_key(extended_keyboard_v1_.get(),
+ connection_->serial(), handled);
+ }
}
bool WaylandKeyboard::Decode(DomCode dom_code,
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_keyboard.h b/chromium/ui/ozone/platform/wayland/host/wayland_keyboard.h
index 1630d8aa681..e449da0be85 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_keyboard.h
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_keyboard.h
@@ -5,6 +5,7 @@
#ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_KEYBOARD_H_
#define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_KEYBOARD_H_
+#include <keyboard-extension-unstable-v1-client-protocol.h>
#include <wayland-client.h>
#include "base/time/time.h"
@@ -30,6 +31,7 @@ class WaylandKeyboard : public EventAutoRepeatHandler::Delegate {
class Delegate;
WaylandKeyboard(wl_keyboard* keyboard,
+ zcr_keyboard_extension_v1* keyboard_extension_v1,
WaylandConnection* connection,
KeyboardLayoutEngine* keyboard_layout_engine,
Delegate* delegate);
@@ -88,6 +90,7 @@ class WaylandKeyboard : public EventAutoRepeatHandler::Delegate {
int flags) override;
wl::Object<wl_keyboard> obj_;
+ wl::Object<zcr_extended_keyboard_v1> extended_keyboard_v1_;
WaylandConnection* const connection_;
Delegate* const delegate_;
@@ -110,12 +113,18 @@ class WaylandKeyboard::Delegate {
virtual void OnKeyboardDestroyed(WaylandKeyboard* keyboard) = 0;
virtual void OnKeyboardFocusChanged(WaylandWindow* window, bool focused) = 0;
virtual void OnKeyboardModifiersChanged(int modifiers) = 0;
- virtual void OnKeyboardKeyEvent(EventType type,
- DomCode dom_code,
- DomKey dom_key,
- KeyboardCode key_code,
- bool repeat,
- base::TimeTicks timestamp) = 0;
+ // Returns a mask of ui::PostDispatchAction indicating how the event was
+ // dispatched.
+ virtual uint32_t OnKeyboardKeyEvent(EventType type,
+ DomCode dom_code,
+ DomKey dom_key,
+ KeyboardCode key_code,
+ bool repeat,
+ base::TimeTicks timestamp) = 0;
+
+ protected:
+ // Prevent deletion through a WaylandKeyboard::Delegate pointer.
+ virtual ~Delegate() = default;
};
} // namespace ui
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_pointer.cc b/chromium/ui/ozone/platform/wayland/host/wayland_pointer.cc
index 8a2da9f409a..1658a68cc93 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_pointer.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_pointer.cc
@@ -45,10 +45,10 @@ void WaylandPointer::Enter(void* data,
wl_fixed_t surface_y) {
DCHECK(data);
WaylandPointer* pointer = static_cast<WaylandPointer*>(data);
- gfx::PointF location(wl_fixed_to_double(surface_x),
- wl_fixed_to_double(surface_y));
- pointer->delegate_->OnPointerFocusChanged(WaylandWindow::FromSurface(surface),
- /*focused=*/true, location);
+ WaylandWindow* window = WaylandWindow::FromSurface(surface);
+ gfx::PointF location{wl_fixed_to_double(surface_x),
+ wl_fixed_to_double(surface_y)};
+ pointer->delegate_->OnPointerFocusChanged(window, location);
}
// static
@@ -58,8 +58,7 @@ void WaylandPointer::Leave(void* data,
wl_surface* surface) {
DCHECK(data);
WaylandPointer* pointer = static_cast<WaylandPointer*>(data);
- pointer->delegate_->OnPointerFocusChanged(WaylandWindow::FromSurface(surface),
- /*focused=*/false, {});
+ pointer->delegate_->OnPointerFocusChanged(nullptr, {});
}
// static
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_pointer.h b/chromium/ui/ozone/platform/wayland/host/wayland_pointer.h
index c4c9ecfbc35..b3f3a6ccbfa 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_pointer.h
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_pointer.h
@@ -73,7 +73,6 @@ class WaylandPointer::Delegate {
virtual void OnPointerCreated(WaylandPointer* pointer) = 0;
virtual void OnPointerDestroyed(WaylandPointer* pointer) = 0;
virtual void OnPointerFocusChanged(WaylandWindow* window,
- bool focused,
const gfx::PointF& location) = 0;
virtual void OnPointerButtonEvent(EventType evtype, int changed_button) = 0;
virtual void OnPointerMotionEvent(const gfx::PointF& location) = 0;
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_pointer_unittest.cc b/chromium/ui/ozone/platform/wayland/host/wayland_pointer_unittest.cc
index f20ee54c250..cf12f91f1b7 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_pointer_unittest.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_pointer_unittest.cc
@@ -237,7 +237,7 @@ TEST_P(WaylandPointerTest, SetBitmapOnPointerFocus) {
BitmapCursorFactoryOzone cursor_factory;
PlatformCursor cursor =
- cursor_factory.CreateImageCursor(dummy_cursor, gfx::Point(5, 8), 1.0f);
+ cursor_factory.CreateImageCursor(dummy_cursor, gfx::Point(5, 8));
scoped_refptr<BitmapCursorOzone> bitmap =
BitmapCursorFactoryOzone::GetBitmapCursor(cursor);
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_popup.cc b/chromium/ui/ozone/platform/wayland/host/wayland_popup.cc
index 116c7be47d1..ba52979990a 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_popup.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_popup.cc
@@ -144,9 +144,7 @@ bool WaylandPopup::OnInitialize(PlatformWindowInitProperties properties) {
}
gfx::Rect WaylandPopup::AdjustPopupWindowPosition() {
- auto* top_level_parent = wl::IsMenuType(parent_window()->type())
- ? parent_window()->parent_window()
- : parent_window();
+ auto* top_level_parent = GetRootParentWindow();
DCHECK(top_level_parent);
DCHECK(buffer_scale() == top_level_parent->buffer_scale());
DCHECK(ui_scale() == top_level_parent->ui_scale());
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_shm_buffer.cc b/chromium/ui/ozone/platform/wayland/host/wayland_shm_buffer.cc
index 298ea68b29f..e4fa21fa09a 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_shm_buffer.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_shm_buffer.cc
@@ -4,6 +4,7 @@
#include "ui/ozone/platform/wayland/host/wayland_shm_buffer.h"
+#include "base/logging.h"
#include "base/memory/platform_shared_memory_region.h"
#include "base/memory/unsafe_shared_memory_region.h"
#include "ui/gfx/skia_util.h"
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_subsurface.cc b/chromium/ui/ozone/platform/wayland/host/wayland_subsurface.cc
index 28f435f39de..e76aa6376dd 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_subsurface.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_subsurface.cc
@@ -7,6 +7,7 @@
#include "ui/ozone/platform/wayland/common/wayland_util.h"
#include "ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h"
#include "ui/ozone/platform/wayland/host/wayland_connection.h"
+#include "ui/ozone/platform/wayland/host/wayland_data_drag_controller.h"
#include "ui/ozone/platform/wayland/host/wayland_window_manager.h"
namespace ui {
@@ -15,7 +16,7 @@ namespace {
gfx::Rect AdjustSubsurfaceBounds(const gfx::Rect& bounds_px,
const gfx::Rect& parent_bounds_px,
- int32_t ui_scale,
+ float ui_scale,
int32_t buffer_scale) {
const auto parent_bounds_dip =
gfx::ScaleToRoundedRect(parent_bounds_px, 1.0 / ui_scale);
@@ -78,7 +79,7 @@ void WaylandSubsurface::CreateSubsurface() {
// windows. If we are in a drag process, use the entered window. Otherwise,
// it must be a tooltip.
if (connection()->IsDragInProgress()) {
- parent = connection()->wayland_data_device()->entered_window();
+ parent = connection()->data_drag_controller()->entered_window();
set_parent_window(parent);
} else {
// If Aura does not not provide a reference parent window, needed by
@@ -114,6 +115,11 @@ void WaylandSubsurface::CreateSubsurface() {
wl_subsurface_set_desync(subsurface_.get());
wl_surface_commit(parent->surface());
connection()->ScheduleFlush();
+
+ // Notify the observers the window has been configured. Please note that
+ // subsurface doesn't send ack configure events. Thus, notify the observers as
+ // soon as the subsurface is created.
+ connection()->wayland_window_manager()->NotifyWindowConfigured(this);
}
bool WaylandSubsurface::OnInitialize(PlatformWindowInitProperties properties) {
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_surface.cc b/chromium/ui/ozone/platform/wayland/host/wayland_surface.cc
index 6f72776d0d3..c2cf21baf62 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_surface.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_surface.cc
@@ -4,350 +4,21 @@
#include "ui/ozone/platform/wayland/host/wayland_surface.h"
-#include "ui/base/dragdrop/drag_drop_types.h"
-#include "ui/base/dragdrop/os_exchange_data.h"
-#include "ui/base/hit_test.h"
-#include "ui/gfx/native_widget_types.h"
-#include "ui/ozone/platform/wayland/host/shell_object_factory.h"
-#include "ui/ozone/platform/wayland/host/shell_surface_wrapper.h"
-#include "ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h"
-#include "ui/ozone/platform/wayland/host/wayland_connection.h"
-#include "ui/ozone/platform/wayland/host/wayland_event_source.h"
-#include "ui/platform_window/platform_window_handler/wm_drop_handler.h"
+#include "ui/ozone/platform/wayland/host/wayland_window.h"
namespace ui {
-WaylandSurface::WaylandSurface(PlatformWindowDelegate* delegate,
- WaylandConnection* connection)
- : WaylandWindow(delegate, connection),
- state_(PlatformWindowState::kNormal) {
- // Set a class property key, which allows |this| to be used for interactive
- // events, e.g. move or resize.
- SetWmMoveResizeHandler(this, AsWmMoveResizeHandler());
+WaylandSurface::WaylandSurface() = default;
+WaylandSurface::~WaylandSurface() = default;
- // Set a class property key, which allows |this| to be used for drag action.
- SetWmDragHandler(this, this);
+gfx::AcceleratedWidget WaylandSurface::GetWidget() const {
+ if (!surface_)
+ return gfx::kNullAcceleratedWidget;
+ return surface_.id();
}
-WaylandSurface::~WaylandSurface() {
- if (drag_closed_callback_) {
- std::move(drag_closed_callback_)
- .Run(DragDropTypes::DragOperation::DRAG_NONE);
- }
-}
-
-bool WaylandSurface::CreateShellSurface() {
- ShellObjectFactory factory;
- shell_surface_ = factory.CreateShellSurfaceWrapper(connection(), this);
- if (!shell_surface_) {
- LOG(ERROR) << "Failed to create a ShellSurface.";
- return false;
- }
-
- shell_surface_->SetAppId(app_id_);
- shell_surface_->SetTitle(window_title_);
- SetSizeConstraints();
- TriggerStateChanges();
- return true;
-}
-
-void WaylandSurface::ApplyPendingBounds() {
- if (pending_bounds_dip_.IsEmpty())
- return;
- DCHECK(shell_surface_);
-
- SetBoundsDip(pending_bounds_dip_);
- shell_surface_->SetWindowGeometry(pending_bounds_dip_);
- pending_bounds_dip_ = gfx::Rect();
- connection()->ScheduleFlush();
-}
-
-void WaylandSurface::DispatchHostWindowDragMovement(
- int hittest,
- const gfx::Point& pointer_location_in_px) {
- DCHECK(shell_surface_);
-
- connection()->event_source()->ResetPointerFlags();
- if (hittest == HTCAPTION)
- shell_surface_->SurfaceMove(connection());
- else
- shell_surface_->SurfaceResize(connection(), hittest);
-
- connection()->ScheduleFlush();
-}
-
-void WaylandSurface::StartDrag(const ui::OSExchangeData& data,
- int operation,
- gfx::NativeCursor cursor,
- base::OnceCallback<void(int)> callback) {
- DCHECK(!drag_closed_callback_);
- drag_closed_callback_ = std::move(callback);
- connection()->StartDrag(data, operation);
-}
-
-void WaylandSurface::Show(bool inactive) {
- if (shell_surface_)
- return;
-
- if (!CreateShellSurface()) {
- Close();
- return;
- }
-
- UpdateBufferScale(false);
-}
-
-void WaylandSurface::Hide() {
- if (!shell_surface_)
- return;
-
- if (child_window()) {
- child_window()->Hide();
- set_child_window(nullptr);
- }
-
- shell_surface_.reset();
- connection()->ScheduleFlush();
-
- // Detach buffer from surface in order to completely shutdown menus and
- // tooltips, and release resources.
- connection()->buffer_manager_host()->ResetSurfaceContents(GetWidget());
-}
-
-bool WaylandSurface::IsVisible() const {
- // X and Windows return true if the window is minimized. For consistency, do
- // the same.
- return !!shell_surface_ || state_ == PlatformWindowState::kMinimized;
-}
-
-void WaylandSurface::SetTitle(const base::string16& title) {
- if (window_title_ == title)
- return;
-
- window_title_ = title;
-
- if (shell_surface_) {
- shell_surface_->SetTitle(title);
- connection()->ScheduleFlush();
- }
-}
-
-void WaylandSurface::ToggleFullscreen() {
- // TODO(msisov, tonikitoo): add multiscreen support. As the documentation says
- // if xdg_toplevel_set_fullscreen() is not provided with wl_output, it's up
- // to the compositor to choose which display will be used to map this surface.
-
- // We must track the previous state to correctly say our state as long as it
- // can be the maximized instead of normal one.
- PlatformWindowState new_state = PlatformWindowState::kUnknown;
- if (state_ == PlatformWindowState::kFullScreen) {
- if (previous_state_ == PlatformWindowState::kMaximized)
- new_state = previous_state_;
- else
- new_state = PlatformWindowState::kNormal;
- } else {
- new_state = PlatformWindowState::kFullScreen;
- }
-
- SetWindowState(new_state);
-}
-
-void WaylandSurface::Maximize() {
- SetWindowState(PlatformWindowState::kMaximized);
-}
-
-void WaylandSurface::Minimize() {
- SetWindowState(PlatformWindowState::kMinimized);
-}
-
-void WaylandSurface::Restore() {
- DCHECK(shell_surface_);
- SetWindowState(PlatformWindowState::kNormal);
-}
-
-PlatformWindowState WaylandSurface::GetPlatformWindowState() const {
- return state_;
-}
-
-void WaylandSurface::SizeConstraintsChanged() {
- // Size constraints only make sense for normal windows.
- if (!shell_surface_)
- return;
-
- DCHECK(delegate());
- min_size_ = delegate()->GetMinimumSizeForWindow();
- max_size_ = delegate()->GetMaximumSizeForWindow();
- SetSizeConstraints();
-}
-
-void WaylandSurface::HandleSurfaceConfigure(int32_t width,
- int32_t height,
- bool is_maximized,
- bool is_fullscreen,
- bool is_activated) {
- // Store the old state to propagte state changes if Wayland decides to change
- // the state to something else.
- PlatformWindowState old_state = state_;
- if (state_ == PlatformWindowState::kMinimized && !is_activated) {
- state_ = PlatformWindowState::kMinimized;
- } else if (is_fullscreen) {
- state_ = PlatformWindowState::kFullScreen;
- } else if (is_maximized) {
- state_ = PlatformWindowState::kMaximized;
- } else {
- state_ = PlatformWindowState::kNormal;
- }
-
- const bool state_changed = old_state != state_;
- const bool is_normal = state_ == PlatformWindowState::kNormal;
-
- // Update state before notifying delegate.
- const bool did_active_change = is_active_ != is_activated;
- is_active_ = is_activated;
-
- // Rather than call SetBounds here for every configure event, just save the
- // most recent bounds, and have WaylandConnection call ApplyPendingBounds
- // when it has finished processing events. We may get many configure events
- // in a row during an interactive resize, and only the last one matters.
- //
- // Width or height set to 0 means that we should decide on width and height by
- // ourselves, but we don't want to set them to anything else. Use restored
- // bounds size or the current bounds iff the current state is normal (neither
- // maximized nor fullscreen).
- //
- // Note: if the browser was started with --start-fullscreen and a user exits
- // the fullscreen mode, wayland may set the width and height to be 1. Instead,
- // explicitly set the bounds to the current desired ones or the previous
- // bounds.
- if (width > 1 && height > 1) {
- pending_bounds_dip_ = gfx::Rect(0, 0, width, height);
- } else if (is_normal) {
- pending_bounds_dip_.set_size(
- gfx::ScaleToRoundedSize(GetRestoredBoundsInPixels().IsEmpty()
- ? GetBounds().size()
- : GetRestoredBoundsInPixels().size(),
-
- 1.0 / buffer_scale()));
- }
-
- // Store the restored bounds of current state differs from the normal state.
- // It can be client or compositor side change from normal to something else.
- // Thus, we must store previous bounds to restore later.
- SetOrResetRestoredBounds();
- ApplyPendingBounds();
-
- if (state_changed)
- delegate()->OnWindowStateChanged(state_);
-
- if (did_active_change)
- delegate()->OnActivationChanged(is_active_);
-}
-
-void WaylandSurface::OnDragEnter(const gfx::PointF& point,
- std::unique_ptr<OSExchangeData> data,
- int operation) {
- WmDropHandler* drop_handler = GetWmDropHandler(*this);
- if (!drop_handler)
- return;
-
- // Wayland sends locations in DIP so they need to be translated to
- // physical pixels.
- drop_handler->OnDragEnter(
- gfx::ScalePoint(point, buffer_scale(), buffer_scale()), std::move(data),
- operation);
-}
-
-int WaylandSurface::OnDragMotion(const gfx::PointF& point,
- uint32_t time,
- int operation) {
- WmDropHandler* drop_handler = GetWmDropHandler(*this);
- if (!drop_handler)
- return 0;
-
- // Wayland sends locations in DIP so they need to be translated to
- // physical pixels.
- return drop_handler->OnDragMotion(
- gfx::ScalePoint(point, buffer_scale(), buffer_scale()), operation);
-}
-
-void WaylandSurface::OnDragDrop(std::unique_ptr<OSExchangeData> data) {
- WmDropHandler* drop_handler = GetWmDropHandler(*this);
- if (!drop_handler)
- return;
- drop_handler->OnDragDrop(std::move(data));
-}
-
-void WaylandSurface::OnDragLeave() {
- WmDropHandler* drop_handler = GetWmDropHandler(*this);
- if (!drop_handler)
- return;
- drop_handler->OnDragLeave();
-}
-
-void WaylandSurface::OnDragSessionClose(uint32_t dnd_action) {
- std::move(drag_closed_callback_).Run(dnd_action);
- connection()->event_source()->ResetPointerFlags();
-}
-
-bool WaylandSurface::OnInitialize(PlatformWindowInitProperties properties) {
- app_id_ = properties.wm_class_class;
- return true;
-}
-
-void WaylandSurface::TriggerStateChanges() {
- if (!shell_surface_)
- return;
-
- if (state_ == PlatformWindowState::kFullScreen)
- shell_surface_->SetFullscreen();
- else
- shell_surface_->UnSetFullscreen();
-
- // Call UnSetMaximized only if current state is normal. Otherwise, if the
- // current state is fullscreen and the previous is maximized, calling
- // UnSetMaximized may result in wrong restored window position that clients
- // are not allowed to know about.
- if (state_ == PlatformWindowState::kMaximized)
- shell_surface_->SetMaximized();
- else if (state_ == PlatformWindowState::kNormal)
- shell_surface_->UnSetMaximized();
-
- if (state_ == PlatformWindowState::kMinimized)
- shell_surface_->SetMinimized();
-
- connection()->ScheduleFlush();
-}
-
-void WaylandSurface::SetWindowState(PlatformWindowState state) {
- previous_state_ = state_;
- state_ = state;
- TriggerStateChanges();
-}
-
-WmMoveResizeHandler* WaylandSurface::AsWmMoveResizeHandler() {
- return static_cast<WmMoveResizeHandler*>(this);
-}
-
-void WaylandSurface::SetSizeConstraints() {
- if (min_size_.has_value())
- shell_surface_->SetMinSize(min_size_->width(), min_size_->height());
- if (max_size_.has_value())
- shell_surface_->SetMaxSize(max_size_->width(), max_size_->height());
-
- connection()->ScheduleFlush();
-}
-
-void WaylandSurface::SetOrResetRestoredBounds() {
- // The |restored_bounds_| are used when the window gets back to normal
- // state after it went maximized or fullscreen. So we reset these if the
- // window has just become normal and store the current bounds if it is
- // either going out of normal state or simply changes the state and we don't
- // have any meaningful value stored.
- if (GetPlatformWindowState() == PlatformWindowState::kNormal) {
- SetRestoredBoundsInPixels({});
- } else if (GetRestoredBoundsInPixels().IsEmpty()) {
- SetRestoredBoundsInPixels(GetBounds());
- }
+gfx::AcceleratedWidget WaylandSurface::GetRootWidget() const {
+ return root_window_->GetWidget();
}
} // namespace ui
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_surface.h b/chromium/ui/ozone/platform/wayland/host/wayland_surface.h
index ceda32d24a7..e432ceb7e7d 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_surface.h
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_surface.h
@@ -5,121 +5,38 @@
#ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_SURFACE_H_
#define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_SURFACE_H_
-#include "ui/ozone/platform/wayland/host/wayland_window.h"
-
-#include "ui/platform_window/platform_window_handler/wm_drag_handler.h"
-#include "ui/platform_window/platform_window_handler/wm_move_resize_handler.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/ozone/platform/wayland/common/wayland_object.h"
namespace ui {
-class ShellSurfaceWrapper;
+class WaylandWindow;
-class WaylandSurface : public WaylandWindow,
- public WmMoveResizeHandler,
- public WmDragHandler {
+// Wrapper of a wl_surface, owned by a WaylandWindow or a WlSubsurface.
+class WaylandSurface {
public:
- WaylandSurface(PlatformWindowDelegate* delegate,
- WaylandConnection* connection);
- ~WaylandSurface() override;
-
- ShellSurfaceWrapper* shell_surface() const { return shell_surface_.get(); }
-
- // Apply the bounds specified in the most recent configure event. This should
- // be called after processing all pending events in the wayland connection.
- void ApplyPendingBounds();
+ WaylandSurface();
+ WaylandSurface(const WaylandSurface&) = delete;
+ WaylandSurface& operator=(const WaylandSurface&) = delete;
+ ~WaylandSurface();
- // WmMoveResizeHandler
- void DispatchHostWindowDragMovement(
- int hittest,
- const gfx::Point& pointer_location_in_px) override;
+ WaylandWindow* root_window() const { return root_window_; }
+ wl_surface* surface() const { return surface_.get(); }
+ int32_t buffer_scale() const { return buffer_scale_; }
- // WmDragHandler
- void StartDrag(const ui::OSExchangeData& data,
- int operation,
- gfx::NativeCursor cursor,
- base::OnceCallback<void(int)> callback) override;
-
- // PlatformWindow
- void Show(bool inactive) override;
- void Hide() override;
- bool IsVisible() const override;
- void SetTitle(const base::string16& title) override;
- void ToggleFullscreen() override;
- void Maximize() override;
- void Minimize() override;
- void Restore() override;
- PlatformWindowState GetPlatformWindowState() const override;
- void SizeConstraintsChanged() override;
+ // gfx::AcceleratedWidget identifies a wl_surface or a ui::WaylandWindow. Note
+ // that GetWidget() and GetRootWidget() do not necessarily return the same
+ // result.
+ gfx::AcceleratedWidget GetWidget() const;
+ gfx::AcceleratedWidget GetRootWidget() const;
private:
- // WaylandWindow overrides:
- void HandleSurfaceConfigure(int32_t widht,
- int32_t height,
- bool is_maximized,
- bool is_fullscreen,
- bool is_activated) override;
- void OnDragEnter(const gfx::PointF& point,
- std::unique_ptr<OSExchangeData> data,
- int operation) override;
- int OnDragMotion(const gfx::PointF& point,
- uint32_t time,
- int operation) override;
- void OnDragDrop(std::unique_ptr<OSExchangeData> data) override;
- void OnDragLeave() override;
- void OnDragSessionClose(uint32_t dnd_action) override;
- bool OnInitialize(PlatformWindowInitProperties properties) override;
-
- void TriggerStateChanges();
- void SetWindowState(PlatformWindowState state);
-
- // Creates a surface window, which is visible as a main window.
- bool CreateShellSurface();
-
- WmMoveResizeHandler* AsWmMoveResizeHandler();
-
- // Propagates the |min_size_| and |max_size_| to the ShellSurface.
- void SetSizeConstraints();
-
- void SetOrResetRestoredBounds();
-
- // Wrappers around shell surface.
- std::unique_ptr<ShellSurfaceWrapper> shell_surface_;
-
- base::OnceCallback<void(int)> drag_closed_callback_;
-
- // These bounds attributes below have suffices that indicate units used.
- // Wayland operates in DIP but the platform operates in physical pixels so
- // our WaylandSurface is the link that has to translate the units. See also
- // comments in the implementation.
- //
- // Bounds that will be applied when the window state is finalized. The window
- // may get several configuration events that update the pending bounds, and
- // only upon finalizing the state is the latest value stored as the current
- // bounds via |ApplyPendingBounds|. Measured in DIP because updated in the
- // handler that receives DIP from Wayland.
- gfx::Rect pending_bounds_dip_;
-
- // Contains the current state of the window.
- PlatformWindowState state_;
- // Contains the previous state of the window.
- PlatformWindowState previous_state_;
-
- bool is_active_ = false;
-
- // Id of the chromium app passed through
- // PlatformWindowInitProperties::wm_class_class. This is used by Wayland
- // compositor to identify the app, unite it's windows into the same stack of
- // windows and find *.desktop file to set various preferences including icons.
- std::string app_id_;
-
- // Title of the ShellSurface.
- base::string16 window_title_;
-
- // Max and min sizes of the WaylandSurface window.
- base::Optional<gfx::Size> min_size_;
- base::Optional<gfx::Size> max_size_;
-
- DISALLOW_COPY_AND_ASSIGN(WaylandSurface);
+ WaylandWindow* root_window_ = nullptr;
+ wl::Object<wl_surface> surface_;
+ // Wayland's scale factor for the output that this window currently belongs
+ // to.
+ int32_t buffer_scale_ = 1;
+ friend class WaylandWindow;
};
} // namespace ui
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc b/chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
new file mode 100644
index 00000000000..10ecbcf7b47
--- /dev/null
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
@@ -0,0 +1,368 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/wayland/host/wayland_toplevel_window.h"
+
+#include "ui/base/dragdrop/drag_drop_types.h"
+#include "ui/base/dragdrop/os_exchange_data.h"
+#include "ui/base/hit_test.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/ozone/platform/wayland/host/shell_object_factory.h"
+#include "ui/ozone/platform/wayland/host/shell_surface_wrapper.h"
+#include "ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h"
+#include "ui/ozone/platform/wayland/host/wayland_connection.h"
+#include "ui/ozone/platform/wayland/host/wayland_data_drag_controller.h"
+#include "ui/ozone/platform/wayland/host/wayland_event_source.h"
+#include "ui/ozone/platform/wayland/host/wayland_window_drag_controller.h"
+#include "ui/platform_window/platform_window_handler/wm_drop_handler.h"
+
+namespace ui {
+
+WaylandToplevelWindow::WaylandToplevelWindow(PlatformWindowDelegate* delegate,
+ WaylandConnection* connection)
+ : WaylandWindow(delegate, connection),
+ state_(PlatformWindowState::kNormal) {
+ // Set a class property key, which allows |this| to be used for interactive
+ // events, e.g. move or resize.
+ SetWmMoveResizeHandler(this, AsWmMoveResizeHandler());
+
+ // Set a class property key, which allows |this| to be used for drag action.
+ SetWmDragHandler(this, this);
+}
+
+WaylandToplevelWindow::~WaylandToplevelWindow() {
+ if (drag_handler_delegate_) {
+ drag_handler_delegate_->OnDragFinished(
+ DragDropTypes::DragOperation::DRAG_NONE);
+ }
+}
+
+bool WaylandToplevelWindow::CreateShellSurface() {
+ ShellObjectFactory factory;
+ shell_surface_ = factory.CreateShellSurfaceWrapper(connection(), this);
+ if (!shell_surface_) {
+ LOG(ERROR) << "Failed to create a ShellSurface.";
+ return false;
+ }
+
+ shell_surface_->SetAppId(app_id_);
+ shell_surface_->SetTitle(window_title_);
+ SetSizeConstraints();
+ TriggerStateChanges();
+ return true;
+}
+
+void WaylandToplevelWindow::ApplyPendingBounds() {
+ if (pending_bounds_dip_.IsEmpty())
+ return;
+ DCHECK(shell_surface_);
+
+ SetBoundsDip(pending_bounds_dip_);
+ shell_surface_->SetWindowGeometry(pending_bounds_dip_);
+ pending_bounds_dip_ = gfx::Rect();
+ connection()->ScheduleFlush();
+}
+
+void WaylandToplevelWindow::DispatchHostWindowDragMovement(
+ int hittest,
+ const gfx::Point& pointer_location_in_px) {
+ DCHECK(shell_surface_);
+
+ connection()->event_source()->ResetPointerFlags();
+ if (hittest == HTCAPTION)
+ shell_surface_->SurfaceMove(connection());
+ else
+ shell_surface_->SurfaceResize(connection(), hittest);
+
+ connection()->ScheduleFlush();
+}
+
+void WaylandToplevelWindow::StartDrag(const ui::OSExchangeData& data,
+ int operation,
+ gfx::NativeCursor cursor,
+ WmDragHandler::Delegate* delegate) {
+ DCHECK(!drag_handler_delegate_);
+ drag_handler_delegate_ = delegate;
+ connection()->data_drag_controller()->StartSession(data, operation);
+}
+
+void WaylandToplevelWindow::Show(bool inactive) {
+ if (shell_surface_)
+ return;
+
+ if (!CreateShellSurface()) {
+ Close();
+ return;
+ }
+
+ UpdateBufferScale(false);
+}
+
+void WaylandToplevelWindow::Hide() {
+ if (!shell_surface_)
+ return;
+
+ if (child_window()) {
+ child_window()->Hide();
+ set_child_window(nullptr);
+ }
+
+ shell_surface_.reset();
+ connection()->ScheduleFlush();
+
+ // Detach buffer from surface in order to completely shutdown menus and
+ // tooltips, and release resources.
+ connection()->buffer_manager_host()->ResetSurfaceContents(GetWidget());
+}
+
+bool WaylandToplevelWindow::IsVisible() const {
+ // X and Windows return true if the window is minimized. For consistency, do
+ // the same.
+ return !!shell_surface_ || state_ == PlatformWindowState::kMinimized;
+}
+
+void WaylandToplevelWindow::SetTitle(const base::string16& title) {
+ if (window_title_ == title)
+ return;
+
+ window_title_ = title;
+
+ if (shell_surface_) {
+ shell_surface_->SetTitle(title);
+ connection()->ScheduleFlush();
+ }
+}
+
+void WaylandToplevelWindow::ToggleFullscreen() {
+ // TODO(msisov, tonikitoo): add multiscreen support. As the documentation says
+ // if xdg_toplevel_set_fullscreen() is not provided with wl_output, it's up
+ // to the compositor to choose which display will be used to map this surface.
+
+ // We must track the previous state to correctly say our state as long as it
+ // can be the maximized instead of normal one.
+ PlatformWindowState new_state = PlatformWindowState::kUnknown;
+ if (state_ == PlatformWindowState::kFullScreen) {
+ if (previous_state_ == PlatformWindowState::kMaximized)
+ new_state = previous_state_;
+ else
+ new_state = PlatformWindowState::kNormal;
+ } else {
+ new_state = PlatformWindowState::kFullScreen;
+ }
+
+ SetWindowState(new_state);
+}
+
+void WaylandToplevelWindow::Maximize() {
+ SetWindowState(PlatformWindowState::kMaximized);
+}
+
+void WaylandToplevelWindow::Minimize() {
+ SetWindowState(PlatformWindowState::kMinimized);
+}
+
+void WaylandToplevelWindow::Restore() {
+ DCHECK(shell_surface_);
+ SetWindowState(PlatformWindowState::kNormal);
+}
+
+PlatformWindowState WaylandToplevelWindow::GetPlatformWindowState() const {
+ return state_;
+}
+
+void WaylandToplevelWindow::SizeConstraintsChanged() {
+ // Size constraints only make sense for normal windows.
+ if (!shell_surface_)
+ return;
+
+ DCHECK(delegate());
+ min_size_ = delegate()->GetMinimumSizeForWindow();
+ max_size_ = delegate()->GetMaximumSizeForWindow();
+ SetSizeConstraints();
+}
+
+void WaylandToplevelWindow::HandleSurfaceConfigure(int32_t width,
+ int32_t height,
+ bool is_maximized,
+ bool is_fullscreen,
+ bool is_activated) {
+ // Store the old state to propagte state changes if Wayland decides to change
+ // the state to something else.
+ PlatformWindowState old_state = state_;
+ if (state_ == PlatformWindowState::kMinimized && !is_activated) {
+ state_ = PlatformWindowState::kMinimized;
+ } else if (is_fullscreen) {
+ state_ = PlatformWindowState::kFullScreen;
+ } else if (is_maximized) {
+ state_ = PlatformWindowState::kMaximized;
+ } else {
+ state_ = PlatformWindowState::kNormal;
+ }
+
+ const bool state_changed = old_state != state_;
+ const bool is_normal = state_ == PlatformWindowState::kNormal;
+
+ // Update state before notifying delegate.
+ const bool did_active_change = is_active_ != is_activated;
+ is_active_ = is_activated;
+
+ // Rather than call SetBounds here for every configure event, just save the
+ // most recent bounds, and have WaylandConnection call ApplyPendingBounds
+ // when it has finished processing events. We may get many configure events
+ // in a row during an interactive resize, and only the last one matters.
+ //
+ // Width or height set to 0 means that we should decide on width and height by
+ // ourselves, but we don't want to set them to anything else. Use restored
+ // bounds size or the current bounds iff the current state is normal (neither
+ // maximized nor fullscreen).
+ //
+ // Note: if the browser was started with --start-fullscreen and a user exits
+ // the fullscreen mode, wayland may set the width and height to be 1. Instead,
+ // explicitly set the bounds to the current desired ones or the previous
+ // bounds.
+ if (width > 1 && height > 1) {
+ pending_bounds_dip_ = gfx::Rect(0, 0, width, height);
+ } else if (is_normal) {
+ pending_bounds_dip_.set_size(
+ gfx::ScaleToRoundedSize(GetRestoredBoundsInPixels().IsEmpty()
+ ? GetBounds().size()
+ : GetRestoredBoundsInPixels().size(),
+
+ 1.0 / buffer_scale()));
+ }
+
+ // Store the restored bounds of current state differs from the normal state.
+ // It can be client or compositor side change from normal to something else.
+ // Thus, we must store previous bounds to restore later.
+ SetOrResetRestoredBounds();
+ ApplyPendingBounds();
+
+ if (state_changed)
+ delegate()->OnWindowStateChanged(state_);
+
+ if (did_active_change)
+ delegate()->OnActivationChanged(is_active_);
+}
+
+void WaylandToplevelWindow::OnDragEnter(const gfx::PointF& point,
+ std::unique_ptr<OSExchangeData> data,
+ int operation) {
+ WmDropHandler* drop_handler = GetWmDropHandler(*this);
+ if (!drop_handler)
+ return;
+
+ // Wayland sends locations in DIP so they need to be translated to
+ // physical pixels.
+ drop_handler->OnDragEnter(
+ gfx::ScalePoint(point, buffer_scale(), buffer_scale()), std::move(data),
+ operation);
+}
+
+int WaylandToplevelWindow::OnDragMotion(const gfx::PointF& point,
+ int operation) {
+ WmDropHandler* drop_handler = GetWmDropHandler(*this);
+ if (!drop_handler)
+ return 0;
+
+ // Wayland sends locations in DIP so they need to be translated to
+ // physical pixels.
+ return drop_handler->OnDragMotion(
+ gfx::ScalePoint(point, buffer_scale(), buffer_scale()), operation);
+}
+
+void WaylandToplevelWindow::OnDragDrop(std::unique_ptr<OSExchangeData> data) {
+ WmDropHandler* drop_handler = GetWmDropHandler(*this);
+ if (!drop_handler)
+ return;
+ drop_handler->OnDragDrop(std::move(data));
+}
+
+void WaylandToplevelWindow::OnDragLeave() {
+ WmDropHandler* drop_handler = GetWmDropHandler(*this);
+ if (!drop_handler)
+ return;
+ drop_handler->OnDragLeave();
+}
+
+void WaylandToplevelWindow::OnDragSessionClose(uint32_t dnd_action) {
+ DCHECK(drag_handler_delegate_);
+ drag_handler_delegate_->OnDragFinished(dnd_action);
+ drag_handler_delegate_ = nullptr;
+ connection()->event_source()->ResetPointerFlags();
+}
+
+bool WaylandToplevelWindow::OnInitialize(
+ PlatformWindowInitProperties properties) {
+ app_id_ = properties.wm_class_class;
+ SetWmMoveLoopHandler(this, static_cast<WmMoveLoopHandler*>(this));
+ return true;
+}
+
+bool WaylandToplevelWindow::RunMoveLoop(const gfx::Vector2d& drag_offset) {
+ DCHECK(connection()->window_drag_controller());
+ return connection()->window_drag_controller()->Drag(this, drag_offset);
+}
+
+void WaylandToplevelWindow::EndMoveLoop() {
+ DCHECK(connection()->window_drag_controller());
+ connection()->window_drag_controller()->StopDragging();
+}
+
+void WaylandToplevelWindow::TriggerStateChanges() {
+ if (!shell_surface_)
+ return;
+
+ if (state_ == PlatformWindowState::kFullScreen)
+ shell_surface_->SetFullscreen();
+ else
+ shell_surface_->UnSetFullscreen();
+
+ // Call UnSetMaximized only if current state is normal. Otherwise, if the
+ // current state is fullscreen and the previous is maximized, calling
+ // UnSetMaximized may result in wrong restored window position that clients
+ // are not allowed to know about.
+ if (state_ == PlatformWindowState::kMaximized)
+ shell_surface_->SetMaximized();
+ else if (state_ == PlatformWindowState::kNormal)
+ shell_surface_->UnSetMaximized();
+
+ if (state_ == PlatformWindowState::kMinimized)
+ shell_surface_->SetMinimized();
+
+ connection()->ScheduleFlush();
+}
+
+void WaylandToplevelWindow::SetWindowState(PlatformWindowState state) {
+ previous_state_ = state_;
+ state_ = state;
+ TriggerStateChanges();
+}
+
+WmMoveResizeHandler* WaylandToplevelWindow::AsWmMoveResizeHandler() {
+ return static_cast<WmMoveResizeHandler*>(this);
+}
+
+void WaylandToplevelWindow::SetSizeConstraints() {
+ if (min_size_.has_value())
+ shell_surface_->SetMinSize(min_size_->width(), min_size_->height());
+ if (max_size_.has_value())
+ shell_surface_->SetMaxSize(max_size_->width(), max_size_->height());
+
+ connection()->ScheduleFlush();
+}
+
+void WaylandToplevelWindow::SetOrResetRestoredBounds() {
+ // The |restored_bounds_| are used when the window gets back to normal
+ // state after it went maximized or fullscreen. So we reset these if the
+ // window has just become normal and store the current bounds if it is
+ // either going out of normal state or simply changes the state and we don't
+ // have any meaningful value stored.
+ if (GetPlatformWindowState() == PlatformWindowState::kNormal) {
+ SetRestoredBoundsInPixels({});
+ } else if (GetRestoredBoundsInPixels().IsEmpty()) {
+ SetRestoredBoundsInPixels(GetBounds());
+ }
+}
+
+} // namespace ui
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.h b/chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.h
new file mode 100644
index 00000000000..0455078ee38
--- /dev/null
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_toplevel_window.h
@@ -0,0 +1,131 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_TOPLEVEL_WINDOW_H_
+#define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_TOPLEVEL_WINDOW_H_
+
+#include "ui/gfx/geometry/vector2d.h"
+#include "ui/ozone/platform/wayland/host/wayland_window.h"
+#include "ui/platform_window/platform_window_handler/wm_drag_handler.h"
+#include "ui/platform_window/platform_window_handler/wm_move_loop_handler.h"
+#include "ui/platform_window/platform_window_handler/wm_move_resize_handler.h"
+
+namespace ui {
+
+class ShellSurfaceWrapper;
+
+class WaylandToplevelWindow : public WaylandWindow,
+ public WmMoveResizeHandler,
+ public WmDragHandler,
+ public WmMoveLoopHandler {
+ public:
+ WaylandToplevelWindow(PlatformWindowDelegate* delegate,
+ WaylandConnection* connection);
+ WaylandToplevelWindow(const WaylandToplevelWindow&) = delete;
+ WaylandToplevelWindow& operator=(const WaylandToplevelWindow&) = delete;
+ ~WaylandToplevelWindow() override;
+
+ ShellSurfaceWrapper* shell_surface() const { return shell_surface_.get(); }
+
+ // Apply the bounds specified in the most recent configure event. This should
+ // be called after processing all pending events in the wayland connection.
+ void ApplyPendingBounds();
+
+ // WmMoveResizeHandler
+ void DispatchHostWindowDragMovement(
+ int hittest,
+ const gfx::Point& pointer_location_in_px) override;
+
+ // WmDragHandler
+ void StartDrag(const ui::OSExchangeData& data,
+ int operation,
+ gfx::NativeCursor cursor,
+ WmDragHandler::Delegate* delegate) override;
+
+ // PlatformWindow
+ void Show(bool inactive) override;
+ void Hide() override;
+ bool IsVisible() const override;
+ void SetTitle(const base::string16& title) override;
+ void ToggleFullscreen() override;
+ void Maximize() override;
+ void Minimize() override;
+ void Restore() override;
+ PlatformWindowState GetPlatformWindowState() const override;
+ void SizeConstraintsChanged() override;
+
+ private:
+ // WaylandWindow overrides:
+ void HandleSurfaceConfigure(int32_t widht,
+ int32_t height,
+ bool is_maximized,
+ bool is_fullscreen,
+ bool is_activated) override;
+ void OnDragEnter(const gfx::PointF& point,
+ std::unique_ptr<OSExchangeData> data,
+ int operation) override;
+ int OnDragMotion(const gfx::PointF& point, int operation) override;
+ void OnDragDrop(std::unique_ptr<OSExchangeData> data) override;
+ void OnDragLeave() override;
+ void OnDragSessionClose(uint32_t dnd_action) override;
+ bool OnInitialize(PlatformWindowInitProperties properties) override;
+
+ // WmMoveLoopHandler:
+ bool RunMoveLoop(const gfx::Vector2d& drag_offset) override;
+ void EndMoveLoop() override;
+
+ void TriggerStateChanges();
+ void SetWindowState(PlatformWindowState state);
+
+ // Creates a surface window, which is visible as a main window.
+ bool CreateShellSurface();
+
+ WmMoveResizeHandler* AsWmMoveResizeHandler();
+
+ // Propagates the |min_size_| and |max_size_| to the ShellSurface.
+ void SetSizeConstraints();
+
+ void SetOrResetRestoredBounds();
+
+ // Wrappers around shell surface.
+ std::unique_ptr<ShellSurfaceWrapper> shell_surface_;
+
+ WmDragHandler::Delegate* drag_handler_delegate_ = nullptr;
+
+ // These bounds attributes below have suffices that indicate units used.
+ // Wayland operates in DIP but the platform operates in physical pixels so
+ // our WaylandToplevelWindow is the link that has to translate the units. See
+ // also comments in the implementation.
+ //
+ // Bounds that will be applied when the window state is finalized. The window
+ // may get several configuration events that update the pending bounds, and
+ // only upon finalizing the state is the latest value stored as the current
+ // bounds via |ApplyPendingBounds|. Measured in DIP because updated in the
+ // handler that receives DIP from Wayland.
+ gfx::Rect pending_bounds_dip_;
+
+ // Contains the current state of the window.
+ PlatformWindowState state_;
+ // Contains the previous state of the window.
+ PlatformWindowState previous_state_;
+
+ bool is_active_ = false;
+
+ // Id of the chromium app passed through
+ // PlatformWindowInitProperties::wm_class_class. This is used by Wayland
+ // compositor to identify the app, unite it's windows into the same stack of
+ // windows and find *.desktop file to set various preferences including icons.
+ std::string app_id_;
+
+ // Title of the ShellSurface.
+ base::string16 window_title_;
+
+ // Max and min sizes of the WaylandToplevelWindow window.
+ base::Optional<gfx::Size> min_size_;
+ base::Optional<gfx::Size> max_size_;
+};
+
+} // namespace ui
+
+#endif // UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_TOPLEVEL_WINDOW_H_
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window.cc b/chromium/ui/ozone/platform/wayland/host/wayland_window.cc
index 9298cb3acbc..21ebb192e5a 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_window.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_window.cc
@@ -30,7 +30,7 @@ WaylandWindow::WaylandWindow(PlatformWindowDelegate* delegate,
WaylandWindow::~WaylandWindow() {
PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
- if (surface_)
+ if (surface())
connection_->wayland_window_manager()->RemoveWindow(GetWidget());
if (parent_window_)
@@ -61,7 +61,7 @@ void WaylandWindow::UpdateBufferScale(bool update_bounds) {
int32_t new_scale = 0;
if (parent_window_) {
- new_scale = parent_window_->buffer_scale_;
+ new_scale = parent_window_->buffer_scale();
ui_scale_ = parent_window_->ui_scale_;
} else {
const auto display = (widget == gfx::kNullAcceleratedWidget)
@@ -80,9 +80,7 @@ void WaylandWindow::UpdateBufferScale(bool update_bounds) {
}
gfx::AcceleratedWidget WaylandWindow::GetWidget() const {
- if (!surface_)
- return gfx::kNullAcceleratedWidget;
- return surface_.id();
+ return wayland_surface_.GetWidget();
}
void WaylandWindow::SetPointerFocus(bool focus) {
has_pointer_focus_ = focus;
@@ -163,7 +161,7 @@ void WaylandWindow::Restore() {}
PlatformWindowState WaylandWindow::GetPlatformWindowState() const {
// Remove normal state for all the other types of windows as it's only the
- // WaylandSurface that supports state changes.
+ // WaylandToplevelWindow that supports state changes.
return PlatformWindowState::kNormal;
}
@@ -250,6 +248,12 @@ uint32_t WaylandWindow::DispatchEvent(const PlatformEvent& native_event) {
auto* event_grabber =
connection_->wayland_window_manager()->located_events_grabber();
auto* root_parent_window = GetRootParentWindow();
+
+ // Wayland sends locations in DIP so they need to be translated to
+ // physical pixels.
+ event->AsLocatedEvent()->set_location_f(gfx::ScalePoint(
+ event->AsLocatedEvent()->location_f(), buffer_scale(), buffer_scale()));
+
// We must reroute the events to the event grabber iff these windows belong
// to the same root parent window. For example, there are 2 top level
// Wayland windows. One of them (window_1) has a child menu window that is
@@ -297,9 +301,7 @@ void WaylandWindow::OnDragEnter(const gfx::PointF& point,
std::unique_ptr<OSExchangeData> data,
int operation) {}
-int WaylandWindow::OnDragMotion(const gfx::PointF& point,
- uint32_t time,
- int operation) {
+int WaylandWindow::OnDragMotion(const gfx::PointF& point, int operation) {
return -1;
}
@@ -310,24 +312,26 @@ void WaylandWindow::OnDragLeave() {}
void WaylandWindow::OnDragSessionClose(uint32_t dnd_action) {}
void WaylandWindow::SetBoundsDip(const gfx::Rect& bounds_dip) {
- SetBounds(gfx::ScaleToRoundedRect(bounds_dip, buffer_scale_));
+ SetBounds(gfx::ScaleToRoundedRect(bounds_dip, buffer_scale()));
}
bool WaylandWindow::Initialize(PlatformWindowInitProperties properties) {
// Properties contain DIP bounds but the buffer scale is initially 1 so it's
// OK to assign. The bounds will be recalculated when the buffer scale
// changes.
- DCHECK_EQ(buffer_scale_, 1);
+ DCHECK_EQ(buffer_scale(), 1);
bounds_px_ = properties.bounds;
opacity_ = properties.opacity;
type_ = properties.type;
- surface_.reset(wl_compositor_create_surface(connection_->compositor()));
- if (!surface_) {
+ wayland_surface_.surface_.reset(
+ wl_compositor_create_surface(connection_->compositor()));
+ wayland_surface_.root_window_ = this;
+ if (!surface()) {
LOG(ERROR) << "Failed to create wl_surface";
return false;
}
- wl_surface_set_user_data(surface_.get(), this);
+ wl_surface_set_user_data(surface(), this);
AddSurfaceListener();
connection_->wayland_window_manager()->AddWindow(GetWidget(), this);
@@ -350,16 +354,16 @@ bool WaylandWindow::Initialize(PlatformWindowInitProperties properties) {
void WaylandWindow::SetBufferScale(int32_t new_scale, bool update_bounds) {
DCHECK_GT(new_scale, 0);
- if (new_scale == buffer_scale_)
+ if (new_scale == buffer_scale())
return;
- auto old_scale = buffer_scale_;
- buffer_scale_ = new_scale;
+ auto old_scale = buffer_scale();
+ wayland_surface_.buffer_scale_ = new_scale;
if (update_bounds)
SetBoundsDip(gfx::ScaleToRoundedRect(bounds_px_, 1.0 / old_scale));
DCHECK(surface());
- wl_surface_set_buffer_scale(surface(), buffer_scale_);
+ wl_surface_set_buffer_scale(surface(), buffer_scale());
connection_->ScheduleFlush();
}
@@ -377,11 +381,12 @@ WaylandWindow* WaylandWindow::GetParentWindow(
// Another case is a notifcation window or a drop down window, which do not
// have a parent in aura. In this case, take the current focused window as a
// parent.
- if (parent_window && parent_window->child_window_)
- return parent_window->child_window_;
+
if (!parent_window)
- return connection_->wayland_window_manager()->GetCurrentFocusedWindow();
- return parent_window;
+ parent_window =
+ connection_->wayland_window_manager()->GetCurrentFocusedWindow();
+
+ return parent_window ? parent_window->GetTopMostChildWindow() : nullptr;
}
WaylandWindow* WaylandWindow::GetRootParentWindow() {
@@ -393,7 +398,7 @@ void WaylandWindow::AddSurfaceListener() {
&WaylandWindow::Enter,
&WaylandWindow::Leave,
};
- wl_surface_add_listener(surface_.get(), &surface_listener, this);
+ wl_surface_add_listener(surface(), &surface_listener, this);
}
void WaylandWindow::AddEnteredOutputId(struct wl_output* output) {
@@ -468,6 +473,10 @@ WaylandWindow* WaylandWindow::GetTopLevelWindow() {
return parent_window_ ? parent_window_->GetTopLevelWindow() : this;
}
+WaylandWindow* WaylandWindow::GetTopMostChildWindow() {
+ return child_window_ ? child_window_->GetTopMostChildWindow() : this;
+}
+
void WaylandWindow::MaybeUpdateOpaqueRegion() {
if (!IsOpaqueWindow())
return;
@@ -490,10 +499,10 @@ uint32_t WaylandWindow::DispatchEventToDelegate(
if (event->IsLocatedEvent())
UpdateCursorPositionFromEvent(Event::Clone(*event));
- DispatchEventFromNativeUiEvent(
+ bool handled = DispatchEventFromNativeUiEvent(
native_event, base::BindOnce(&PlatformWindowDelegate::DispatchEvent,
base::Unretained(delegate_)));
- return POST_DISPATCH_STOP_PROPAGATION;
+ return handled ? POST_DISPATCH_STOP_PROPAGATION : POST_DISPATCH_NONE;
}
// static
@@ -502,7 +511,7 @@ void WaylandWindow::Enter(void* data,
struct wl_output* output) {
auto* window = static_cast<WaylandWindow*>(data);
if (window) {
- DCHECK(window->surface_.get() == wl_surface);
+ DCHECK(window->surface() == wl_surface);
window->AddEnteredOutputId(output);
}
}
@@ -513,7 +522,7 @@ void WaylandWindow::Leave(void* data,
struct wl_output* output) {
auto* window = static_cast<WaylandWindow*>(data);
if (window) {
- DCHECK(window->surface_.get() == wl_surface);
+ DCHECK(window->surface() == wl_surface);
window->RemoveEnteredOutputId(output);
}
}
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window.h b/chromium/ui/ozone/platform/wayland/host/wayland_window.h
index 43cfa54d4cc..9c42eb59c80 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_window.h
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_window.h
@@ -17,6 +17,7 @@
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/ozone/platform/wayland/common/wayland_object.h"
+#include "ui/ozone/platform/wayland/host/wayland_surface.h"
#include "ui/platform_window/platform_window.h"
#include "ui/platform_window/platform_window_delegate.h"
#include "ui/platform_window/platform_window_init_properties.h"
@@ -36,7 +37,7 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher {
~WaylandWindow() override;
// A factory method that can create any of the derived types of WaylandWindow
- // (WaylandSurface, WaylandPopup and WaylandSubsurface).
+ // (WaylandToplevelWindow, WaylandPopup and WaylandSubsurface).
static std::unique_ptr<WaylandWindow> Create(
PlatformWindowDelegate* delegate,
WaylandConnection* connection,
@@ -53,7 +54,8 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher {
// to do so (this is not needed upon window initialization).
void UpdateBufferScale(bool update_bounds);
- wl_surface* surface() const { return surface_.get(); }
+ WaylandSurface* wayland_surface() { return &wayland_surface_; }
+ wl_surface* surface() const { return wayland_surface_.surface(); }
void set_parent_window(WaylandWindow* parent_window) {
parent_window_ = parent_window;
@@ -80,7 +82,7 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher {
void set_child_window(WaylandWindow* window) { child_window_ = window; }
WaylandWindow* child_window() const { return child_window_; }
- int32_t buffer_scale() const { return buffer_scale_; }
+ int32_t buffer_scale() const { return wayland_surface_.buffer_scale(); }
int32_t ui_scale() const { return ui_scale_; }
const base::flat_set<uint32_t>& entered_outputs_ids() const {
@@ -144,12 +146,17 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher {
std::unique_ptr<OSExchangeData> data,
int operation);
virtual int OnDragMotion(const gfx::PointF& point,
- uint32_t time,
int operation);
virtual void OnDragDrop(std::unique_ptr<OSExchangeData> data);
virtual void OnDragLeave();
virtual void OnDragSessionClose(uint32_t dnd_action);
+ // Returns a root parent window within the same hierarchy.
+ WaylandWindow* GetRootParentWindow();
+
+ // Returns a top most child window within the same hierarchy.
+ WaylandWindow* GetTopMostChildWindow();
+
protected:
WaylandWindow(PlatformWindowDelegate* delegate,
WaylandConnection* connection);
@@ -175,9 +182,6 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher {
// Initializes the WaylandWindow with supplied properties.
bool Initialize(PlatformWindowInitProperties properties);
- // Returns a root parent window.
- WaylandWindow* GetRootParentWindow();
-
// Install a surface listener and start getting wl_output enter/leave events.
void AddSurfaceListener();
@@ -212,7 +216,7 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher {
WaylandWindow* parent_window_ = nullptr;
WaylandWindow* child_window_ = nullptr;
- wl::Object<wl_surface> surface_;
+ WaylandSurface wayland_surface_;
// The current cursor bitmap (immutable).
scoped_refptr<BitmapCursorOzone> bitmap_;
@@ -225,9 +229,6 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher {
bool has_pointer_focus_ = false;
bool has_keyboard_focus_ = false;
bool has_touch_focus_ = false;
- // Wayland's scale factor for the output that this window currently belongs
- // to.
- int32_t buffer_scale_ = 1;
// The UI scale may be forced through the command line, which means that it
// replaces the default value that is equal to the natural device scale.
// We need it to place and size the menus properly.
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller.cc b/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller.cc
new file mode 100644
index 00000000000..08d5cb633ca
--- /dev/null
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller.cc
@@ -0,0 +1,325 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/wayland/host/wayland_window_drag_controller.h"
+
+#include <cstdint>
+#include <memory>
+#include <ostream>
+#include <utility>
+
+#include "base/callback.h"
+#include "base/check.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop_current.h"
+#include "base/notreached.h"
+#include "base/run_loop.h"
+#include "ui/base/dragdrop/drag_drop_types.h"
+#include "ui/events/event.h"
+#include "ui/events/event_constants.h"
+#include "ui/events/platform/platform_event_dispatcher.h"
+#include "ui/events/platform/platform_event_source.h"
+#include "ui/events/platform/scoped_event_dispatcher.h"
+#include "ui/events/platform_event.h"
+#include "ui/events/types/event_type.h"
+#include "ui/gfx/geometry/point_f.h"
+#include "ui/gfx/geometry/vector2d.h"
+#include "ui/ozone/platform/wayland/host/wayland_connection.h"
+#include "ui/ozone/platform/wayland/host/wayland_cursor_position.h"
+#include "ui/ozone/platform/wayland/host/wayland_data_device_manager.h"
+#include "ui/ozone/platform/wayland/host/wayland_data_offer.h"
+#include "ui/ozone/platform/wayland/host/wayland_data_source.h"
+#include "ui/ozone/platform/wayland/host/wayland_event_source.h"
+#include "ui/ozone/platform/wayland/host/wayland_pointer.h"
+#include "ui/ozone/platform/wayland/host/wayland_surface.h"
+#include "ui/ozone/platform/wayland/host/wayland_window.h"
+#include "ui/ozone/platform/wayland/host/wayland_window_manager.h"
+
+namespace ui {
+
+namespace {
+
+// Custom mime type used for window dragging DND sessions.
+constexpr char kMimeTypeChromiumWindow[] = "chromium/x-window";
+
+// DND action used in window dragging DND sessions.
+constexpr uint32_t kDndActionWindowDrag =
+ WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
+
+} // namespace
+
+WaylandWindowDragController::WaylandWindowDragController(
+ WaylandConnection* connection,
+ WaylandDataDeviceManager* device_manager,
+ WaylandPointer::Delegate* pointer_delegate)
+ : connection_(connection),
+ data_device_manager_(device_manager),
+ data_device_(device_manager->GetDevice()),
+ window_manager_(connection_->wayland_window_manager()),
+ pointer_delegate_(pointer_delegate) {
+ DCHECK(data_device_);
+ DCHECK(pointer_delegate_);
+}
+
+WaylandWindowDragController::~WaylandWindowDragController() = default;
+
+bool WaylandWindowDragController::Drag(WaylandToplevelWindow* window,
+ const gfx::Vector2d& offset) {
+ DCHECK_LE(state_, State::kAttached);
+ DCHECK(window);
+
+ if (!OfferWindow())
+ return false;
+
+ DCHECK_EQ(state_, State::kAttached);
+ dragged_window_ = window;
+ drag_offset_ = offset;
+ RunLoop();
+
+ DCHECK(state_ == State::kAttached || state_ == State::kDropped);
+ bool dropped = state_ == State::kDropped;
+ if (dropped)
+ HandleDropAndResetState();
+ return dropped;
+}
+
+void WaylandWindowDragController::StopDragging() {
+ if (state_ != State::kDetached)
+ return;
+
+ VLOG(1) << "End drag loop requested. state=" << state_;
+
+ // This function is supposed to be called to indicate that the window was just
+ // snapped into a tab strip. So switch to |kAttached| state and ask to quit
+ // the nested loop.
+ state_ = State::kAttached;
+ QuitLoop();
+}
+
+bool WaylandWindowDragController::IsDragSource() const {
+ DCHECK(data_source_);
+ return true;
+}
+
+// Icon drawing and update for window/tab dragging is handled by buffer manager.
+void WaylandWindowDragController::DrawIcon() {}
+
+void WaylandWindowDragController::OnDragOffer(
+ std::unique_ptr<WaylandDataOffer> offer) {
+ DCHECK_GE(state_, State::kAttached);
+ DCHECK(offer);
+ DCHECK(!data_offer_);
+ data_offer_ = std::move(offer);
+}
+
+void WaylandWindowDragController::OnDragEnter(WaylandWindow* window,
+ const gfx::PointF& location,
+ uint32_t serial) {
+ DCHECK_GE(state_, State::kAttached);
+ DCHECK(window);
+
+ // Forward focus change event to the input delegate, so other components, such
+ // as WaylandScreen, are able to properly retrieve focus related info during
+ // window dragging sesstions.
+ pointer_delegate_->OnPointerFocusChanged(window, location);
+
+ VLOG(1) << "OnEnter. widget=" << window->GetWidget();
+
+ // Ensure this is a valid "window drag" offer.
+ DCHECK(data_offer_);
+ DCHECK_EQ(data_offer_->mime_types().size(), 1u);
+ DCHECK_EQ(data_offer_->mime_types().front(), kMimeTypeChromiumWindow);
+
+ // Accept the offer and set the dnd action.
+ data_offer_->SetAction(kDndActionWindowDrag, kDndActionWindowDrag);
+ data_offer_->Accept(serial, kMimeTypeChromiumWindow);
+}
+
+void WaylandWindowDragController::OnDragMotion(const gfx::PointF& location) {
+ DCHECK_GE(state_, State::kAttached);
+ VLOG(2) << "OnMotion. location=" << location.ToString();
+
+ // Forward cursor location update info to the input handling delegate.
+ pointer_delegate_->OnPointerMotionEvent(location);
+}
+
+void WaylandWindowDragController::OnDragLeave() {
+ DCHECK_GE(state_, State::kAttached);
+ DCHECK_LE(state_, State::kDetached);
+
+ // In order to guarantee ET_MOUSE_RELEASED event is delivered once the DND
+ // session finishes, the focused window is not reset here. This is similar to
+ // the "implicit grab" behavior implemented by Wayland compositors for
+ // wl_pointer events. Additionally, this makes it possible for the drag
+ // controller to overcome deviations in the order that wl_data_source and
+ // wl_pointer events arrive when the drop happens. For example, unlike Weston
+ // and Sway, Gnome Shell <= 2.26 sends them in the following order:
+ //
+ // wl_data_device.leave > wl_pointer.enter > wl_data_source.cancel/finish
+ //
+ // which would require hacky workarounds in HandleDropAndResetState function
+ // to properly detect and handle such cases.
+
+ VLOG(1) << "OnLeave";
+
+ data_offer_.reset();
+}
+
+void WaylandWindowDragController::OnDragDrop() {
+ // Not used for window dragging sessions. Handling of drop events is fully
+ // done at OnDataSourceFinish function, i.e: wl_data_source::{cancel,finish}.
+}
+
+void WaylandWindowDragController::OnDataSourceFinish(bool completed) {
+ DCHECK_GE(state_, State::kAttached);
+ DCHECK(data_source_);
+
+ VLOG(1) << "Drop received. state=" << state_;
+
+ // Release DND objects.
+ data_offer_.reset();
+ data_source_.reset();
+ icon_surface_.reset();
+ dragged_window_ = nullptr;
+
+ // Transition to |kDropped| state and determine the next action to take. If
+ // drop happened while the move loop was running (i.e: kDetached), ask to quit
+ // the loop, otherwise notify session end and reset state right away.
+ State state_when_dropped = std::exchange(state_, State::kDropped);
+ if (state_when_dropped == State::kDetached)
+ QuitLoop();
+ else
+ HandleDropAndResetState();
+
+ data_device_->ResetDragDelegate();
+}
+
+void WaylandWindowDragController::OnDataSourceSend(const std::string& mime_type,
+ std::string* contents) {
+ // There is no actual data exchange in DnD window dragging sessions. Window
+ // snapping, for example, is supposed to be handled at higher level UI layers.
+}
+
+bool WaylandWindowDragController::CanDispatchEvent(const PlatformEvent& event) {
+ return state_ == State::kDetached;
+}
+
+uint32_t WaylandWindowDragController::DispatchEvent(
+ const PlatformEvent& event) {
+ DCHECK_EQ(state_, State::kDetached);
+ DCHECK(base::MessageLoopCurrentForUI::IsSet());
+
+ VLOG(2) << "Dispatch. event=" << event->GetName();
+
+ if (event->type() == ET_MOUSE_MOVED || event->type() == ET_MOUSE_DRAGGED) {
+ HandleMotionEvent(event->AsMouseEvent());
+ return POST_DISPATCH_STOP_PROPAGATION;
+ }
+ return POST_DISPATCH_PERFORM_DEFAULT;
+}
+
+bool WaylandWindowDragController::OfferWindow() {
+ DCHECK_LE(state_, State::kAttached);
+
+ auto* window = window_manager_->GetCurrentFocusedWindow();
+ if (!window) {
+ LOG(ERROR) << "Failed to get focused window.";
+ return false;
+ }
+
+ if (!data_source_)
+ data_source_ = data_device_manager_->CreateSource(this);
+
+ if (state_ == State::kIdle) {
+ DCHECK(!icon_surface_);
+ icon_surface_.reset(
+ wl_compositor_create_surface(connection_->compositor()));
+
+ VLOG(1) << "Starting DND session.";
+ state_ = State::kAttached;
+ data_source_->Offer({kMimeTypeChromiumWindow});
+ data_source_->SetAction(DragDropTypes::DRAG_MOVE);
+ data_device_->StartDrag(*data_source_, *window, icon_surface_.get(), this);
+ }
+ return true;
+}
+
+void WaylandWindowDragController::HandleMotionEvent(MouseEvent* event) {
+ DCHECK_EQ(state_, State::kDetached);
+ DCHECK(dragged_window_);
+ DCHECK(event);
+
+ // Update current cursor position, so it can be retrieved later on through
+ // |Screen::GetCursorScreenPoint| API.
+ int32_t scale = dragged_window_->buffer_scale();
+ gfx::PointF scaled_location =
+ gfx::ScalePoint(event->location_f(), scale, scale);
+ connection_->wayland_cursor_position()->OnCursorPositionChanged(
+ gfx::ToFlooredPoint(scaled_location));
+
+ // Notify listeners about window bounds change (i.e: re-positioning) event.
+ // To do so, set the new bounds as per the motion event location and the drag
+ // offset. Note that setting a new location (i.e: bounds.origin()) for a
+ // surface has no visual effect in ozone/wayland backend. Actual window
+ // re-positioning during dragging session is done through the drag icon.
+ gfx::Point new_location = event->location() - drag_offset_;
+ gfx::Size size = dragged_window_->GetBounds().size();
+ dragged_window_->SetBounds({new_location, size});
+}
+
+// Dispatch mouse release event (to tell clients that the drop just happened)
+// clear focus and reset internal state. Must be called when the session is
+// about to finish.
+void WaylandWindowDragController::HandleDropAndResetState() {
+ DCHECK_EQ(state_, State::kDropped);
+ DCHECK(window_manager_->GetCurrentFocusedWindow());
+ VLOG(1) << "Notifying drop. window="
+ << window_manager_->GetCurrentFocusedWindow();
+
+ EventFlags pointer_button = EF_LEFT_MOUSE_BUTTON;
+ DCHECK(connection_->event_source()->IsPointerButtonPressed(pointer_button));
+ pointer_delegate_->OnPointerButtonEvent(ET_MOUSE_RELEASED, pointer_button);
+
+ state_ = State::kIdle;
+}
+
+void WaylandWindowDragController::RunLoop() {
+ DCHECK_EQ(state_, State::kAttached);
+ DCHECK(dragged_window_);
+
+ VLOG(1) << "Starting drag loop. widget=" << dragged_window_->GetWidget();
+
+ // TODO(crbug.com/896640): Handle cursor
+ auto old_dispatcher = std::move(nested_dispatcher_);
+ nested_dispatcher_ =
+ PlatformEventSource::GetInstance()->OverrideDispatcher(this);
+
+ base::WeakPtr<WaylandWindowDragController> alive(weak_factory_.GetWeakPtr());
+
+ state_ = State::kDetached;
+ base::RunLoop loop(base::RunLoop::Type::kNestableTasksAllowed);
+ quit_loop_closure_ = loop.QuitClosure();
+ loop.Run();
+
+ if (!alive)
+ return;
+
+ nested_dispatcher_ = std::move(old_dispatcher);
+
+ VLOG(1) << "Quitting drag loop " << state_;
+}
+
+void WaylandWindowDragController::QuitLoop() {
+ DCHECK(!quit_loop_closure_.is_null());
+
+ nested_dispatcher_.reset();
+ std::move(quit_loop_closure_).Run();
+}
+
+std::ostream& operator<<(std::ostream& out,
+ WaylandWindowDragController::State state) {
+ return out << static_cast<int>(state);
+}
+
+} // namespace ui
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller.h b/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller.h
new file mode 100644
index 00000000000..071cc9672bc
--- /dev/null
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller.h
@@ -0,0 +1,126 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_WINDOW_DRAG_CONTROLLER_H_
+#define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_WINDOW_DRAG_CONTROLLER_H_
+
+#include <cstdint>
+#include <iosfwd>
+#include <memory>
+#include <string>
+
+#include "base/callback_forward.h"
+#include "base/memory/weak_ptr.h"
+#include "ui/events/event.h"
+#include "ui/events/platform/platform_event_dispatcher.h"
+#include "ui/events/platform/scoped_event_dispatcher.h"
+#include "ui/gfx/geometry/vector2d.h"
+#include "ui/ozone/platform/wayland/gpu/wayland_surface_factory.h"
+#include "ui/ozone/platform/wayland/host/wayland_data_device.h"
+#include "ui/ozone/platform/wayland/host/wayland_data_source.h"
+#include "ui/ozone/platform/wayland/host/wayland_pointer.h"
+#include "ui/ozone/platform/wayland/host/wayland_toplevel_window.h"
+
+namespace ui {
+
+class WaylandConnection;
+class WaylandDataDeviceManager;
+class WaylandDataOffer;
+class WaylandWindow;
+class WaylandWindowManager;
+
+// Drag controller implementation that drives window moving sessions (aka: tab
+// dragging). Wayland Drag and Drop protocol is used, under the hood, to keep
+// track of cursor location and surface focus.
+//
+// TODO(crbug.com/896640): Use drag icon to emulate window moving.
+class WaylandWindowDragController : public WaylandDataDevice::DragDelegate,
+ public WaylandDataSource::Delegate,
+ public PlatformEventDispatcher {
+ public:
+ // Constants used to keep track of the drag controller state.
+ enum class State {
+ kIdle, // No DnD session nor drag loop running.
+ kAttached, // DnD session ongoing but no drag loop running.
+ kDetached, // Drag loop running. ie: blocked in a Drag() call.
+ kDropped // Drop event was just received.
+ };
+
+ WaylandWindowDragController(WaylandConnection* connection,
+ WaylandDataDeviceManager* device_manager,
+ WaylandPointer::Delegate* pointer_delegate);
+ WaylandWindowDragController(const WaylandWindowDragController&) = delete;
+ WaylandWindowDragController& operator=(const WaylandWindowDragController&) =
+ delete;
+ ~WaylandWindowDragController() override;
+
+ bool Drag(WaylandToplevelWindow* surface, const gfx::Vector2d& offset);
+ void StopDragging();
+
+ State state() const { return state_; }
+
+ private:
+ // WaylandDataDevice::DragDelegate:
+ bool IsDragSource() const override;
+ void DrawIcon() override;
+ void OnDragOffer(std::unique_ptr<WaylandDataOffer> offer) override;
+ void OnDragEnter(WaylandWindow* window,
+ const gfx::PointF& location,
+ uint32_t serial) override;
+ void OnDragMotion(const gfx::PointF& location) override;
+ void OnDragLeave() override;
+ void OnDragDrop() override;
+
+ // WaylandDataSource::Delegate
+ void OnDataSourceFinish(bool completed) override;
+ void OnDataSourceSend(const std::string& mime_type,
+ std::string* contents) override;
+
+ // PlatformEventDispatcher
+ bool CanDispatchEvent(const PlatformEvent& event) override;
+ uint32_t DispatchEvent(const PlatformEvent& event) override;
+
+ // Offers the focused window as available to be dragged. A new data source is
+ // setup and the underlying DnD session is started, if not done yet.
+ bool OfferWindow();
+ // Handles drag/move mouse |event|, while in |kDetached| mode, forwarding it
+ // as a bounds change event to the upper layer handlers.
+ void HandleMotionEvent(MouseEvent* event);
+ // Handles the mouse button release (i.e: drop). Dispatches the required
+ // events and resets the internal state.
+ void HandleDropAndResetState();
+ // Registers as the top level PlatformEvent dispatcher and runs a nested
+ // RunLoop, which blocks until the DnD session finishes.
+ void RunLoop();
+ // Unregisters the internal event dispatcher and asks to quit the nested loop.
+ void QuitLoop();
+
+ WaylandConnection* const connection_;
+ WaylandDataDeviceManager* const data_device_manager_;
+ WaylandDataDevice* const data_device_;
+ WaylandWindowManager* const window_manager_;
+ WaylandPointer::Delegate* const pointer_delegate_;
+
+ State state_ = State::kIdle;
+ WaylandToplevelWindow* dragged_window_ = nullptr;
+ gfx::Vector2d drag_offset_;
+
+ std::unique_ptr<WaylandDataSource> data_source_;
+ std::unique_ptr<WaylandDataOffer> data_offer_;
+ wl::Object<wl_surface> icon_surface_;
+
+ std::unique_ptr<ScopedEventDispatcher> nested_dispatcher_;
+ base::OnceClosure quit_loop_closure_;
+
+ base::WeakPtrFactory<WaylandWindowDragController> weak_factory_{this};
+};
+
+// Stream operator so WaylandWindowDragController::State can be used in
+// log/assertion statements.
+std::ostream& operator<<(std::ostream& out,
+ WaylandWindowDragController::State state);
+
+} // namespace ui
+
+#endif // UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_WINDOW_DRAG_CONTROLLER_H_
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller_unittest.cc b/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller_unittest.cc
new file mode 100644
index 00000000000..56a13b46e48
--- /dev/null
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_drag_controller_unittest.cc
@@ -0,0 +1,519 @@
+// 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 <linux/input-event-codes.h>
+#include <wayland-server-protocol.h>
+#include <wayland-server.h>
+#include <wayland-util.h>
+
+#include <cstdint>
+
+#include "base/notreached.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/events/base_event_utils.h"
+#include "ui/events/event.h"
+#include "ui/events/types/event_type.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/ozone/platform/wayland/host/wayland_data_device.h"
+#include "ui/ozone/platform/wayland/host/wayland_screen.h"
+#include "ui/ozone/platform/wayland/host/wayland_window.h"
+#include "ui/ozone/platform/wayland/host/wayland_window_drag_controller.h"
+#include "ui/ozone/platform/wayland/host/wayland_window_manager.h"
+#include "ui/ozone/platform/wayland/test/constants.h"
+#include "ui/ozone/platform/wayland/test/mock_pointer.h"
+#include "ui/ozone/platform/wayland/test/mock_surface.h"
+#include "ui/ozone/platform/wayland/test/test_data_device.h"
+#include "ui/ozone/platform/wayland/test/test_data_device_manager.h"
+#include "ui/ozone/platform/wayland/test/test_data_offer.h"
+#include "ui/ozone/platform/wayland/test/test_data_source.h"
+#include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h"
+#include "ui/ozone/platform/wayland/test/wayland_test.h"
+#include "ui/ozone/test/mock_platform_window_delegate.h"
+#include "ui/platform_window/platform_window_delegate.h"
+#include "ui/platform_window/platform_window_handler/wm_move_loop_handler.h"
+
+using testing::_;
+using testing::Mock;
+
+namespace ui {
+
+class WaylandWindowDragControllerTest : public WaylandTest,
+ public wl::TestDataDevice::Delegate {
+ public:
+ WaylandWindowDragControllerTest() = default;
+ ~WaylandWindowDragControllerTest() override = default;
+
+ void SetUp() override {
+ WaylandTest::SetUp();
+ screen_ = std::make_unique<WaylandScreen>(connection_.get());
+
+ wl_seat_send_capabilities(server_.seat()->resource(),
+ WL_SEAT_CAPABILITY_POINTER);
+ Sync();
+ pointer_ = server_.seat()->pointer();
+ ASSERT_TRUE(pointer_);
+
+ EXPECT_FALSE(window_->has_pointer_focus());
+ EXPECT_EQ(State::kIdle, drag_controller()->state());
+
+ data_device_manager_ = server_.data_device_manager();
+ DCHECK(data_device_manager_);
+
+ source_ = nullptr;
+ data_device_manager_->data_device()->set_delegate(this);
+ }
+
+ void TearDown() override {
+ data_device_manager_->data_device()->set_delegate(nullptr);
+ }
+
+ WaylandWindowDragController* drag_controller() const {
+ return connection_->window_drag_controller();
+ }
+
+ WaylandWindowManager* window_manager() const {
+ return connection_->wayland_window_manager();
+ }
+
+ uint32_t NextSerial() const {
+ static uint32_t serial = 0;
+ return ++serial;
+ }
+
+ uint32_t NextTime() const {
+ static uint32_t timestamp = 0;
+ return ++timestamp;
+ }
+
+ protected:
+ using State = WaylandWindowDragController::State;
+
+ // wl::TestDataDevice::Delegate:
+ void StartDrag(wl::TestDataSource* source,
+ wl::MockSurface* origin,
+ uint32_t serial) override {
+ EXPECT_FALSE(source_);
+ source_ = source;
+ OfferAndEnter(origin);
+ }
+
+ // Helper functions
+ void SendDndMotion(const gfx::Point& location) {
+ EXPECT_TRUE(source_);
+ wl_fixed_t x = wl_fixed_from_int(location.x());
+ wl_fixed_t y = wl_fixed_from_int(location.y());
+ data_device_manager_->data_device()->OnMotion(NextTime(), x, y);
+ }
+
+ void SendDndEnter(WaylandWindow* window) {
+ EXPECT_TRUE(window);
+ OfferAndEnter(server_.GetObject<wl::MockSurface>(window->GetWidget()));
+ }
+
+ void SendDndLeave() {
+ EXPECT_TRUE(source_);
+ data_device_manager_->data_device()->OnLeave();
+ }
+
+ void SendDndDrop() {
+ EXPECT_TRUE(source_);
+ source_->OnCancelled();
+ }
+
+ void SendPointerEnter(WaylandWindow* window,
+ MockPlatformWindowDelegate* delegate) {
+ auto* surface = server_.GetObject<wl::MockSurface>(window->GetWidget());
+ wl_pointer_send_enter(pointer_->resource(), NextSerial(),
+ surface->resource(), 0, 0);
+ EXPECT_CALL(*delegate, DispatchEvent(_)).Times(1);
+ Sync();
+
+ EXPECT_EQ(window, window_manager()->GetCurrentFocusedWindow());
+ }
+
+ void SendPointerPress(WaylandWindow* window,
+ MockPlatformWindowDelegate* delegate,
+ int button) {
+ wl_pointer_send_button(pointer_->resource(), NextSerial(), NextTime(),
+ button, WL_POINTER_BUTTON_STATE_PRESSED);
+ EXPECT_CALL(*delegate, DispatchEvent(_)).Times(1);
+ Sync();
+
+ EXPECT_EQ(window, window_manager()->GetCurrentFocusedWindow());
+ }
+
+ void SendPointerMotion(WaylandWindow* window,
+ MockPlatformWindowDelegate* delegate,
+ gfx::Point location) {
+ wl_fixed_t x = wl_fixed_from_int(location.x());
+ wl_fixed_t y = wl_fixed_from_int(location.y());
+ wl_pointer_send_motion(pointer_->resource(), NextTime(), x, y);
+ EXPECT_CALL(*delegate, DispatchEvent(_)).WillOnce([](Event* event) {
+ EXPECT_TRUE(event->IsMouseEvent());
+ EXPECT_EQ(ET_MOUSE_DRAGGED, event->type());
+ });
+ Sync();
+
+ EXPECT_EQ(window->GetWidget(),
+ screen_->GetLocalProcessWidgetAtPoint(location, {}));
+ }
+
+ void OfferAndEnter(wl::MockSurface* surface) {
+ EXPECT_TRUE(source_);
+ auto* data_device = data_device_manager_->data_device();
+ auto* offer = data_device->OnDataOffer();
+ EXPECT_EQ(1u, source_->mime_types().size());
+ for (const auto& mime_type : source_->mime_types())
+ offer->OnOffer(mime_type, {});
+
+ wl_data_device_send_enter(data_device->resource(), NextSerial(),
+ surface->resource(), 0, 0, offer->resource());
+ }
+
+ // client objects
+ std::unique_ptr<WaylandScreen> screen_;
+
+ // server objects
+ wl::TestDataDeviceManager* data_device_manager_;
+ wl::TestDataSource* source_;
+ wl::MockPointer* pointer_;
+};
+
+// Check the following flow works as expected:
+// 1. With a single 1 window open,
+// 2. Move pointer into it, press left button, move cursor a bit (drag),
+// 3. Run move loop, drag it within the window bounds and drop.
+TEST_P(WaylandWindowDragControllerTest, DragInsideWindowAndDrop) {
+ // Ensure there is no window currently focused
+ EXPECT_FALSE(window_manager()->GetCurrentFocusedWindow());
+ EXPECT_EQ(gfx::kNullAcceleratedWidget,
+ screen_->GetLocalProcessWidgetAtPoint({10, 10}, {}));
+
+ SendPointerEnter(window_.get(), &delegate_);
+ SendPointerPress(window_.get(), &delegate_, BTN_LEFT);
+ SendPointerMotion(window_.get(), &delegate_, {10, 10});
+
+ // Set up an "interaction flow" and RunMoveLoop:
+ // - Event dispatching and bounds changes are monitored
+ // - At each event, emulates a new event at server side and proceeds to the
+ // next test step.
+ auto* move_loop_handler = GetWmMoveLoopHandler(*window_);
+ DCHECK(move_loop_handler);
+
+ enum { kStarted, kDragging, kDropping, kDone } test_step = kStarted;
+
+ EXPECT_CALL(delegate_, DispatchEvent(_)).WillRepeatedly([&](Event* event) {
+ EXPECT_TRUE(event->IsMouseEvent());
+ switch (test_step) {
+ case kStarted:
+ EXPECT_EQ(ET_MOUSE_ENTERED, event->type());
+ EXPECT_EQ(State::kDetached, drag_controller()->state());
+ // Ensure PlatformScreen keeps consistent.
+ EXPECT_EQ(window_->GetWidget(),
+ screen_->GetLocalProcessWidgetAtPoint({10, 10}, {}));
+ // Drag it a bit more.
+ SendDndMotion({20, 20});
+ test_step = kDragging;
+ break;
+ case kDropping:
+ EXPECT_EQ(ET_MOUSE_RELEASED, event->type());
+ EXPECT_EQ(State::kDropped, drag_controller()->state());
+ // Ensure PlatformScreen keeps consistent.
+ EXPECT_EQ(gfx::Point(20, 20), screen_->GetCursorScreenPoint());
+ EXPECT_EQ(window_->GetWidget(),
+ screen_->GetLocalProcessWidgetAtPoint({20, 20}, {}));
+ test_step = kDone;
+ break;
+ case kDone:
+ EXPECT_EQ(ET_MOUSE_EXITED, event->type());
+ EXPECT_EQ(window_->GetWidget(),
+ screen_->GetLocalProcessWidgetAtPoint({20, 20}, {}));
+ break;
+ case kDragging:
+ default:
+ FAIL() << " event=" << event->GetName()
+ << " state=" << drag_controller()->state()
+ << " step=" << static_cast<int>(test_step);
+ return;
+ }
+ });
+
+ EXPECT_CALL(delegate_, OnBoundsChanged(_))
+ .WillOnce([&](const gfx::Rect& bounds) {
+ EXPECT_EQ(State::kDetached, drag_controller()->state());
+ EXPECT_EQ(kDragging, test_step);
+ EXPECT_EQ(gfx::Point(20, 20), bounds.origin());
+
+ SendDndDrop();
+ test_step = kDropping;
+ });
+
+ // RunMoveLoop() blocks until the dragging session ends, so resume test
+ // server's run loop until it returns.
+ server_.Resume();
+ move_loop_handler->RunMoveLoop({});
+ server_.Pause();
+
+ SendPointerEnter(window_.get(), &delegate_);
+ Sync();
+
+ EXPECT_EQ(State::kIdle, drag_controller()->state());
+ EXPECT_EQ(window_.get(), window_manager()->GetCurrentFocusedWindow());
+ EXPECT_EQ(window_->GetWidget(),
+ screen_->GetLocalProcessWidgetAtPoint({20, 20}, {}));
+}
+
+// Check the following flow works as expected:
+// 1. With only 1 window open;
+// 2. Move pointer into it, press left button, move cursor a bit (drag);
+// 3. Run move loop,
+// 4. Drag pointer to outside the window and release the mouse button, and make
+// sure RELEASE and EXIT mouse events are delivered even when the drop
+// happens outside the bounds of any surface.
+TEST_P(WaylandWindowDragControllerTest, DragExitWindowAndDrop) {
+ // Ensure there is no window currently focused
+ EXPECT_FALSE(window_manager()->GetCurrentFocusedWindow());
+ EXPECT_EQ(gfx::kNullAcceleratedWidget,
+ screen_->GetLocalProcessWidgetAtPoint({10, 10}, {}));
+
+ SendPointerEnter(window_.get(), &delegate_);
+ SendPointerPress(window_.get(), &delegate_, BTN_LEFT);
+ SendPointerMotion(window_.get(), &delegate_, {10, 10});
+
+ // Sets up an "interaction flow" and RunMoveLoop:
+ // - Event dispatching and bounds changes are monitored
+ // - At each event, emulates a new event on server side and proceeds to the
+ // next test step.
+ auto* move_loop_handler = GetWmMoveLoopHandler(*window_);
+ DCHECK(move_loop_handler);
+
+ enum {
+ kStarted,
+ kDragging,
+ kExitedWindow,
+ kDropping,
+ kDone
+ } test_step = kStarted;
+
+ EXPECT_CALL(delegate_, DispatchEvent(_)).WillRepeatedly([&](Event* event) {
+ EXPECT_TRUE(event->IsMouseEvent());
+ switch (test_step) {
+ case kStarted:
+ EXPECT_EQ(ET_MOUSE_ENTERED, event->type());
+ EXPECT_EQ(State::kDetached, drag_controller()->state());
+ // Ensure PlatformScreen keeps consistent.
+ EXPECT_EQ(window_->GetWidget(),
+ screen_->GetLocalProcessWidgetAtPoint({10, 10}, {}));
+ // Drag window a bit more.
+ SendDndMotion({20, 20});
+ test_step = kDragging;
+ break;
+ case kExitedWindow:
+ EXPECT_EQ(ET_MOUSE_EXITED, event->type());
+ // Release mouse button with no window foucsed.
+ SendDndDrop();
+ test_step = kDropping;
+ break;
+ case kDropping:
+ EXPECT_EQ(ET_MOUSE_RELEASED, event->type());
+ EXPECT_EQ(State::kDropped, drag_controller()->state());
+ // Ensure PlatformScreen keeps consistent.
+ EXPECT_EQ(gfx::Point(20, 20), screen_->GetCursorScreenPoint());
+ EXPECT_EQ(window_->GetWidget(),
+ screen_->GetLocalProcessWidgetAtPoint({20, 20}, {}));
+ test_step = kDone;
+ break;
+ case kDone:
+ EXPECT_EQ(ET_MOUSE_EXITED, event->type());
+ break;
+ case kDragging:
+ default:
+ FAIL() << " event=" << event->GetName()
+ << " state=" << drag_controller()->state()
+ << " step=" << static_cast<int>(test_step);
+ return;
+ }
+ });
+
+ EXPECT_CALL(delegate_, OnBoundsChanged(_))
+ .WillOnce([&](const gfx::Rect& bounds) {
+ EXPECT_EQ(State::kDetached, drag_controller()->state());
+ EXPECT_EQ(kDragging, test_step);
+ EXPECT_EQ(gfx::Point(20, 20), bounds.origin());
+
+ SendDndDrop();
+ test_step = kDropping;
+ });
+
+ // RunMoveLoop() blocks until the dragging sessions ends, so resume test
+ // server's run loop until it returns.
+ server_.Resume();
+ move_loop_handler->RunMoveLoop({});
+ server_.Pause();
+
+ SendPointerEnter(window_.get(), &delegate_);
+ Sync();
+
+ EXPECT_EQ(State::kIdle, drag_controller()->state());
+ EXPECT_EQ(window_.get(), window_manager()->GetCurrentFocusedWindow());
+ EXPECT_EQ(window_->GetWidget(),
+ screen_->GetLocalProcessWidgetAtPoint({20, 20}, {}));
+}
+
+// Check the following flow works as expected:
+// 1. With 2 windows open,
+// 2. Focus window 1, starts dragging,
+// 3. Run move loop,
+// 4. Drag the pointer out of window 1 and then into window 2,
+// 5. Drag it a bit more (within window 2) and then calls EndMoveLoop(),
+// emulating a window snap), and then
+// 6. With the window in "snapped" state, drag it further and then drop.
+TEST_P(WaylandWindowDragControllerTest, DragToOtherWindowSnapDragDrop) {
+ // Init and open |target_window|.
+ PlatformWindowInitProperties properties{gfx::Rect{80, 80}};
+ properties.type = PlatformWindowType::kWindow;
+ EXPECT_CALL(delegate_, OnAcceleratedWidgetAvailable(_)).Times(1);
+ auto window_2 = WaylandWindow::Create(&delegate_, connection_.get(),
+ std::move(properties));
+ ASSERT_NE(gfx::kNullAcceleratedWidget, window_2->GetWidget());
+ Sync();
+
+ // Ensure there is no window currently focused
+ EXPECT_FALSE(window_manager()->GetCurrentFocusedWindow());
+ EXPECT_EQ(gfx::kNullAcceleratedWidget,
+ screen_->GetLocalProcessWidgetAtPoint({10, 10}, {}));
+
+ auto* source_window = window_.get();
+ auto* target_window = window_2.get();
+ EXPECT_TRUE(source_window);
+ EXPECT_TRUE(target_window);
+
+ SendPointerEnter(source_window, &delegate_);
+ SendPointerPress(source_window, &delegate_, BTN_LEFT);
+ SendPointerMotion(source_window, &delegate_, {10, 10});
+
+ // Sets up an "interaction flow" and RunMoveLoop:
+ // - Event dispatching and bounds changes are monitored
+ // - At each event, emulates a new event on server side and proceeds to the
+ // next test step.
+ auto* move_loop_handler = GetWmMoveLoopHandler(*window_);
+ DCHECK(move_loop_handler);
+
+ enum {
+ kStarted,
+ kDragging,
+ kEnteredTarget,
+ kSnapped,
+ kDone
+ } test_step = kStarted;
+
+ EXPECT_CALL(delegate_, DispatchEvent(_)).WillRepeatedly([&](Event* event) {
+ EXPECT_TRUE(event->IsMouseEvent());
+ switch (test_step) {
+ case kStarted:
+ EXPECT_EQ(ET_MOUSE_ENTERED, event->type());
+ EXPECT_EQ(State::kDetached, drag_controller()->state());
+ // Ensure PlatformScreen keeps consistent.
+ EXPECT_EQ(source_window->GetWidget(),
+ screen_->GetLocalProcessWidgetAtPoint({10, 10}, {}));
+ // Drag window a bit more.
+ SendDndMotion({50, 50});
+ test_step = kDragging;
+ break;
+ case kEnteredTarget:
+ EXPECT_EQ(ET_MOUSE_ENTERED, event->type());
+ EXPECT_EQ(State::kDetached, drag_controller()->state());
+ // Ensure PlatformScreen keeps consistent.
+ EXPECT_EQ(target_window->GetWidget(),
+ screen_->GetLocalProcessWidgetAtPoint({10, 10}, {}));
+
+ move_loop_handler->EndMoveLoop();
+ test_step = kSnapped;
+ break;
+ default:
+ FAIL() << " event=" << event->GetName()
+ << " state=" << drag_controller()->state()
+ << " step=" << static_cast<int>(test_step);
+ return;
+ }
+ });
+
+ EXPECT_CALL(delegate_, OnBoundsChanged(_))
+ .WillOnce([&](const gfx::Rect& bounds) {
+ EXPECT_EQ(State::kDetached, drag_controller()->state());
+ EXPECT_EQ(kDragging, test_step);
+ EXPECT_EQ(gfx::Point(50, 50), bounds.origin());
+
+ // Exit |source_window| and enter the |target_window|.
+ SendDndLeave();
+ SendDndEnter(target_window);
+ test_step = kEnteredTarget;
+ });
+
+ // RunMoveLoop() blocks until the dragging sessions ends, so resume test
+ // server's run loop until it returns.
+ server_.Resume();
+ move_loop_handler->RunMoveLoop({});
+ server_.Pause();
+
+ // Continue the dragging session after "snapping" the window. At this point,
+ // the DND session is expected to be still alive and responding normally to
+ // data object events.
+ EXPECT_EQ(State::kAttached, drag_controller()->state());
+ EXPECT_EQ(kSnapped, test_step);
+
+ // Drag the pointer a bit more within |target_window| and then releases the
+ // mouse button and ensures drag controller delivers the events properly and
+ // exit gracefully.
+ SendDndMotion({30, 30});
+ SendDndMotion({30, 33});
+ SendDndMotion({30, 36});
+ SendDndMotion({30, 39});
+ SendDndMotion({30, 42});
+ EXPECT_CALL(delegate_, DispatchEvent(_)).Times(5);
+ Sync();
+
+ EXPECT_EQ(gfx::Point(30, 42), screen_->GetCursorScreenPoint());
+ EXPECT_EQ(target_window->GetWidget(),
+ screen_->GetLocalProcessWidgetAtPoint({50, 50}, {}));
+
+ SendDndDrop();
+ EXPECT_CALL(delegate_, DispatchEvent(_)).WillRepeatedly([&](Event* event) {
+ EXPECT_TRUE(event->IsMouseEvent());
+ switch (test_step) {
+ case kSnapped:
+ EXPECT_EQ(ET_MOUSE_RELEASED, event->type());
+ EXPECT_EQ(State::kDropped, drag_controller()->state());
+ test_step = kDone;
+ break;
+ case kDone:
+ EXPECT_EQ(ET_MOUSE_EXITED, event->type());
+ EXPECT_EQ(target_window->GetWidget(),
+ screen_->GetLocalProcessWidgetAtPoint({30, 42}, {}));
+ break;
+ default:
+ FAIL() << " event=" << event->GetName()
+ << " state=" << drag_controller()->state()
+ << " step=" << static_cast<int>(test_step);
+ return;
+ }
+ });
+ Sync();
+
+ SendPointerEnter(target_window, &delegate_);
+ EXPECT_EQ(target_window, window_manager()->GetCurrentFocusedWindow());
+ EXPECT_EQ(target_window->GetWidget(),
+ screen_->GetLocalProcessWidgetAtPoint({20, 20}, {}));
+}
+
+INSTANTIATE_TEST_SUITE_P(XdgVersionStableTest,
+ WaylandWindowDragControllerTest,
+ ::testing::Values(kXdgShellStable));
+
+INSTANTIATE_TEST_SUITE_P(XdgVersionV6Test,
+ WaylandWindowDragControllerTest,
+ ::testing::Values(kXdgShellV6));
+
+} // namespace ui
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window_factory.cc b/chromium/ui/ozone/platform/wayland/host/wayland_window_factory.cc
index f74df68dbff..29d70dbdc39 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_window_factory.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_factory.cc
@@ -8,7 +8,7 @@
#include "ui/ozone/platform/wayland/host/wayland_connection.h"
#include "ui/ozone/platform/wayland/host/wayland_popup.h"
#include "ui/ozone/platform/wayland/host/wayland_subsurface.h"
-#include "ui/ozone/platform/wayland/host/wayland_surface.h"
+#include "ui/ozone/platform/wayland/host/wayland_toplevel_window.h"
#include "ui/ozone/platform/wayland/host/wayland_window.h"
namespace ui {
@@ -26,7 +26,7 @@ std::unique_ptr<WaylandWindow> WaylandWindow::Create(
// parent window to be set. Thus, create a normal window instead then.
if (properties.parent_widget == gfx::kNullAcceleratedWidget &&
!connection->wayland_window_manager()->GetCurrentFocusedWindow()) {
- window.reset(new WaylandSurface(delegate, connection));
+ window.reset(new WaylandToplevelWindow(delegate, connection));
} else if (connection->IsDragInProgress()) {
// We are in the process of drag and requested a popup. Most probably,
// it is an arrow window.
@@ -43,7 +43,7 @@ std::unique_ptr<WaylandWindow> WaylandWindow::Create(
case PlatformWindowType::kDrag:
// TODO(msisov): Figure out what kind of surface we need to create for
// bubble and drag windows.
- window.reset(new WaylandSurface(delegate, connection));
+ window.reset(new WaylandToplevelWindow(delegate, connection));
break;
default:
NOTREACHED();
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window_manager.cc b/chromium/ui/ozone/platform/wayland/host/wayland_window_manager.cc
index bd7050813b2..51f07bbc27a 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_window_manager.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_manager.cc
@@ -20,6 +20,11 @@ void WaylandWindowManager::RemoveObserver(WaylandWindowObserver* observer) {
observers_.RemoveObserver(observer);
}
+void WaylandWindowManager::NotifyWindowConfigured(WaylandWindow* window) {
+ for (WaylandWindowObserver& observer : observers_)
+ observer.OnWindowConfigured(window);
+}
+
void WaylandWindowManager::GrabLocatedEvents(WaylandWindow* window) {
DCHECK_NE(located_events_grabber_, window);
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window_manager.h b/chromium/ui/ozone/platform/wayland/host/wayland_window_manager.h
index 1873c907a68..1f4cad045ca 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_window_manager.h
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_manager.h
@@ -27,6 +27,10 @@ class WaylandWindowManager {
void AddObserver(WaylandWindowObserver* observer);
void RemoveObserver(WaylandWindowObserver* observer);
+ // Notifies observers that the Window has been ack configured and
+ // WaylandBufferManagerHost can start attaching buffers to the |surface_|.
+ void NotifyWindowConfigured(WaylandWindow* window);
+
// Stores the window that should grab the located events.
void GrabLocatedEvents(WaylandWindow* event_grabber);
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window_observer.cc b/chromium/ui/ozone/platform/wayland/host/wayland_window_observer.cc
index d81f991e080..e289ea88e3d 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_window_observer.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_observer.cc
@@ -12,4 +12,6 @@ void WaylandWindowObserver::OnWindowAdded(WaylandWindow* window) {}
void WaylandWindowObserver::OnWindowRemoved(WaylandWindow* window) {}
+void WaylandWindowObserver::OnWindowConfigured(WaylandWindow* window) {}
+
} // namespace ui
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window_observer.h b/chromium/ui/ozone/platform/wayland/host/wayland_window_observer.h
index 0f81b8b5731..c461b03114e 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_window_observer.h
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_observer.h
@@ -20,6 +20,9 @@ class WaylandWindowObserver : public base::CheckedObserver {
// Called when |window| has been removed.
virtual void OnWindowRemoved(WaylandWindow* window);
+ // Called when |window| has been ack configured.
+ virtual void OnWindowConfigured(WaylandWindow* window);
+
protected:
~WaylandWindowObserver() override;
};
diff --git a/chromium/ui/ozone/platform/wayland/host/wayland_window_unittest.cc b/chromium/ui/ozone/platform/wayland/host/wayland_window_unittest.cc
index 9a1112a1bd8..d7c533edc09 100644
--- a/chromium/ui/ozone/platform/wayland/host/wayland_window_unittest.cc
+++ b/chromium/ui/ozone/platform/wayland/host/wayland_window_unittest.cc
@@ -98,25 +98,6 @@ class WaylandWindowTest : public WaylandTest {
}
protected:
- void SendConfigureEvent(int width,
- int height,
- uint32_t serial,
- struct wl_array* states) {
- // In xdg_shell_v6+, both surfaces send serial configure event and toplevel
- // surfaces send other data like states, heights and widths.
- if (GetParam() == kXdgShellV6) {
- zxdg_surface_v6_send_configure(xdg_surface_->resource(), serial);
- ASSERT_TRUE(xdg_surface_->xdg_toplevel());
- zxdg_toplevel_v6_send_configure(xdg_surface_->xdg_toplevel()->resource(),
- width, height, states);
- } else {
- xdg_surface_send_configure(xdg_surface_->resource(), serial);
- ASSERT_TRUE(xdg_surface_->xdg_toplevel());
- xdg_toplevel_send_configure(xdg_surface_->xdg_toplevel()->resource(),
- width, height, states);
- }
- }
-
void SendConfigureEventPopup(gfx::AcceleratedWidget menu_widget,
const gfx::Rect bounds) {
auto* popup = GetPopupByWidget(menu_widget);
@@ -253,12 +234,20 @@ TEST_P(WaylandWindowTest, MaximizeAndRestore) {
const auto kNormalBounds = gfx::Rect{0, 0, 500, 300};
const auto kMaximizedBounds = gfx::Rect{0, 0, 800, 600};
+ uint32_t serial = 0;
+
// Make sure the window has normal state initially.
EXPECT_CALL(delegate_, OnBoundsChanged(kNormalBounds));
window_->SetBounds(kNormalBounds);
EXPECT_EQ(PlatformWindowState::kNormal, window_->GetPlatformWindowState());
VerifyAndClearExpectations();
+ // Deactivate the surface.
+ auto empty_state = MakeStateArray({});
+ SendConfigureEvent(xdg_surface_, 0, 0, ++serial, empty_state.get());
+
+ Sync();
+
auto active_maximized = MakeStateArray(
{XDG_TOPLEVEL_STATE_ACTIVATED, XDG_TOPLEVEL_STATE_MAXIMIZED});
EXPECT_CALL(*GetXdgToplevel(), SetMaximized());
@@ -268,7 +257,8 @@ TEST_P(WaylandWindowTest, MaximizeAndRestore) {
EXPECT_CALL(delegate_, OnBoundsChanged(kMaximizedBounds));
EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(0);
window_->Maximize();
- SendConfigureEvent(kMaximizedBounds.width(), kMaximizedBounds.height(), 1,
+ SendConfigureEvent(xdg_surface_, kMaximizedBounds.width(),
+ kMaximizedBounds.height(), ++serial,
active_maximized.get());
Sync();
VerifyAndClearExpectations();
@@ -278,7 +268,8 @@ TEST_P(WaylandWindowTest, MaximizeAndRestore) {
kMaximizedBounds.height()));
EXPECT_CALL(delegate_, OnActivationChanged(Eq(false)));
EXPECT_CALL(delegate_, OnBoundsChanged(_)).Times(0);
- SendConfigureEvent(kMaximizedBounds.width(), kMaximizedBounds.height(), 2,
+ SendConfigureEvent(xdg_surface_, kMaximizedBounds.width(),
+ kMaximizedBounds.height(), ++serial,
inactive_maximized.get());
Sync();
VerifyAndClearExpectations();
@@ -287,7 +278,8 @@ TEST_P(WaylandWindowTest, MaximizeAndRestore) {
kMaximizedBounds.height()));
EXPECT_CALL(delegate_, OnActivationChanged(Eq(true)));
EXPECT_CALL(delegate_, OnBoundsChanged(_)).Times(0);
- SendConfigureEvent(kMaximizedBounds.width(), kMaximizedBounds.height(), 3,
+ SendConfigureEvent(xdg_surface_, kMaximizedBounds.width(),
+ kMaximizedBounds.height(), ++serial,
active_maximized.get());
Sync();
VerifyAndClearExpectations();
@@ -301,7 +293,7 @@ TEST_P(WaylandWindowTest, MaximizeAndRestore) {
window_->Restore();
// Reinitialize wl_array, which removes previous old states.
auto active = InitializeWlArrayWithActivatedState();
- SendConfigureEvent(0, 0, 4, active.get());
+ SendConfigureEvent(xdg_surface_, 0, 0, ++serial, active.get());
Sync();
}
@@ -310,7 +302,7 @@ TEST_P(WaylandWindowTest, Minimize) {
// Make sure the window is initialized to normal state from the beginning.
EXPECT_EQ(PlatformWindowState::kNormal, window_->GetPlatformWindowState());
- SendConfigureEvent(0, 0, 1, states.get());
+ SendConfigureEvent(xdg_surface_, 0, 0, 1, states.get());
Sync();
EXPECT_CALL(*GetXdgToplevel(), SetMinimized());
@@ -320,24 +312,24 @@ TEST_P(WaylandWindowTest, Minimize) {
// Reinitialize wl_array, which removes previous old states.
states = ScopedWlArray();
- SendConfigureEvent(0, 0, 2, states.get());
+ SendConfigureEvent(xdg_surface_, 0, 0, 2, states.get());
Sync();
// Wayland compositor doesn't notify clients about minimized state, but rather
- // if a window is not activated. Thus, a WaylandSurface marks itself as being
- // minimized and and sets state to minimized. Thus, the state mustn't change
- // after the configuration event is sent.
+ // if a window is not activated. Thus, a WaylandToplevelWindow marks itself as
+ // being minimized and and sets state to minimized. Thus, the state mustn't
+ // change after the configuration event is sent.
EXPECT_EQ(window_->GetPlatformWindowState(), PlatformWindowState::kMinimized);
// Send one additional empty configuration event (which means the surface is
// not maximized, fullscreen or activated) to ensure, WaylandWindow stays in
// the same minimized state and doesn't notify its delegate.
EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(0);
- SendConfigureEvent(0, 0, 3, states.get());
+ SendConfigureEvent(xdg_surface_, 0, 0, 3, states.get());
Sync();
// And one last time to ensure the behaviour.
- SendConfigureEvent(0, 0, 4, states.get());
+ SendConfigureEvent(xdg_surface_, 0, 0, 4, states.get());
Sync();
}
@@ -346,7 +338,7 @@ TEST_P(WaylandWindowTest, SetFullscreenAndRestore) {
EXPECT_EQ(PlatformWindowState::kNormal, window_->GetPlatformWindowState());
ScopedWlArray states = InitializeWlArrayWithActivatedState();
- SendConfigureEvent(0, 0, 1, states.get());
+ SendConfigureEvent(xdg_surface_, 0, 0, 1, states.get());
Sync();
AddStateToWlArray(XDG_TOPLEVEL_STATE_FULLSCREEN, states.get());
@@ -358,7 +350,7 @@ TEST_P(WaylandWindowTest, SetFullscreenAndRestore) {
// comment in the WaylandWindow::ToggleFullscreen.
EXPECT_EQ(window_->GetPlatformWindowState(),
PlatformWindowState::kFullScreen);
- SendConfigureEvent(0, 0, 2, states.get());
+ SendConfigureEvent(xdg_surface_, 0, 0, 2, states.get());
Sync();
EXPECT_CALL(*GetXdgToplevel(), UnsetFullscreen());
@@ -367,7 +359,7 @@ TEST_P(WaylandWindowTest, SetFullscreenAndRestore) {
EXPECT_EQ(window_->GetPlatformWindowState(), PlatformWindowState::kNormal);
// Reinitialize wl_array, which removes previous old states.
states = InitializeWlArrayWithActivatedState();
- SendConfigureEvent(0, 0, 3, states.get());
+ SendConfigureEvent(xdg_surface_, 0, 0, 3, states.get());
Sync();
EXPECT_EQ(window_->GetPlatformWindowState(), PlatformWindowState::kNormal);
}
@@ -408,7 +400,7 @@ TEST_P(WaylandWindowTest, StartWithFullscreen) {
// Activate the surface.
ScopedWlArray states = InitializeWlArrayWithActivatedState();
AddStateToWlArray(XDG_TOPLEVEL_STATE_FULLSCREEN, states.get());
- SendConfigureEvent(0, 0, 1, states.get());
+ SendConfigureEvent(xdg_surface_, 0, 0, 1, states.get());
Sync();
@@ -454,7 +446,7 @@ TEST_P(WaylandWindowTest, StartMaximized) {
// Activate the surface.
ScopedWlArray states = InitializeWlArrayWithActivatedState();
AddStateToWlArray(XDG_TOPLEVEL_STATE_MAXIMIZED, states.get());
- SendConfigureEvent(0, 0, 1, states.get());
+ SendConfigureEvent(xdg_surface_, 0, 0, 1, states.get());
Sync();
@@ -467,7 +459,7 @@ TEST_P(WaylandWindowTest, CompositorSideStateChanges) {
ScopedWlArray states = InitializeWlArrayWithActivatedState();
AddStateToWlArray(XDG_TOPLEVEL_STATE_MAXIMIZED, states.get());
- SendConfigureEvent(2000, 2000, 1, states.get());
+ SendConfigureEvent(xdg_surface_, 2000, 2000, 1, states.get());
EXPECT_CALL(delegate_,
OnWindowStateChanged(Eq(PlatformWindowState::kMaximized)))
@@ -480,7 +472,7 @@ TEST_P(WaylandWindowTest, CompositorSideStateChanges) {
// Unmaximize
states = InitializeWlArrayWithActivatedState();
- SendConfigureEvent(0, 0, 2, states.get());
+ SendConfigureEvent(xdg_surface_, 0, 0, 2, states.get());
EXPECT_CALL(delegate_, OnWindowStateChanged(Eq(PlatformWindowState::kNormal)))
.Times(1);
@@ -489,7 +481,7 @@ TEST_P(WaylandWindowTest, CompositorSideStateChanges) {
// Now, set to fullscreen.
AddStateToWlArray(XDG_TOPLEVEL_STATE_FULLSCREEN, states.get());
- SendConfigureEvent(2005, 2005, 3, states.get());
+ SendConfigureEvent(xdg_surface_, 2005, 2005, 3, states.get());
EXPECT_CALL(delegate_,
OnWindowStateChanged(Eq(PlatformWindowState::kFullScreen)))
.Times(1);
@@ -499,7 +491,7 @@ TEST_P(WaylandWindowTest, CompositorSideStateChanges) {
// Unfullscreen
states = InitializeWlArrayWithActivatedState();
- SendConfigureEvent(0, 0, 4, states.get());
+ SendConfigureEvent(xdg_surface_, 0, 0, 4, states.get());
EXPECT_CALL(delegate_, OnWindowStateChanged(Eq(PlatformWindowState::kNormal)))
.Times(1);
@@ -511,7 +503,7 @@ TEST_P(WaylandWindowTest, CompositorSideStateChanges) {
// Now, maximize, fullscreen and restore.
states = InitializeWlArrayWithActivatedState();
AddStateToWlArray(XDG_TOPLEVEL_STATE_MAXIMIZED, states.get());
- SendConfigureEvent(2000, 2000, 1, states.get());
+ SendConfigureEvent(xdg_surface_, 2000, 2000, 1, states.get());
EXPECT_CALL(delegate_,
OnWindowStateChanged(Eq(PlatformWindowState::kMaximized)))
@@ -521,7 +513,7 @@ TEST_P(WaylandWindowTest, CompositorSideStateChanges) {
Sync();
AddStateToWlArray(XDG_TOPLEVEL_STATE_FULLSCREEN, states.get());
- SendConfigureEvent(2005, 2005, 1, states.get());
+ SendConfigureEvent(xdg_surface_, 2005, 2005, 1, states.get());
EXPECT_CALL(delegate_,
OnWindowStateChanged(Eq(PlatformWindowState::kFullScreen)))
@@ -530,7 +522,7 @@ TEST_P(WaylandWindowTest, CompositorSideStateChanges) {
// Restore
states = InitializeWlArrayWithActivatedState();
- SendConfigureEvent(0, 0, 4, states.get());
+ SendConfigureEvent(xdg_surface_, 0, 0, 4, states.get());
EXPECT_CALL(delegate_, OnWindowStateChanged(Eq(PlatformWindowState::kNormal)))
.Times(1);
@@ -544,12 +536,19 @@ TEST_P(WaylandWindowTest, SetMaximizedFullscreenAndRestore) {
const auto kNormalBounds = gfx::Rect{0, 0, 500, 300};
const auto kMaximizedBounds = gfx::Rect{0, 0, 800, 600};
+ uint32_t serial = 0;
+
// Make sure the window has normal state initially.
EXPECT_CALL(delegate_, OnBoundsChanged(kNormalBounds));
window_->SetBounds(kNormalBounds);
EXPECT_EQ(PlatformWindowState::kNormal, window_->GetPlatformWindowState());
VerifyAndClearExpectations();
+ // Deactivate the surface.
+ ScopedWlArray empty_state;
+ SendConfigureEvent(xdg_surface_, 0, 0, ++serial, empty_state.get());
+ Sync();
+
auto active_maximized = MakeStateArray(
{XDG_TOPLEVEL_STATE_ACTIVATED, XDG_TOPLEVEL_STATE_MAXIMIZED});
EXPECT_CALL(*GetXdgToplevel(), SetMaximized());
@@ -561,7 +560,8 @@ TEST_P(WaylandWindowTest, SetMaximizedFullscreenAndRestore) {
window_->Maximize();
// State changes are synchronous.
EXPECT_EQ(PlatformWindowState::kMaximized, window_->GetPlatformWindowState());
- SendConfigureEvent(kMaximizedBounds.width(), kMaximizedBounds.height(), 2,
+ SendConfigureEvent(xdg_surface_, kMaximizedBounds.width(),
+ kMaximizedBounds.height(), ++serial,
active_maximized.get());
Sync();
// Verify that the state has not been changed.
@@ -578,7 +578,8 @@ TEST_P(WaylandWindowTest, SetMaximizedFullscreenAndRestore) {
EXPECT_EQ(PlatformWindowState::kFullScreen,
window_->GetPlatformWindowState());
AddStateToWlArray(XDG_TOPLEVEL_STATE_FULLSCREEN, active_maximized.get());
- SendConfigureEvent(kMaximizedBounds.width(), kMaximizedBounds.height(), 3,
+ SendConfigureEvent(xdg_surface_, kMaximizedBounds.width(),
+ kMaximizedBounds.height(), ++serial,
active_maximized.get());
Sync();
// Verify that the state has not been changed.
@@ -596,7 +597,7 @@ TEST_P(WaylandWindowTest, SetMaximizedFullscreenAndRestore) {
EXPECT_EQ(PlatformWindowState::kNormal, window_->GetPlatformWindowState());
// Reinitialize wl_array, which removes previous old states.
auto active = InitializeWlArrayWithActivatedState();
- SendConfigureEvent(0, 0, 4, active.get());
+ SendConfigureEvent(xdg_surface_, 0, 0, ++serial, active.get());
Sync();
EXPECT_EQ(PlatformWindowState::kNormal, window_->GetPlatformWindowState());
}
@@ -614,8 +615,8 @@ TEST_P(WaylandWindowTest, RestoreBoundsAfterMaximize) {
EXPECT_CALL(delegate_, OnBoundsChanged(Eq(maximized_bounds)));
window_->Maximize();
AddStateToWlArray(XDG_TOPLEVEL_STATE_MAXIMIZED, states.get());
- SendConfigureEvent(maximized_bounds.width(), maximized_bounds.height(), 1,
- states.get());
+ SendConfigureEvent(xdg_surface_, maximized_bounds.width(),
+ maximized_bounds.height(), 1, states.get());
Sync();
restored_bounds = window_->GetRestoredBoundsInPixels();
EXPECT_EQ(bounds, restored_bounds);
@@ -629,7 +630,7 @@ TEST_P(WaylandWindowTest, RestoreBoundsAfterMaximize) {
window_->Restore();
// Reinitialize wl_array, which removes previous old states.
states = InitializeWlArrayWithActivatedState();
- SendConfigureEvent(0, 0, 2, states.get());
+ SendConfigureEvent(xdg_surface_, 0, 0, 2, states.get());
Sync();
bounds = window_->GetBounds();
EXPECT_EQ(bounds, restored_bounds);
@@ -641,7 +642,7 @@ TEST_P(WaylandWindowTest, RestoreBoundsAfterFullscreen) {
const gfx::Rect current_bounds = window_->GetBounds();
ScopedWlArray states = InitializeWlArrayWithActivatedState();
- SendConfigureEvent(0, 0, 1, states.get());
+ SendConfigureEvent(xdg_surface_, 0, 0, 1, states.get());
Sync();
gfx::Rect restored_bounds = window_->GetRestoredBoundsInPixels();
@@ -652,8 +653,8 @@ TEST_P(WaylandWindowTest, RestoreBoundsAfterFullscreen) {
EXPECT_CALL(delegate_, OnBoundsChanged(Eq(fullscreen_bounds)));
window_->ToggleFullscreen();
AddStateToWlArray(XDG_TOPLEVEL_STATE_FULLSCREEN, states.get());
- SendConfigureEvent(fullscreen_bounds.width(), fullscreen_bounds.height(), 2,
- states.get());
+ SendConfigureEvent(xdg_surface_, fullscreen_bounds.width(),
+ fullscreen_bounds.height(), 2, states.get());
Sync();
restored_bounds = window_->GetRestoredBoundsInPixels();
EXPECT_EQ(bounds, restored_bounds);
@@ -667,7 +668,7 @@ TEST_P(WaylandWindowTest, RestoreBoundsAfterFullscreen) {
window_->Restore();
// Reinitialize wl_array, which removes previous old states.
states = InitializeWlArrayWithActivatedState();
- SendConfigureEvent(0, 0, 3, states.get());
+ SendConfigureEvent(xdg_surface_, 0, 0, 3, states.get());
Sync();
bounds = window_->GetBounds();
EXPECT_EQ(bounds, restored_bounds);
@@ -688,8 +689,8 @@ TEST_P(WaylandWindowTest, RestoreBoundsAfterMaximizeAndFullscreen) {
EXPECT_CALL(delegate_, OnBoundsChanged(Eq(maximized_bounds)));
window_->Maximize();
AddStateToWlArray(XDG_TOPLEVEL_STATE_MAXIMIZED, states.get());
- SendConfigureEvent(maximized_bounds.width(), maximized_bounds.height(), 1,
- states.get());
+ SendConfigureEvent(xdg_surface_, maximized_bounds.width(),
+ maximized_bounds.height(), 1, states.get());
Sync();
restored_bounds = window_->GetRestoredBoundsInPixels();
EXPECT_EQ(bounds, restored_bounds);
@@ -698,8 +699,8 @@ TEST_P(WaylandWindowTest, RestoreBoundsAfterMaximizeAndFullscreen) {
EXPECT_CALL(delegate_, OnBoundsChanged(Eq(fullscreen_bounds)));
window_->ToggleFullscreen();
AddStateToWlArray(XDG_TOPLEVEL_STATE_FULLSCREEN, states.get());
- SendConfigureEvent(fullscreen_bounds.width(), fullscreen_bounds.height(), 2,
- states.get());
+ SendConfigureEvent(xdg_surface_, fullscreen_bounds.width(),
+ fullscreen_bounds.height(), 2, states.get());
Sync();
gfx::Rect fullscreen_restore_bounds = window_->GetRestoredBoundsInPixels();
EXPECT_EQ(restored_bounds, fullscreen_restore_bounds);
@@ -709,8 +710,8 @@ TEST_P(WaylandWindowTest, RestoreBoundsAfterMaximizeAndFullscreen) {
// Reinitialize wl_array, which removes previous old states.
states = InitializeWlArrayWithActivatedState();
AddStateToWlArray(XDG_TOPLEVEL_STATE_MAXIMIZED, states.get());
- SendConfigureEvent(maximized_bounds.width(), maximized_bounds.height(), 3,
- states.get());
+ SendConfigureEvent(xdg_surface_, maximized_bounds.width(),
+ maximized_bounds.height(), 3, states.get());
Sync();
restored_bounds = window_->GetRestoredBoundsInPixels();
EXPECT_EQ(restored_bounds, fullscreen_restore_bounds);
@@ -724,7 +725,7 @@ TEST_P(WaylandWindowTest, RestoreBoundsAfterMaximizeAndFullscreen) {
window_->Restore();
// Reinitialize wl_array, which removes previous old states.
states = InitializeWlArrayWithActivatedState();
- SendConfigureEvent(0, 0, 4, states.get());
+ SendConfigureEvent(xdg_surface_, 0, 0, 4, states.get());
Sync();
bounds = window_->GetBounds();
EXPECT_EQ(bounds, restored_bounds);
@@ -747,7 +748,7 @@ TEST_P(WaylandWindowTest, SendsBoundsOnRequest) {
EXPECT_CALL(*xdg_surface_,
SetWindowGeometry(0, 0, new_bounds.width(), new_bounds.height()))
.Times(2);
- SendConfigureEvent(0, 0, 2, states.get());
+ SendConfigureEvent(xdg_surface_, 0, 0, 2, states.get());
Sync();
// Restored bounds should keep empty value.
@@ -757,7 +758,7 @@ TEST_P(WaylandWindowTest, SendsBoundsOnRequest) {
// Second case is when Wayland sends a configure event with 1, 1 height and
// width. It looks more like a bug in Gnome Shell with Wayland as long as the
// documentation says it must be set to 0, 0, when wayland requests bounds.
- SendConfigureEvent(0, 0, 3, states.get());
+ SendConfigureEvent(xdg_surface_, 0, 0, 3, states.get());
Sync();
// Restored bounds should keep empty value.
@@ -818,14 +819,14 @@ TEST_P(WaylandWindowTest, ConfigureEvent) {
// xdg_toplevel in xdg_shell_v6 and by xdg_surface_ in xdg_shell_v5.
EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, 1000, 1000)).Times(1);
EXPECT_CALL(*xdg_surface_, AckConfigure(12));
- SendConfigureEvent(1000, 1000, 12, states.get());
+ SendConfigureEvent(xdg_surface_, 1000, 1000, 12, states.get());
Sync();
EXPECT_CALL(delegate_, OnBoundsChanged(Eq(gfx::Rect(0, 0, 1500, 1000))));
EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, 1500, 1000)).Times(1);
EXPECT_CALL(*xdg_surface_, AckConfigure(13));
- SendConfigureEvent(1500, 1000, 13, states.get());
+ SendConfigureEvent(xdg_surface_, 1500, 1000, 13, states.get());
}
TEST_P(WaylandWindowTest, ConfigureEventWithNulledSize) {
@@ -834,7 +835,7 @@ TEST_P(WaylandWindowTest, ConfigureEventWithNulledSize) {
// If Wayland sends configure event with 0 width and 0 size, client should
// call back with desired sizes. In this case, that's the actual size of
// the window.
- SendConfigureEvent(0, 0, 14, states.get());
+ SendConfigureEvent(xdg_surface_, 0, 0, 14, states.get());
// |xdg_surface_| must receive the following calls in both xdg_shell_v5 and
// xdg_shell_v6. Other calls like SetTitle or SetMaximized are recieved by
// xdg_toplevel in xdg_shell_v6 and by xdg_surface_ in xdg_shell_v5.
@@ -843,16 +844,23 @@ TEST_P(WaylandWindowTest, ConfigureEventWithNulledSize) {
}
TEST_P(WaylandWindowTest, OnActivationChanged) {
+ uint32_t serial = 0;
+
+ // Deactivate the surface.
+ ScopedWlArray empty_state;
+ SendConfigureEvent(xdg_surface_, 0, 0, ++serial, empty_state.get());
+ Sync();
+
{
ScopedWlArray states = InitializeWlArrayWithActivatedState();
EXPECT_CALL(delegate_, OnActivationChanged(Eq(true)));
- SendConfigureEvent(0, 0, 1, states.get());
+ SendConfigureEvent(xdg_surface_, 0, 0, ++serial, states.get());
Sync();
}
ScopedWlArray states;
EXPECT_CALL(delegate_, OnActivationChanged(Eq(false)));
- SendConfigureEvent(0, 0, 2, states.get());
+ SendConfigureEvent(xdg_surface_, 0, 0, ++serial, states.get());
Sync();
}
@@ -1275,7 +1283,8 @@ TEST_P(WaylandWindowTest, CanDispatchEvent) {
TEST_P(WaylandWindowTest, DispatchWindowMove) {
EXPECT_CALL(*GetXdgToplevel(), Move(_));
- ui::GetWmMoveResizeHandler(*window_)->DispatchHostWindowDragMovement(HTCAPTION, gfx::Point());
+ ui::GetWmMoveResizeHandler(*window_)->DispatchHostWindowDragMovement(
+ HTCAPTION, gfx::Point());
}
// Makes sure hit tests are converted into right edges.
@@ -1482,7 +1491,7 @@ TEST_P(WaylandWindowTest, SetOpaqueRegion) {
gfx::Rect new_bounds(0, 0, 500, 600);
auto state_array = MakeStateArray({XDG_TOPLEVEL_STATE_ACTIVATED});
- SendConfigureEvent(new_bounds.width(), new_bounds.height(), 1,
+ SendConfigureEvent(xdg_surface_, new_bounds.width(), new_bounds.height(), 1,
state_array.get());
SkIRect rect =
@@ -1494,7 +1503,7 @@ TEST_P(WaylandWindowTest, SetOpaqueRegion) {
VerifyAndClearExpectations();
new_bounds.set_size(gfx::Size(1000, 534));
- SendConfigureEvent(new_bounds.width(), new_bounds.height(), 2,
+ SendConfigureEvent(xdg_surface_, new_bounds.width(), new_bounds.height(), 2,
state_array.get());
rect = SkIRect::MakeXYWH(0, 0, new_bounds.width(), new_bounds.height());
@@ -1922,8 +1931,8 @@ TEST_P(WaylandWindowTest, SetsPropertiesOnShow) {
// We can't mock all those methods above as long as the xdg_toplevel is
// created and destroyed on each show and hide call. However, it is the same
- // WaylandSurface object that cached the values we set and must restore them
- // on Show().
+ // WaylandToplevelWindow object that cached the values we set and must restore
+ // them on Show().
EXPECT_EQ(mock_xdg_toplevel->min_size(), min_size.value());
EXPECT_EQ(mock_xdg_toplevel->max_size(), max_size.value());
EXPECT_EQ(std::string(kAppId), mock_xdg_toplevel->app_id());
@@ -2018,6 +2027,59 @@ TEST_P(WaylandWindowTest, CreatesPopupOnTouchDownSerial) {
EXPECT_EQ(test_popup->grab_serial(), touch_down_serial);
}
+// Tests nested menu windows get the topmost window in the stack of windows
+// within the same family/tree.
+TEST_P(WaylandWindowTest, NestedPopupWindowsGetCorrectParent) {
+ VerifyAndClearExpectations();
+
+ gfx::Rect menu_window_bounds(gfx::Rect(10, 20, 20, 20));
+ std::unique_ptr<WaylandWindow> menu_window = CreateWaylandWindowWithParams(
+ PlatformWindowType::kMenu, window_->GetWidget(), menu_window_bounds,
+ &delegate_);
+ EXPECT_TRUE(menu_window);
+
+ EXPECT_TRUE(menu_window->parent_window() == window_.get());
+
+ gfx::Rect menu_window_bounds2(gfx::Rect(20, 40, 30, 20));
+ std::unique_ptr<WaylandWindow> menu_window2 = CreateWaylandWindowWithParams(
+ PlatformWindowType::kMenu, window_->GetWidget(), menu_window_bounds2,
+ &delegate_);
+ EXPECT_TRUE(menu_window2);
+
+ EXPECT_TRUE(menu_window2->parent_window() == menu_window.get());
+
+ gfx::Rect menu_window_bounds3(gfx::Rect(30, 40, 30, 20));
+ std::unique_ptr<WaylandWindow> menu_window3 = CreateWaylandWindowWithParams(
+ PlatformWindowType::kMenu, window_->GetWidget(), menu_window_bounds3,
+ &delegate_);
+ EXPECT_TRUE(menu_window3);
+
+ EXPECT_TRUE(menu_window3->parent_window() == menu_window2.get());
+
+ gfx::Rect menu_window_bounds4(gfx::Rect(40, 40, 30, 20));
+ std::unique_ptr<WaylandWindow> menu_window4 = CreateWaylandWindowWithParams(
+ PlatformWindowType::kMenu, window_->GetWidget(), menu_window_bounds4,
+ &delegate_);
+ EXPECT_TRUE(menu_window4);
+
+ EXPECT_TRUE(menu_window4->parent_window() == menu_window3.get());
+}
+
+TEST_P(WaylandWindowTest, DoesNotGrabPopupIfNoSeat) {
+ // Create a popup window and verify the grab serial is not set.
+ MockPlatformWindowDelegate delegate;
+ auto popup = CreateWaylandWindowWithParams(
+ PlatformWindowType::kMenu, window_->GetWidget(), gfx::Rect(0, 0, 50, 50),
+ &delegate);
+ ASSERT_TRUE(popup);
+
+ Sync();
+
+ auto* test_popup = GetPopupByWidget(popup->GetWidget());
+ ASSERT_TRUE(test_popup);
+ EXPECT_EQ(test_popup->grab_serial(), 0u);
+}
+
INSTANTIATE_TEST_SUITE_P(XdgVersionStableTest,
WaylandWindowTest,
::testing::Values(kXdgShellStable));
diff --git a/chromium/ui/ozone/platform/wayland/host/xdg_popup_wrapper_impl.cc b/chromium/ui/ozone/platform/wayland/host/xdg_popup_wrapper_impl.cc
index 1e81f70b003..758ae1f222e 100644
--- a/chromium/ui/ozone/platform/wayland/host/xdg_popup_wrapper_impl.cc
+++ b/chromium/ui/ozone/platform/wayland/host/xdg_popup_wrapper_impl.cc
@@ -18,7 +18,7 @@
#include "ui/ozone/platform/wayland/host/wayland_event_source.h"
#include "ui/ozone/platform/wayland/host/wayland_pointer.h"
#include "ui/ozone/platform/wayland/host/wayland_popup.h"
-#include "ui/ozone/platform/wayland/host/wayland_surface.h"
+#include "ui/ozone/platform/wayland/host/wayland_toplevel_window.h"
#include "ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.h"
namespace ui {
@@ -286,8 +286,8 @@ bool XDGPopupWrapperImpl::Initialize(WaylandConnection* connection,
static_cast<XDGPopupWrapperImpl*>(wayland_popup->shell_popup());
parent_xdg_surface = popup->xdg_surface();
} else {
- WaylandSurface* wayland_surface =
- static_cast<WaylandSurface*>(wayland_window_->parent_window());
+ WaylandToplevelWindow* wayland_surface =
+ static_cast<WaylandToplevelWindow*>(wayland_window_->parent_window());
parent_xdg_surface =
static_cast<XDGSurfaceWrapperImpl*>(wayland_surface->shell_surface());
}
@@ -324,41 +324,8 @@ bool XDGPopupWrapperImpl::InitializeStable(
xdg_positioner_destroy(positioner);
- // According to the spec, the grab call can only be done on a key press, mouse
- // press or touch down. However, there is a problem with popup windows and
- // touch events so long as Chromium creates them only on consequent touch up
- // events. That results in Wayland compositors dismissing popups. To fix
- // the issue, do not use grab with touch events. Please note that explicit
- // grab means that a Wayland compositor dismisses a popup whenever the user
- // clicks outside the created surfaces. If the explicit grab is not used, the
- // popups are not dismissed in such cases. What is more, current non-ozone X11
- // implementation does the same. This means there is no functionality changes
- // and we do things right.
- //
- // We cannot know what was the last event. Instead, we can check if the window
- // has pointer or keyboard focus. If so, the popup will be explicitly grabbed.
- //
- // There is a bug in the gnome/mutter - if explicit grab is not used,
- // unmapping of a wl_surface (aka destroying xdg_popup and surface) to hide a
- // window results in weird behaviour. That is, a popup continues to be visible
- // on a display and it results in a crash of the entire session. Thus, just
- // continue to use grab here and avoid showing popups for touch events on
- // gnome/mutter. That is better than crashing the entire system. Otherwise,
- // Chromium has to change the way how it reacts on touch events - instead of
- // creating a menu on touch up, it must do it on touch down events.
- // https://gitlab.gnome.org/GNOME/mutter/issues/698#note_562601.
- std::unique_ptr<base::Environment> env(base::Environment::Create());
- if ((base::nix::GetDesktopEnvironment(env.get()) ==
- base::nix::DESKTOP_ENVIRONMENT_GNOME) ||
- (wayland_window_->parent_window()->has_pointer_focus() ||
- wayland_window_->parent_window()->has_keyboard_focus())) {
- // When drag process starts, as described the protocol -
- // https://goo.gl/1Mskq3, the client must have an active implicit grab. If
- // we try to create a popup and grab it, it will be immediately dismissed.
- // Thus, do not take explicit grab during drag process.
- if (!connection->IsDragInProgress())
- xdg_popup_grab(xdg_popup_.get(), connection->seat(),
- connection->serial());
+ if (CanGrabPopup(connection)) {
+ xdg_popup_grab(xdg_popup_.get(), connection->seat(), connection->serial());
}
xdg_popup_add_listener(xdg_popup_.get(), &xdg_popup_listener, this);
@@ -375,18 +342,11 @@ struct xdg_positioner* XDGPopupWrapperImpl::CreatePositionerStable(
if (!positioner)
return nullptr;
- bool is_right_click_menu =
- connection->event_source()->IsPointerButtonPressed(EF_RIGHT_MOUSE_BUTTON);
+ auto menu_type = GetMenuTypeForPositioner(connection, parent_window);
- // Different types of menu require different anchors, constraint adjustments,
- // gravity and etc.
- MenuType menu_type = MenuType::TYPE_UNKNOWN;
- if (is_right_click_menu)
- menu_type = MenuType::TYPE_RIGHT_CLICK;
- else if (wl::IsMenuType(parent_window->type()))
- menu_type = MenuType::TYPE_3DOT_CHILD_MENU;
- else
- menu_type = MenuType::TYPE_3DOT_PARENT_MENU;
+ // The parent we got must be the topmost in the stack of the same family
+ // windows.
+ DCHECK_EQ(parent_window->GetTopMostChildWindow(), parent_window);
// Place anchor to the end of the possible position.
gfx::Rect anchor_rect = GetAnchorRect(
@@ -426,34 +386,7 @@ bool XDGPopupWrapperImpl::InitializeV6(
zxdg_positioner_v6_destroy(positioner);
- // According to the spec, the grab call can only be done on a key press, mouse
- // press or touch down. However, there is a problem with popup windows and
- // touch events so long as Chromium creates them only on consequent touch up
- // events. That results in Wayland compositors dismissing popups. To fix
- // the issue, do not use grab with touch events. Please note that explicit
- // grab means that a Wayland compositor dismisses a popup whenever the user
- // clicks outside the created surfaces. If the explicit grab is not used, the
- // popups are not dismissed in such cases. What is more, current non-ozone X11
- // implementation does the same. This means there is no functionality changes
- // and we do things right.
- //
- // We cannot know what was the last event. Instead, we can check if the window
- // has pointer or keyboard focus. If so, the popup will be explicitly grabbed.
- //
- // There is a bug in the gnome/mutter - if explicit grab is not used,
- // unmapping of a wl_surface (aka destroying xdg_popup and surface) to hide a
- // window results in weird behaviour. That is, a popup continues to be visible
- // on a display and it results in a crash of the entire session. Thus, just
- // continue to use grab here and avoid showing popups for touch events on
- // gnome/mutter. That is better than crashing the entire system. Otherwise,
- // Chromium has to change the way how it reacts on touch events - instead of
- // creating a menu on touch up, it must do it on touch down events.
- // https://gitlab.gnome.org/GNOME/mutter/issues/698#note_562601.
- std::unique_ptr<base::Environment> env(base::Environment::Create());
- if ((base::nix::GetDesktopEnvironment(env.get()) ==
- base::nix::DESKTOP_ENVIRONMENT_GNOME) ||
- (wayland_window_->parent_window()->has_pointer_focus() ||
- wayland_window_->parent_window()->has_keyboard_focus())) {
+ if (CanGrabPopup(connection)) {
zxdg_popup_v6_grab(zxdg_popup_v6_.get(), connection->seat(),
connection->serial());
}
@@ -473,18 +406,11 @@ zxdg_positioner_v6* XDGPopupWrapperImpl::CreatePositionerV6(
if (!positioner)
return nullptr;
- bool is_right_click_menu =
- connection->event_source()->IsPointerButtonPressed(EF_RIGHT_MOUSE_BUTTON);
+ auto menu_type = GetMenuTypeForPositioner(connection, parent_window);
- // Different types of menu require different anchors, constraint adjustments,
- // gravity and etc.
- MenuType menu_type = MenuType::TYPE_UNKNOWN;
- if (is_right_click_menu)
- menu_type = MenuType::TYPE_RIGHT_CLICK;
- else if (wl::IsMenuType(parent_window->type()))
- menu_type = MenuType::TYPE_3DOT_CHILD_MENU;
- else
- menu_type = MenuType::TYPE_3DOT_PARENT_MENU;
+ // The parent we got must be the topmost in the stack of the same family
+ // windows.
+ DCHECK_EQ(parent_window->GetTopMostChildWindow(), parent_window);
// Place anchor to the end of the possible position.
gfx::Rect anchor_rect = GetAnchorRect(
@@ -505,6 +431,61 @@ zxdg_positioner_v6* XDGPopupWrapperImpl::CreatePositionerV6(
return positioner;
}
+MenuType XDGPopupWrapperImpl::GetMenuTypeForPositioner(
+ WaylandConnection* connection,
+ WaylandWindow* parent_window) const {
+ bool is_right_click_menu =
+ connection->event_source()->last_pointer_button_pressed() &
+ EF_RIGHT_MOUSE_BUTTON;
+
+ // Different types of menu require different anchors, constraint adjustments,
+ // gravity and etc.
+ if (is_right_click_menu)
+ return MenuType::TYPE_RIGHT_CLICK;
+ else if (!wl::IsMenuType(parent_window->type()))
+ return MenuType::TYPE_3DOT_PARENT_MENU;
+ else
+ return MenuType::TYPE_3DOT_CHILD_MENU;
+}
+
+bool XDGPopupWrapperImpl::CanGrabPopup(WaylandConnection* connection) const {
+ // When drag process starts, as described the protocol -
+ // https://goo.gl/1Mskq3, the client must have an active implicit grab. If
+ // we try to create a popup and grab it, it will be immediately dismissed.
+ // Thus, do not take explicit grab during drag process.
+ if (connection->IsDragInProgress() || !connection->seat())
+ return false;
+
+ // According to the spec, the grab call can only be done on a key press, mouse
+ // press or touch down. However, there is a problem with popup windows and
+ // touch events so long as Chromium creates them only on consequent touch up
+ // events. That results in Wayland compositors dismissing popups. To fix the
+ // issue, do not use grab with touch events. Please note that explicit grab
+ // means that a Wayland compositor dismisses a popup whenever the user clicks
+ // outside the created surfaces. If the explicit grab is not used, the popups
+ // are not dismissed in such cases. What is more, current non-ozone X11
+ // implementation does the same. This means there is no functionality changes
+ // and we do things right.
+ //
+ // We cannot know what was the last event. Instead, we can check if the window
+ // has pointer or keyboard focus. If so, the popup will be explicitly grabbed.
+ //
+ // There is a bug in the gnome/mutter - if explicit grab is not used,
+ // unmapping of a wl_surface (aka destroying xdg_popup and surface) to hide a
+ // window results in weird behaviour. That is, a popup continues to be visible
+ // on a display and it results in a crash of the entire session. Thus, just
+ // continue to use grab here and avoid showing popups for touch events on
+ // gnome/mutter. That is better than crashing the entire system. Otherwise,
+ // Chromium has to change the way how it reacts on touch events - instead of
+ // creating a menu on touch up, it must do it on touch down events.
+ // https://gitlab.gnome.org/GNOME/mutter/issues/698#note_562601.
+ std::unique_ptr<base::Environment> env(base::Environment::Create());
+ return (base::nix::GetDesktopEnvironment(env.get()) ==
+ base::nix::DESKTOP_ENVIRONMENT_GNOME) ||
+ (wayland_window_->parent_window()->has_pointer_focus() ||
+ wayland_window_->parent_window()->has_keyboard_focus());
+}
+
void XDGPopupWrapperImpl::Configure(void* data,
int32_t x,
int32_t y,
diff --git a/chromium/ui/ozone/platform/wayland/host/xdg_popup_wrapper_impl.h b/chromium/ui/ozone/platform/wayland/host/xdg_popup_wrapper_impl.h
index c0479cfff7f..62f38a2dc6a 100644
--- a/chromium/ui/ozone/platform/wayland/host/xdg_popup_wrapper_impl.h
+++ b/chromium/ui/ozone/platform/wayland/host/xdg_popup_wrapper_impl.h
@@ -7,6 +7,7 @@
#include <memory>
+#include "base/macros.h"
#include "ui/ozone/platform/wayland/host/shell_popup_wrapper.h"
namespace ui {
@@ -41,6 +42,11 @@ class XDGPopupWrapperImpl : public ShellPopupWrapper {
WaylandWindow* parent_window,
const gfx::Rect& bounds);
+ MenuType GetMenuTypeForPositioner(WaylandConnection* connection,
+ WaylandWindow* parent_window) const;
+
+ bool CanGrabPopup(WaylandConnection* connection) const;
+
// xdg_popup_listener
static void Configure(void* data,
int32_t x,
diff --git a/chromium/ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.cc b/chromium/ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.cc
index e7df772ae47..ae1ca159fd4 100644
--- a/chromium/ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.cc
+++ b/chromium/ui/ozone/platform/wayland/host/xdg_surface_wrapper_impl.cc
@@ -119,6 +119,8 @@ void XDGSurfaceWrapperImpl::AckConfigure() {
zxdg_surface_v6_ack_configure(zxdg_surface_v6_.get(),
pending_configure_serial_);
}
+ connection_->wayland_window_manager()->NotifyWindowConfigured(
+ wayland_window_);
}
void XDGSurfaceWrapperImpl::SetWindowGeometry(const gfx::Rect& bounds) {
diff --git a/chromium/ui/ozone/platform/wayland/ozone_platform_wayland.cc b/chromium/ui/ozone/platform/wayland/ozone_platform_wayland.cc
index 8ffb52c5213..894166db3f9 100644
--- a/chromium/ui/ozone/platform/wayland/ozone_platform_wayland.cc
+++ b/chromium/ui/ozone/platform/wayland/ozone_platform_wayland.cc
@@ -14,6 +14,7 @@
#include "base/message_loop/message_pump_type.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "ui/base/buildflags.h"
+#include "ui/base/cursor/cursor_factory.h"
#include "ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h"
#include "ui/base/ime/linux/input_method_auralinux.h"
#include "ui/events/ozone/layout/keyboard_layout_engine_manager.h"
@@ -98,9 +99,7 @@ class OzonePlatformWayland : public OzonePlatform {
return overlay_manager_.get();
}
- CursorFactoryOzone* GetCursorFactoryOzone() override {
- return cursor_factory_.get();
- }
+ CursorFactory* GetCursorFactory() override { return cursor_factory_.get(); }
InputController* GetInputController() override {
return input_controller_.get();
@@ -249,7 +248,7 @@ class OzonePlatformWayland : public OzonePlatform {
std::unique_ptr<KeyboardLayoutEngine> keyboard_layout_engine_;
std::unique_ptr<WaylandConnection> connection_;
std::unique_ptr<WaylandSurfaceFactory> surface_factory_;
- std::unique_ptr<BitmapCursorFactoryOzone> cursor_factory_;
+ std::unique_ptr<CursorFactory> cursor_factory_;
std::unique_ptr<StubOverlayManager> overlay_manager_;
std::unique_ptr<InputController> input_controller_;
std::unique_ptr<GpuPlatformSupportHost> gpu_platform_support_host_;
diff --git a/chromium/ui/ozone/platform/wayland/test/mock_wp_presentation.cc b/chromium/ui/ozone/platform/wayland/test/mock_wp_presentation.cc
index d24c13658aa..cd87cb8aafc 100644
--- a/chromium/ui/ozone/platform/wayland/test/mock_wp_presentation.cc
+++ b/chromium/ui/ozone/platform/wayland/test/mock_wp_presentation.cc
@@ -36,6 +36,12 @@ MockWpPresentation::MockWpPresentation()
MockWpPresentation::~MockWpPresentation() {}
+wl_resource* MockWpPresentation::ReleasePresentationCallback() {
+ auto* presentation_callback = presentation_callback_;
+ presentation_callback_ = nullptr;
+ return presentation_callback;
+}
+
void MockWpPresentation::SendPresentationCallback() {
if (!presentation_callback_)
return;
@@ -50,4 +56,14 @@ void MockWpPresentation::SendPresentationCallback() {
presentation_callback_ = nullptr;
}
+void MockWpPresentation::SendPresentationCallbackDiscarded() {
+ if (!presentation_callback_)
+ return;
+
+ wp_presentation_feedback_send_discarded(presentation_callback_);
+ wl_client_flush(wl_resource_get_client(presentation_callback_));
+ wl_resource_destroy(presentation_callback_);
+ presentation_callback_ = nullptr;
+}
+
} // namespace wl
diff --git a/chromium/ui/ozone/platform/wayland/test/mock_wp_presentation.h b/chromium/ui/ozone/platform/wayland/test/mock_wp_presentation.h
index 616cd6e3fe1..64d09ab4f8e 100644
--- a/chromium/ui/ozone/platform/wayland/test/mock_wp_presentation.h
+++ b/chromium/ui/ozone/platform/wayland/test/mock_wp_presentation.h
@@ -7,7 +7,7 @@
#include <presentation-time-server-protocol.h>
-#include "base/logging.h"
+#include "base/check.h"
#include "base/macros.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "ui/ozone/platform/wayland/test/global_object.h"
@@ -34,7 +34,10 @@ class MockWpPresentation : public GlobalObject {
presentation_callback_ = callback_resource;
}
+ wl_resource* ReleasePresentationCallback();
+
void SendPresentationCallback();
+ void SendPresentationCallbackDiscarded();
private:
wl_resource* presentation_callback_ = nullptr;
diff --git a/chromium/ui/ozone/platform/wayland/test/scoped_wl_array.cc b/chromium/ui/ozone/platform/wayland/test/scoped_wl_array.cc
new file mode 100644
index 00000000000..9977ca1db17
--- /dev/null
+++ b/chromium/ui/ozone/platform/wayland/test/scoped_wl_array.cc
@@ -0,0 +1,41 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/ozone/platform/wayland/test/scoped_wl_array.h"
+
+#include <wayland-server-core.h>
+
+namespace wl {
+
+ScopedWlArray::ScopedWlArray(const std::vector<int32_t> states) {
+ wl_array_init(&array_);
+ for (const auto& state : states)
+ AddStateToWlArray(state);
+}
+
+ScopedWlArray::ScopedWlArray(ScopedWlArray&& rhs) {
+ array_ = rhs.array_;
+ // wl_array_init sets rhs.array_'s fields to nullptr, so that
+ // the free() in wl_array_release() is a no-op.
+ wl_array_init(&rhs.array_);
+}
+
+ScopedWlArray& ScopedWlArray::operator=(ScopedWlArray&& rhs) {
+ wl_array_release(&array_);
+ array_ = rhs.array_;
+ // wl_array_init sets rhs.array_'s fields to nullptr, so that
+ // the free() in wl_array_release() is a no-op.
+ wl_array_init(&rhs.array_);
+ return *this;
+}
+
+ScopedWlArray::~ScopedWlArray() {
+ wl_array_release(&array_);
+}
+
+void ScopedWlArray::AddStateToWlArray(uint32_t state) {
+ *static_cast<uint32_t*>(wl_array_add(&array_, sizeof array_)) = state;
+}
+
+} // namespace wl
diff --git a/chromium/ui/ozone/platform/wayland/test/scoped_wl_array.h b/chromium/ui/ozone/platform/wayland/test/scoped_wl_array.h
new file mode 100644
index 00000000000..d9c86f99265
--- /dev/null
+++ b/chromium/ui/ozone/platform/wayland/test/scoped_wl_array.h
@@ -0,0 +1,31 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_OZONE_PLATFORM_WAYLAND_TEST_SCOPED_WL_ARRAY_H_
+#define UI_OZONE_PLATFORM_WAYLAND_TEST_SCOPED_WL_ARRAY_H_
+
+#include <wayland-server-core.h>
+
+#include <vector>
+
+namespace wl {
+
+class ScopedWlArray {
+ public:
+ explicit ScopedWlArray(const std::vector<int32_t> states);
+ ScopedWlArray(ScopedWlArray&& rhs);
+ ScopedWlArray& operator=(ScopedWlArray&& rhs);
+ ~ScopedWlArray();
+
+ wl_array* get() { return &array_; }
+
+ void AddStateToWlArray(uint32_t state);
+
+ private:
+ wl_array array_;
+};
+
+} // namespace wl
+
+#endif // UI_OZONE_PLATFORM_WAYLAND_TEST_SCOPED_WL_ARRAY_H_
diff --git a/chromium/ui/ozone/platform/wayland/test/test_data_device.cc b/chromium/ui/ozone/platform/wayland/test/test_data_device.cc
index fcd71d6e9c5..6fc8a3e464d 100644
--- a/chromium/ui/ozone/platform/wayland/test/test_data_device.cc
+++ b/chromium/ui/ozone/platform/wayland/test/test_data_device.cc
@@ -6,8 +6,13 @@
#include <wayland-server-core.h>
+#include <cstdint>
+
#include "base/notreached.h"
+#include "ui/ozone/platform/wayland/test/mock_surface.h"
+#include "ui/ozone/platform/wayland/test/server_object.h"
#include "ui/ozone/platform/wayland/test/test_data_offer.h"
+#include "ui/ozone/platform/wayland/test/test_data_source.h"
namespace wl {
@@ -19,7 +24,11 @@ void DataDeviceStartDrag(wl_client* client,
wl_resource* origin,
wl_resource* icon,
uint32_t serial) {
- NOTIMPLEMENTED();
+ auto* data_source = GetUserDataAs<TestDataSource>(source);
+ auto* origin_surface = GetUserDataAs<MockSurface>(origin);
+
+ GetUserDataAs<TestDataDevice>(resource)->StartDrag(data_source,
+ origin_surface, serial);
}
void DataDeviceSetSelection(wl_client* client,
@@ -50,6 +59,16 @@ void TestDataDevice::SetSelection(TestDataSource* data_source,
NOTIMPLEMENTED();
}
+void TestDataDevice::StartDrag(TestDataSource* source,
+ MockSurface* origin,
+ uint32_t serial) {
+ DCHECK(source);
+ DCHECK(origin);
+ if (delegate_)
+ delegate_->StartDrag(source, origin, serial);
+ wl_client_flush(client_);
+}
+
TestDataOffer* TestDataDevice::OnDataOffer() {
wl_resource* data_offer_resource = CreateResourceWithImpl<TestDataOffer>(
client_, &wl_data_offer_interface, wl_resource_get_version(resource()),
diff --git a/chromium/ui/ozone/platform/wayland/test/test_data_device.h b/chromium/ui/ozone/platform/wayland/test/test_data_device.h
index 9ac3357ed53..85ad4af518a 100644
--- a/chromium/ui/ozone/platform/wayland/test/test_data_device.h
+++ b/chromium/ui/ozone/platform/wayland/test/test_data_device.h
@@ -7,7 +7,10 @@
#include <wayland-server-protocol.h>
+#include <cstdint>
+
#include "base/macros.h"
+#include "ui/ozone/platform/wayland/test/mock_surface.h"
#include "ui/ozone/platform/wayland/test/server_object.h"
struct wl_client;
@@ -22,10 +25,21 @@ class TestDataSource;
class TestDataDevice : public ServerObject {
public:
+ struct Delegate {
+ virtual void StartDrag(TestDataSource* source,
+ MockSurface* origin,
+ uint32_t serial) = 0;
+ };
+
TestDataDevice(wl_resource* resource, wl_client* client);
~TestDataDevice() override;
+ void set_delegate(Delegate* delegate) { delegate_ = delegate; }
+
void SetSelection(TestDataSource* data_source, uint32_t serial);
+ void StartDrag(TestDataSource* data_source,
+ MockSurface* origin,
+ uint32_t serial);
TestDataOffer* OnDataOffer();
void OnEnter(uint32_t serial,
@@ -41,6 +55,7 @@ class TestDataDevice : public ServerObject {
private:
TestDataOffer* data_offer_;
wl_client* client_ = nullptr;
+ Delegate* delegate_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(TestDataDevice);
};
diff --git a/chromium/ui/ozone/platform/wayland/test/test_data_source.cc b/chromium/ui/ozone/platform/wayland/test/test_data_source.cc
index 530a2e443c7..4d11ec8e9b1 100644
--- a/chromium/ui/ozone/platform/wayland/test/test_data_source.cc
+++ b/chromium/ui/ozone/platform/wayland/test/test_data_source.cc
@@ -5,6 +5,8 @@
#include "ui/ozone/platform/wayland/test/test_data_source.h"
#include <wayland-server-core.h>
+
+#include <cstdint>
#include <utility>
#include "base/bind.h"
@@ -51,16 +53,16 @@ void DataSourceDestroy(wl_client* client, wl_resource* resource) {
wl_resource_destroy(resource);
}
-void SetActions(wl_client* client,
- wl_resource* resource,
- uint32_t dnd_actions) {
- NOTIMPLEMENTED();
+void DataSourceSetActions(wl_client* client,
+ wl_resource* resource,
+ uint32_t dnd_actions) {
+ GetUserDataAs<TestDataSource>(resource)->SetActions(dnd_actions);
}
} // namespace
const struct wl_data_source_interface kTestDataSourceImpl = {
- DataSourceOffer, DataSourceDestroy, SetActions};
+ DataSourceOffer, DataSourceDestroy, DataSourceSetActions};
TestDataSource::TestDataSource(wl_resource* resource)
: ServerObject(resource),
@@ -70,7 +72,11 @@ TestDataSource::TestDataSource(wl_resource* resource)
TestDataSource::~TestDataSource() {}
void TestDataSource::Offer(const std::string& mime_type) {
- NOTIMPLEMENTED();
+ mime_types_.push_back(mime_type);
+}
+
+void TestDataSource::SetActions(uint32_t dnd_actions) {
+ actions_ |= dnd_actions;
}
void TestDataSource::ReadData(const std::string& mime_type,
diff --git a/chromium/ui/ozone/platform/wayland/test/test_data_source.h b/chromium/ui/ozone/platform/wayland/test/test_data_source.h
index 3cfdbff7a69..bcf66c3c26a 100644
--- a/chromium/ui/ozone/platform/wayland/test/test_data_source.h
+++ b/chromium/ui/ozone/platform/wayland/test/test_data_source.h
@@ -7,10 +7,12 @@
#include <wayland-server-protocol.h>
+#include <cstdint>
#include <memory>
#include <string>
#include <vector>
+#include "base/callback_forward.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "ui/ozone/platform/wayland/test/server_object.h"
@@ -31,15 +33,23 @@ class TestDataSource : public ServerObject {
~TestDataSource() override;
void Offer(const std::string& mime_type);
+ void SetActions(uint32_t dnd_actions);
using ReadDataCallback = base::OnceCallback<void(std::vector<uint8_t>&&)>;
void ReadData(const std::string& mime_type, ReadDataCallback callback);
void OnCancelled();
+ std::vector<std::string> mime_types() const { return mime_types_; }
+
+ uint32_t actions() const { return actions_; }
+
private:
const scoped_refptr<base::SequencedTaskRunner> task_runner_;
+ std::vector<std::string> mime_types_;
+ uint32_t actions_ = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
+
DISALLOW_COPY_AND_ASSIGN(TestDataSource);
};
diff --git a/chromium/ui/ozone/platform/wayland/test/test_subsurface.cc b/chromium/ui/ozone/platform/wayland/test/test_subsurface.cc
index 1609bb2012a..73773c773b1 100644
--- a/chromium/ui/ozone/platform/wayland/test/test_subsurface.cc
+++ b/chromium/ui/ozone/platform/wayland/test/test_subsurface.cc
@@ -4,7 +4,7 @@
#include "ui/ozone/platform/wayland/test/mock_surface.h"
-#include "base/logging.h"
+#include "base/notreached.h"
namespace wl {
diff --git a/chromium/ui/ozone/platform/wayland/test/wayland_test.cc b/chromium/ui/ozone/platform/wayland/test/wayland_test.cc
index 2866ab6a07f..67c8f2bbaf2 100644
--- a/chromium/ui/ozone/platform/wayland/test/wayland_test.cc
+++ b/chromium/ui/ozone/platform/wayland/test/wayland_test.cc
@@ -10,6 +10,7 @@
#include "ui/ozone/platform/wayland/host/wayland_output_manager.h"
#include "ui/ozone/platform/wayland/host/wayland_screen.h"
#include "ui/ozone/platform/wayland/test/mock_surface.h"
+#include "ui/ozone/platform/wayland/test/scoped_wl_array.h"
#include "ui/platform_window/platform_window_init_properties.h"
#if BUILDFLAG(USE_XKBCOMMON)
@@ -66,6 +67,11 @@ void WaylandTest::SetUp() {
surface_ = server_.GetObject<wl::MockSurface>(widget_);
ASSERT_TRUE(surface_);
+ // The surface must be activated before buffers are attached.
+ ActivateSurface(server_.GetObject<wl::MockSurface>(widget_)->xdg_surface());
+
+ Sync();
+
initialized_ = true;
}
@@ -86,4 +92,33 @@ void WaylandTest::Sync() {
server_.Pause();
}
+void WaylandTest::SendConfigureEvent(wl::MockXdgSurface* xdg_surface,
+ int width,
+ int height,
+ uint32_t serial,
+ struct wl_array* states) {
+ // In xdg_shell_v6+, both surfaces send serial configure event and toplevel
+ // surfaces send other data like states, heights and widths.
+ // Please note that toplevel surfaces may not exist if the surface was created
+ // for the popup role.
+ if (GetParam() == kXdgShellV6) {
+ zxdg_surface_v6_send_configure(xdg_surface->resource(), serial);
+ if (xdg_surface->xdg_toplevel()) {
+ zxdg_toplevel_v6_send_configure(xdg_surface->xdg_toplevel()->resource(),
+ width, height, states);
+ }
+ } else {
+ xdg_surface_send_configure(xdg_surface->resource(), serial);
+ if (xdg_surface->xdg_toplevel()) {
+ xdg_toplevel_send_configure(xdg_surface->xdg_toplevel()->resource(),
+ width, height, states);
+ }
+ }
+}
+
+void WaylandTest::ActivateSurface(wl::MockXdgSurface* xdg_surface) {
+ wl::ScopedWlArray state({XDG_TOPLEVEL_STATE_ACTIVATED});
+ SendConfigureEvent(xdg_surface, 0, 0, 1, state.get());
+}
+
} // namespace ui
diff --git a/chromium/ui/ozone/platform/wayland/test/wayland_test.h b/chromium/ui/ozone/platform/wayland/test/wayland_test.h
index fc8161eb51d..97df3efbade 100644
--- a/chromium/ui/ozone/platform/wayland/test/wayland_test.h
+++ b/chromium/ui/ozone/platform/wayland/test/wayland_test.h
@@ -24,6 +24,7 @@
namespace wl {
class MockSurface;
+class MockXdgSurface;
} // namespace wl
namespace ui {
@@ -47,6 +48,18 @@ class WaylandTest : public ::testing::TestWithParam<uint32_t> {
void Sync();
protected:
+ // Sends configure event for the |xdg_surface|.
+ void SendConfigureEvent(wl::MockXdgSurface* xdg_surface,
+ int width,
+ int height,
+ uint32_t serial,
+ struct wl_array* states);
+
+ // Sends XDG_TOPLEVEL_STATE_ACTIVATED to the |xdg_surface| with width and
+ // height set to 0, which results in asking the client to set the width and
+ // height of the surface.
+ void ActivateSurface(wl::MockXdgSurface* xdg_surface);
+
base::test::TaskEnvironment task_environment_;
wl::TestWaylandServerThread server_;
diff --git a/chromium/ui/ozone/platform/wayland/wayland_buffer_manager_unittest.cc b/chromium/ui/ozone/platform/wayland/wayland_buffer_manager_unittest.cc
index de10370fe69..acf7594d5e2 100644
--- a/chromium/ui/ozone/platform/wayland/wayland_buffer_manager_unittest.cc
+++ b/chromium/ui/ozone/platform/wayland/wayland_buffer_manager_unittest.cc
@@ -125,7 +125,7 @@ class WaylandBufferManagerTest : public WaylandTest {
}
}
- void CreateDmabufBasedBufferAndSetTerminateExpecation(
+ void CreateDmabufBasedBufferAndSetTerminateExpectation(
bool fail,
uint32_t buffer_id,
base::ScopedFD fd = base::ScopedFD(),
@@ -187,11 +187,14 @@ class WaylandBufferManagerTest : public WaylandTest {
}
}
- std::unique_ptr<WaylandWindow> CreateWindow() {
+ std::unique_ptr<WaylandWindow> CreateWindow(
+ PlatformWindowType type = PlatformWindowType::kWindow,
+ gfx::AcceleratedWidget parent_widget = gfx::kNullAcceleratedWidget) {
testing::Mock::VerifyAndClearExpectations(&delegate_);
PlatformWindowInitProperties properties;
properties.bounds = gfx::Rect(0, 0, 800, 600);
- properties.type = PlatformWindowType::kWindow;
+ properties.type = type;
+ properties.parent_widget = parent_widget;
auto new_window = WaylandWindow::Create(&delegate_, connection_.get(),
std::move(properties));
EXPECT_TRUE(new_window);
@@ -214,8 +217,8 @@ TEST_P(WaylandBufferManagerTest, CreateDmabufBasedBuffers) {
EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
- CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/,
- kDmabufBufferId);
+ CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/,
+ kDmabufBufferId);
DestroyBufferAndSetTerminateExpectation(gfx::kNullAcceleratedWidget,
kDmabufBufferId, false /*fail*/);
}
@@ -255,7 +258,7 @@ TEST_P(WaylandBufferManagerTest, VerifyModifiers) {
EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
- CreateDmabufBasedBufferAndSetTerminateExpecation(
+ CreateDmabufBasedBufferAndSetTerminateExpectation(
false /*fail*/, kDmabufBufferId, base::ScopedFD(), kDefaultSize, {1}, {2},
{kFormatModiferLinear}, kFourccFormatR8, 1);
@@ -304,7 +307,7 @@ TEST_P(WaylandBufferManagerTest, ValidateDataFromGpu) {
for (const auto& bad : kBadInputs) {
EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(0);
base::ScopedFD dummy;
- CreateDmabufBasedBufferAndSetTerminateExpecation(
+ CreateDmabufBasedBufferAndSetTerminateExpectation(
true /*fail*/, bad.buffer_id,
bad.has_file ? MakeFD() : std::move(dummy), bad.size, bad.strides,
bad.offsets, bad.modifiers, bad.format, bad.planes_count);
@@ -321,25 +324,27 @@ TEST_P(WaylandBufferManagerTest, CreateAndDestroyBuffer) {
// id if they haven't been assigned to any surfaces yet.
{
EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(2);
- CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/,
- kBufferId1);
- CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/,
- kBufferId2);
+ CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/,
+ kBufferId1);
+ CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/,
+ kBufferId2);
// Can't create buffer with existing id.
- CreateDmabufBasedBufferAndSetTerminateExpecation(true /*fail*/, kBufferId2);
+ CreateDmabufBasedBufferAndSetTerminateExpectation(true /*fail*/,
+ kBufferId2);
}
// ... impossible to create buffers with the same id if one of them
// has already been attached to a surface.
{
EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
- CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/,
- kBufferId1);
+ CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/,
+ kBufferId1);
buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, window_->GetBounds());
- CreateDmabufBasedBufferAndSetTerminateExpecation(true /*fail*/, kBufferId1);
+ CreateDmabufBasedBufferAndSetTerminateExpectation(true /*fail*/,
+ kBufferId1);
}
// ... impossible to destroy non-existing buffer.
@@ -356,8 +361,8 @@ TEST_P(WaylandBufferManagerTest, CreateAndDestroyBuffer) {
// specified.
{
EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
- CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/,
- kBufferId1);
+ CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/,
+ kBufferId1);
buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, window_->GetBounds());
@@ -369,22 +374,22 @@ TEST_P(WaylandBufferManagerTest, CreateAndDestroyBuffer) {
// widgets.
{
EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
- CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/,
- kBufferId1);
+ CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/,
+ kBufferId1);
DestroyBufferAndSetTerminateExpectation(widget, kBufferId1, false /*fail*/);
}
// ... impossible to destroy buffers twice.
{
EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(3);
- CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/,
- kBufferId1);
+ CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/,
+ kBufferId1);
// Attach to a surface.
buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, window_->GetBounds());
// Created non-attached buffer as well.
- CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/,
- kBufferId2);
+ CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/,
+ kBufferId2);
DestroyBufferAndSetTerminateExpectation(widget, kBufferId1, false /*fail*/);
// Can't destroy the buffer with non-existing id (the manager cleared the
@@ -397,8 +402,8 @@ TEST_P(WaylandBufferManagerTest, CreateAndDestroyBuffer) {
kBufferId2, true /*fail*/);
// Create and destroy non-attached buffer twice.
- CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/,
- kBufferId2);
+ CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/,
+ kBufferId2);
DestroyBufferAndSetTerminateExpectation(gfx::kNullAcceleratedWidget,
kBufferId2, false /*fail*/);
DestroyBufferAndSetTerminateExpectation(gfx::kNullAcceleratedWidget,
@@ -408,7 +413,7 @@ TEST_P(WaylandBufferManagerTest, CreateAndDestroyBuffer) {
TEST_P(WaylandBufferManagerTest, CommitBufferNonExistingBufferId) {
EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
- CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, 1u);
+ CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, 1u);
// Can't commit for non-existing buffer id.
SetTerminateCallbackExpectationAndDestroyChannel(&callback_, true /*fail*/);
@@ -421,7 +426,7 @@ TEST_P(WaylandBufferManagerTest, CommitBufferNonExistingBufferId) {
TEST_P(WaylandBufferManagerTest, CommitBufferNullWidget) {
constexpr uint32_t kBufferId = 1;
EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
- CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, kBufferId);
+ CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId);
// Can't commit for non-existing widget.
SetTerminateCallbackExpectationAndDestroyChannel(&callback_, true /*fail*/);
@@ -443,8 +448,8 @@ TEST_P(WaylandBufferManagerTest, EnsureCorrectOrderOfCallbacks) {
auto* linux_dmabuf = server_.zwp_linux_dmabuf_v1();
EXPECT_CALL(*linux_dmabuf, CreateParams(_, _, _)).Times(2);
- CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, kBufferId1);
- CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, kBufferId2);
+ CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId1);
+ CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId2);
Sync();
@@ -551,9 +556,9 @@ TEST_P(WaylandBufferManagerTest,
auto* linux_dmabuf = server_.zwp_linux_dmabuf_v1();
EXPECT_CALL(*linux_dmabuf, CreateParams(_, _, _)).Times(3);
- CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, kBufferId1);
- CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, kBufferId2);
- CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, kBufferId3);
+ CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId1);
+ CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId2);
+ CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId3);
Sync();
@@ -650,7 +655,149 @@ TEST_P(WaylandBufferManagerTest,
DestroyBufferAndSetTerminateExpectation(widget, kBufferId3, false /*fail*/);
}
-TEST_P(WaylandBufferManagerTest, MultiplePendingPresentationsForSameBuffer) {}
+// This test ensures that a discarded presentation feedback sent prior receiving
+// results for the previous presentation feedback does not make them
+// automatically failed.
+TEST_P(WaylandBufferManagerTest,
+ EnsureDiscardedPresentationDoesNotMakePreviousFeedbacksFailed) {
+ constexpr uint32_t kBufferId1 = 1;
+ constexpr uint32_t kBufferId2 = 2;
+ constexpr uint32_t kBufferId3 = 3;
+
+ // Enable wp_presentation support.
+ auto* mock_wp_presentation = server_.EnsureWpPresentation();
+ ASSERT_TRUE(mock_wp_presentation);
+
+ const gfx::AcceleratedWidget widget = window_->GetWidget();
+ const gfx::Rect bounds = gfx::Rect({0, 0}, kDefaultSize);
+ window_->SetBounds(bounds);
+
+ MockSurfaceGpu mock_surface_gpu(buffer_manager_gpu_.get(), widget_);
+
+ auto* linux_dmabuf = server_.zwp_linux_dmabuf_v1();
+ EXPECT_CALL(*linux_dmabuf, CreateParams(_, _, _)).Times(3);
+ CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId1);
+ CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId2);
+ CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId3);
+
+ Sync();
+
+ ProcessCreatedBufferResourcesWithExpectation(3u /* expected size */,
+ false /* fail */);
+
+ auto* mock_surface = server_.GetObject<wl::MockSurface>(widget);
+
+ constexpr uint32_t kNumberOfCommits = 3;
+ EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(kNumberOfCommits);
+ EXPECT_CALL(*mock_surface, Frame(_)).Times(kNumberOfCommits);
+ EXPECT_CALL(*mock_surface, Commit()).Times(kNumberOfCommits);
+
+ // All the other expectations must come in order.
+ ::testing::InSequence sequence;
+ EXPECT_CALL(mock_surface_gpu,
+ OnSubmission(kBufferId1, gfx::SwapResult::SWAP_ACK))
+ .Times(1);
+ EXPECT_CALL(mock_surface_gpu, OnPresentation(_, _)).Times(0);
+
+ // Commit first buffer
+ buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, bounds);
+
+ Sync();
+
+ // Will be sent later.
+ auto* presentation_callback1 =
+ mock_wp_presentation->ReleasePresentationCallback();
+
+ mock_surface->SendFrameCallback();
+
+ Sync();
+
+ EXPECT_CALL(mock_surface_gpu,
+ OnSubmission(kBufferId2, gfx::SwapResult::SWAP_ACK))
+ .Times(1);
+ EXPECT_CALL(mock_surface_gpu, OnPresentation(_, _)).Times(0);
+
+ // Commit second buffer
+ buffer_manager_gpu_->CommitBuffer(widget, kBufferId2, bounds);
+
+ Sync();
+
+ // Will be sent later.
+ auto* presentation_callback2 =
+ mock_wp_presentation->ReleasePresentationCallback();
+
+ // Release previous buffer and commit third buffer.
+ mock_surface->ReleasePrevAttachedBuffer();
+ mock_surface->SendFrameCallback();
+
+ Sync();
+
+ EXPECT_CALL(mock_surface_gpu,
+ OnSubmission(kBufferId3, gfx::SwapResult::SWAP_ACK))
+ .Times(1);
+ EXPECT_CALL(mock_surface_gpu, OnPresentation(_, _)).Times(0);
+
+ // Commit third buffer
+ buffer_manager_gpu_->CommitBuffer(widget, kBufferId3, bounds);
+
+ Sync();
+
+ mock_surface->ReleasePrevAttachedBuffer();
+
+ Sync();
+
+ // Even though WaylandBufferManagerHost stores the previous stores
+ // presentation feedbacks and waits for their value, the current last one
+ // mustn't result in the previous marked as failed. Thus, no feedback must be
+ // actually sent to the MockSurfaceGpu as it's required to send feedbacks in
+ // order.
+ EXPECT_CALL(mock_surface_gpu, OnPresentation(_, _)).Times(0);
+
+ mock_wp_presentation->SendPresentationCallbackDiscarded();
+
+ Sync();
+
+ // Now, start to send all the previous callbacks.
+ EXPECT_CALL(mock_surface_gpu,
+ OnPresentation(
+ kBufferId1,
+ ::testing::Field(
+ &gfx::PresentationFeedback::flags,
+ ::testing::Eq(gfx::PresentationFeedback::Flags::kVSync))))
+ .Times(1);
+
+ mock_wp_presentation->set_presentation_callback(presentation_callback1);
+ mock_wp_presentation->SendPresentationCallback();
+
+ Sync();
+
+ // Now, send the second presentation feedback. It will send both second and
+ // third feedback that was discarded.
+ EXPECT_CALL(mock_surface_gpu,
+ OnPresentation(
+ kBufferId2,
+ ::testing::Field(
+ &gfx::PresentationFeedback::flags,
+ ::testing::Eq(gfx::PresentationFeedback::Flags::kVSync))))
+ .Times(1);
+ EXPECT_CALL(
+ mock_surface_gpu,
+ OnPresentation(
+ kBufferId3,
+ ::testing::Field(
+ &gfx::PresentationFeedback::flags,
+ ::testing::Eq(gfx::PresentationFeedback::Flags::kFailure))))
+ .Times(1);
+
+ mock_wp_presentation->set_presentation_callback(presentation_callback2);
+ mock_wp_presentation->SendPresentationCallback();
+
+ Sync();
+
+ DestroyBufferAndSetTerminateExpectation(widget, kBufferId1, false /*fail*/);
+ DestroyBufferAndSetTerminateExpectation(widget, kBufferId2, false /*fail*/);
+ DestroyBufferAndSetTerminateExpectation(widget, kBufferId3, false /*fail*/);
+}
TEST_P(WaylandBufferManagerTest, TestCommitBufferConditions) {
constexpr uint32_t kDmabufBufferId = 1;
@@ -662,8 +809,8 @@ TEST_P(WaylandBufferManagerTest, TestCommitBufferConditions) {
auto* linux_dmabuf = server_.zwp_linux_dmabuf_v1();
EXPECT_CALL(*linux_dmabuf, CreateParams(_, _, _)).Times(1);
- CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/,
- kDmabufBufferId);
+ CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/,
+ kDmabufBufferId);
// Part 1: the surface mustn't have a buffer attached until
// zwp_linux_buffer_params_v1_send_created is called. Instead, the buffer must
@@ -694,8 +841,8 @@ TEST_P(WaylandBufferManagerTest, TestCommitBufferConditions) {
// sent by the server.
EXPECT_CALL(*linux_dmabuf, CreateParams(_, _, _)).Times(1);
- CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/,
- kDmabufBufferId2);
+ CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/,
+ kDmabufBufferId2);
ProcessCreatedBufferResourcesWithExpectation(1u /* expected size */,
false /* fail */);
@@ -726,6 +873,77 @@ TEST_P(WaylandBufferManagerTest, TestCommitBufferConditions) {
false /*fail*/);
}
+// Tests the surface does not have buffers attached until it's configured at
+// least once.
+TEST_P(WaylandBufferManagerTest, TestCommitBufferConditionsAckConfigured) {
+ constexpr uint32_t kDmabufBufferId = 1;
+
+ // Exercise three window types that create different windows - toplevel, popup
+ // and subsurface.
+ std::vector<PlatformWindowType> window_types{PlatformWindowType::kWindow,
+ PlatformWindowType::kPopup,
+ PlatformWindowType::kTooltip};
+
+ for (const auto& type : window_types) {
+ // If the type is not kWindow, provide default created window as parent of
+ // the newly created window.
+ auto temp_window = CreateWindow(type, type != PlatformWindowType::kWindow
+ ? widget_
+ : gfx::kNullAcceleratedWidget);
+ auto widget = temp_window->GetWidget();
+
+ // Subsurface doesn't have an interface for sending configure events.
+ // Thus, WaylandSubsurface notifies the manager that the window is
+ // activated upon creation of the subsurface. Skip calling Show() and call
+ // later then.
+ if (type != PlatformWindowType::kTooltip)
+ temp_window->Show(false);
+
+ Sync();
+
+ auto* mock_surface = server_.GetObject<wl::MockSurface>(widget);
+
+ auto* linux_dmabuf = server_.zwp_linux_dmabuf_v1();
+ EXPECT_CALL(*linux_dmabuf, CreateParams(_, _, _)).Times(1);
+
+ CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/,
+ kDmabufBufferId);
+
+ Sync();
+
+ ProcessCreatedBufferResourcesWithExpectation(1u /* expected size */,
+ false /* fail */);
+
+ EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(0);
+ EXPECT_CALL(*mock_surface, Frame(_)).Times(0);
+ EXPECT_CALL(*mock_surface, Commit()).Times(0);
+
+ buffer_manager_gpu_->CommitBuffer(widget, kDmabufBufferId,
+ window_->GetBounds());
+ Sync();
+
+ if (type != PlatformWindowType::kTooltip) {
+ DCHECK(mock_surface->xdg_surface());
+ ActivateSurface(mock_surface->xdg_surface());
+ } else {
+ // See the comment near Show() call above.
+ temp_window->Show(false);
+ }
+
+ EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(1);
+ EXPECT_CALL(*mock_surface, Frame(_)).Times(1);
+ EXPECT_CALL(*mock_surface, Commit()).Times(1);
+
+ Sync();
+
+ temp_window.reset();
+ DestroyBufferAndSetTerminateExpectation(widget, kDmabufBufferId,
+ false /*fail*/);
+
+ Sync();
+ }
+}
+
// The buffer that is not originally attached to any of the surfaces,
// must be attached when a commit request comes. Also, it must setup a buffer
// release listener and OnSubmission must be called for that buffer if it is
@@ -743,7 +961,7 @@ TEST_P(WaylandBufferManagerTest, AnonymousBufferAttachedAndReleased) {
auto* linux_dmabuf = server_.zwp_linux_dmabuf_v1();
EXPECT_CALL(*linux_dmabuf, CreateParams(_, _, _)).Times(1);
- CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, kBufferId1);
+ CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId1);
Sync();
@@ -776,7 +994,7 @@ TEST_P(WaylandBufferManagerTest, AnonymousBufferAttachedAndReleased) {
// Now synchronously create a second buffer and commit it. The release
// callback must be setup and OnSubmission must be called.
EXPECT_CALL(*linux_dmabuf, CreateParams(_, _, _)).Times(1);
- CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, kBufferId2);
+ CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId2);
Sync();
@@ -804,7 +1022,7 @@ TEST_P(WaylandBufferManagerTest, AnonymousBufferAttachedAndReleased) {
// released once the buffer is committed and processed (that is, it must be
// able to setup a buffer release callback).
EXPECT_CALL(*linux_dmabuf, CreateParams(_, _, _)).Times(1);
- CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, kBufferId3);
+ CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId3);
Sync();
@@ -845,7 +1063,7 @@ TEST_P(WaylandBufferManagerTest, DestroyBufferForDestroyedWindow) {
auto widget = temp_window->GetWidget();
EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
- CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, kBufferId);
+ CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId);
Sync();
@@ -866,7 +1084,7 @@ TEST_P(WaylandBufferManagerTest, DestroyedWindowNoSubmissionSingleBuffer) {
auto bounds = temp_window->GetBounds();
EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
- CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, kBufferId);
+ CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId);
ProcessCreatedBufferResourcesWithExpectation(1u /* expected size */,
false /* fail */);
@@ -894,11 +1112,22 @@ TEST_P(WaylandBufferManagerTest, DestroyedWindowNoSubmissionMultipleBuffers) {
constexpr uint32_t kBufferId2 = 2;
auto temp_window = CreateWindow();
+ temp_window->Show(false);
+
+ Sync();
+
auto widget = temp_window->GetWidget();
auto bounds = temp_window->GetBounds();
+ auto* mock_surface = server_.GetObject<wl::MockSurface>(widget);
+ ASSERT_TRUE(mock_surface);
+
+ ActivateSurface(mock_surface->xdg_surface());
+
+ Sync();
+
EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
- CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, kBufferId1);
+ CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId1);
ProcessCreatedBufferResourcesWithExpectation(1u /* expected size */,
false /* fail */);
@@ -915,13 +1144,12 @@ TEST_P(WaylandBufferManagerTest, DestroyedWindowNoSubmissionMultipleBuffers) {
Sync();
- auto* mock_surface = server_.GetObject<wl::MockSurface>(widget);
mock_surface->SendFrameCallback();
Sync();
EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
- CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, kBufferId2);
+ CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId2);
ProcessCreatedBufferResourcesWithExpectation(1u /* expected size */,
false /* fail */);
@@ -966,8 +1194,8 @@ TEST_P(WaylandBufferManagerTest, SubmitSameBufferMultipleTimes) {
auto* linux_dmabuf = server_.zwp_linux_dmabuf_v1();
EXPECT_CALL(*linux_dmabuf, CreateParams(_, _, _)).Times(2);
- CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, kBufferId1);
- CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, kBufferId2);
+ CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId1);
+ CreateDmabufBasedBufferAndSetTerminateExpectation(false /*fail*/, kBufferId2);
Sync();