diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2021-10-26 13:57:00 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2021-11-02 11:31:01 +0000 |
commit | 1943b3c2a1dcee36c233724fc4ee7613d71b9cf6 (patch) | |
tree | 8c1b5f12357025c197da5427ae02cfdc2f3570d6 /chromium/ui/base | |
parent | 21ba0c5d4bf8fba15dddd97cd693bad2358b77fd (diff) | |
download | qtwebengine-chromium-1943b3c2a1dcee36c233724fc4ee7613d71b9cf6.tar.gz |
BASELINE: Update Chromium to 94.0.4606.111
Change-Id: I924781584def20fc800bedf6ff41fdb96c438193
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/ui/base')
216 files changed, 3740 insertions, 2255 deletions
diff --git a/chromium/ui/base/BUILD.gn b/chromium/ui/base/BUILD.gn index 2939b20ceb0..8d3dd673f60 100644 --- a/chromium/ui/base/BUILD.gn +++ b/chromium/ui/base/BUILD.gn @@ -119,16 +119,6 @@ component("base") { "l10n/time_format.h", "layout.cc", "layout.h", - "metadata/base_type_conversion.cc", - "metadata/base_type_conversion.h", - "metadata/metadata_cache.cc", - "metadata/metadata_cache.h", - "metadata/metadata_header_macros.h", - "metadata/metadata_impl_macros.h", - "metadata/metadata_macros_internal.h", - "metadata/metadata_types.cc", - "metadata/metadata_types.h", - "metadata/property_metadata.h", "models/button_menu_item_model.cc", "models/button_menu_item_model.h", "models/combobox_model.cc", @@ -452,10 +442,10 @@ component("base") { deps = [ ":locales_list", + "//base", "//base:base_static", "//base:i18n", "//base/third_party/dynamic_annotations", - "//base/util/type_safety:type_safety", "//build:chromeos_buildflags", "//net", "//third_party/brotli:dec", @@ -467,6 +457,7 @@ component("base") { "//ui/base/clipboard:clipboard_types", "//ui/base/dragdrop/mojom", "//ui/display", + "//ui/display/util:gpu_info_util", "//ui/events", "//ui/events/devices", "//ui/resources:webui_generated_resources_grd", @@ -648,9 +639,6 @@ component("features") { "//base", "//build:chromeos_buildflags", ] - if (!is_ios) { - deps += [ "//media:media_buildflags" ] - } } # TODO(crbug.com/1091985): Support CrOS. @@ -899,8 +887,6 @@ test("ui_base_unittests") { "l10n/l10n_util_unittest.cc", "l10n/time_format_unittest.cc", "layout_unittest.cc", - "metadata/base_type_conversion_unittest.cc", - "metadata/metadata_unittest.cc", "models/image_model_unittest.cc", "models/simple_combobox_model_unittest.cc", "models/simple_menu_model_unittest.cc", @@ -972,6 +958,7 @@ test("ui_base_unittests") { "//ui/base/clipboard:file_info", "//ui/base/cursor:unittests", "//ui/base/data_transfer_policy:unittests", + "//ui/base/metadata:metadata_tests", "//ui/base/prediction:prediction", "//ui/display", "//ui/events:events_base", @@ -1191,6 +1178,11 @@ test("ui_base_unittests") { if (is_mac) { data += [ "$root_out_dir/ui_unittests Framework.framework/" ] } + + if (!is_ios) { + sources += [ "//ui/lottie/animation_unittest.cc" ] + deps += [ "//ui/lottie" ] + } } if (is_mac) { diff --git a/chromium/ui/base/accelerators/accelerator_manager_unittest.cc b/chromium/ui/base/accelerators/accelerator_manager_unittest.cc index 701c651ce14..99b81947933 100644 --- a/chromium/ui/base/accelerators/accelerator_manager_unittest.cc +++ b/chromium/ui/base/accelerators/accelerator_manager_unittest.cc @@ -4,7 +4,7 @@ #include "ui/base/accelerators/accelerator_manager.h" -#include "base/stl_util.h" +#include "base/cxx17_backports.h" #include "base/test/scoped_feature_list.h" #include "build/chromeos_buildflags.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/chromium/ui/base/accelerators/menu_label_accelerator_util_linux_unittest.cc b/chromium/ui/base/accelerators/menu_label_accelerator_util_linux_unittest.cc index 3b9a42f9665..83ab48ff958 100644 --- a/chromium/ui/base/accelerators/menu_label_accelerator_util_linux_unittest.cc +++ b/chromium/ui/base/accelerators/menu_label_accelerator_util_linux_unittest.cc @@ -6,7 +6,7 @@ #include <stddef.h> -#include "base/stl_util.h" +#include "base/cxx17_backports.h" #include "testing/gtest/include/gtest/gtest.h" namespace ui { diff --git a/chromium/ui/base/class_property.cc b/chromium/ui/base/class_property.cc index 86c0c48d3d0..78767b3972c 100644 --- a/chromium/ui/base/class_property.cc +++ b/chromium/ui/base/class_property.cc @@ -7,6 +7,8 @@ #include <algorithm> #include <utility> +#include "base/notreached.h" + namespace ui { PropertyHandler::PropertyHandler() = default; @@ -28,7 +30,7 @@ int64_t PropertyHandler::SetPropertyInternal(const void* key, PropertyDeallocator deallocator, int64_t value, int64_t default_value) { - int64_t old = GetPropertyInternal(key, default_value); + int64_t old = GetPropertyInternal(key, default_value, false); if (value == default_value) { prop_map_.erase(key); } else { @@ -53,12 +55,30 @@ void PropertyHandler::ClearProperties() { prop_map_.clear(); } +PropertyHandler* PropertyHandler::GetParentHandler() const { + // If you plan on using cascading properties, you must override this method + // to return the "parent" handler. If you want to use cascading properties in + // scenarios where there isn't a notion of a parent, just override this method + // and return null. + NOTREACHED(); + return nullptr; +} + int64_t PropertyHandler::GetPropertyInternal(const void* key, - int64_t default_value) const { - auto iter = prop_map_.find(key); - if (iter == prop_map_.end()) - return default_value; - return iter->second.value; + int64_t default_value, + bool search_parent) const { + const PropertyHandler* handler = this; + while (handler) { + auto iter = handler->prop_map_.find(key); + if (iter == handler->prop_map_.end()) { + if (!search_parent) + break; + handler = handler->GetParentHandler(); + continue; + } + return iter->second.value; + } + return default_value; } std::set<const void*> PropertyHandler::GetAllPropertyKeys() const { diff --git a/chromium/ui/base/class_property.h b/chromium/ui/base/class_property.h index 25c00e7d064..88b49386ae0 100644 --- a/chromium/ui/base/class_property.h +++ b/chromium/ui/base/class_property.h @@ -8,7 +8,9 @@ #include <stdint.h> #include <map> +#include <memory> #include <set> +#include <type_traits> #include "base/component_export.h" #include "base/time/time.h" @@ -45,6 +47,18 @@ // If the properties are used outside the file where they are defined // their accessor methods should also be declared in a suitable header // using DECLARE_EXPORTED_UI_CLASS_PROPERTY_TYPE(FOO_EXPORT, MyType) +// +// Cascading properties: +// +// Use the DEFINE_CASCADING_XXX macros to create a class property type that +// will automatically search up an instance hierarchy for the first defined +// property. This only affects the GetProperty() call. SetProperty() will +// still explicitly set the value on the given instance. This is useful for +// hierarchies of instances which a single set property can effect a whole sub- +// tree of instances. +// +// In order to use this feature, you must override GetParentHandler() on the +// class that inherits PropertyHandler. namespace ui { @@ -55,6 +69,7 @@ template<typename T> struct ClassProperty { T default_value; const char* name; + bool cascading = false; PropertyDeallocator deallocator; }; @@ -79,8 +94,10 @@ class COMPONENT_EXPORT(UI_BASE) PropertyHandler { // Sets the |value| of the given class |property|. Setting to the default // value (e.g., NULL) removes the property. The lifetime of objects set as // values of unowned properties is managed by the caller (owned properties are - // freed when they are overwritten or cleared). - template<typename T> + // freed when they are overwritten or cleared). NOTE: This should NOT be + // for passing a raw pointer for owned properties. Prefer the std::unique_ptr + // version below. + template <typename T> void SetProperty(const ClassProperty<T>* property, T value); // Sets the |value| of the given class |property|, which must be an owned @@ -97,8 +114,16 @@ class COMPONENT_EXPORT(UI_BASE) PropertyHandler { template <typename T> void SetProperty(const ClassProperty<T*>* property, T&& value); + // Sets the |value| of the given class |property|, which must be an owned + // property and of pointer type. Use std::make_unique<> or base::WrapUnique to + // ensure proper ownership transfer. + template <typename T> + T* SetProperty(const ClassProperty<T*>* property, std::unique_ptr<T> value); + // Returns the value of the given class |property|. Returns the // property-specific default value if the property was not previously set. + // The return value is the raw pointer useful for accessing the value + // contents. template<typename T> T GetProperty(const ClassProperty<T>* property) const; @@ -115,6 +140,10 @@ class COMPONENT_EXPORT(UI_BASE) PropertyHandler { virtual void AfterPropertyChange(const void* key, int64_t old_value) {} void ClearProperties(); + // Override this function when inheriting this class on a class or classes + // in which instances are arranged in a parent-child relationship and + // the intent is to use cascading properties. + virtual PropertyHandler* GetParentHandler() const; // Called by the public {Set,Get,Clear}Property functions. int64_t SetPropertyInternal(const void* key, @@ -122,7 +151,13 @@ class COMPONENT_EXPORT(UI_BASE) PropertyHandler { PropertyDeallocator deallocator, int64_t value, int64_t default_value); - int64_t GetPropertyInternal(const void* key, int64_t default_value) const; + // |search_parent| is required here for the setters to be able to look up the + // current value of property only on the current instance without searching + // the parent handler. This value is sent with the AfterPropertyChange() + // notification. + int64_t GetPropertyInternal(const void* key, + int64_t default_value, + bool search_parent) const; private: // Value struct to keep the name and deallocator for this property. @@ -164,9 +199,10 @@ namespace subtle { class COMPONENT_EXPORT(UI_BASE) PropertyHelper { public: - template<typename T> + template <typename T> static void Set(::ui::PropertyHandler* handler, - const ::ui::ClassProperty<T>* property, T value) { + const ::ui::ClassProperty<T>* property, + T value) { int64_t old = handler->SetPropertyInternal( property, property->name, value == property->default_value ? nullptr : property->deallocator, @@ -177,16 +213,18 @@ class COMPONENT_EXPORT(UI_BASE) PropertyHelper { (*property->deallocator)(old); } } - template<typename T> + template <typename T> static T Get(const ::ui::PropertyHandler* handler, - const ::ui::ClassProperty<T>* property) { + const ::ui::ClassProperty<T>* property, + bool allow_cascade) { return ClassPropertyCaster<T>::FromInt64(handler->GetPropertyInternal( - property, ClassPropertyCaster<T>::ToInt64(property->default_value))); + property, ClassPropertyCaster<T>::ToInt64(property->default_value), + property->cascading && allow_cascade)); } - template<typename T> + template <typename T> static void Clear(::ui::PropertyHandler* handler, const ::ui::ClassProperty<T>* property) { - handler->SetProperty(property, property->default_value); + Set(handler, property, property->default_value); } }; @@ -202,13 +240,13 @@ template <typename T> void PropertyHandler::SetProperty(const ClassProperty<T*>* property, const T& value) { // Prevent additional heap allocation if possible. - T* const old = GetProperty(property); + T* const old = subtle::PropertyHelper::Get<T*>(this, property, false); if (old) { T temp(*old); *old = value; AfterPropertyChange(property, reinterpret_cast<int64_t>(&temp)); } else { - SetProperty(property, new T(value)); + SetProperty(property, std::make_unique<T>(value)); } } @@ -216,16 +254,26 @@ template <typename T> void PropertyHandler::SetProperty(const ClassProperty<T*>* property, T&& value) { // Prevent additional heap allocation if possible. - T* const old = GetProperty(property); + T* const old = subtle::PropertyHelper::Get<T*>(this, property, false); if (old) { T temp(std::move(*old)); *old = std::forward<T>(value); AfterPropertyChange(property, reinterpret_cast<int64_t>(&temp)); } else { - SetProperty(property, new T(std::forward<T>(value))); + SetProperty(property, std::make_unique<T>(std::forward<T>(value))); } } +template <typename T> +T* PropertyHandler::SetProperty(const ClassProperty<T*>* property, + std::unique_ptr<T> value) { + // This form only works for 'owned' properties. + DCHECK(property->deallocator); + T* value_ptr = value.get(); + subtle::PropertyHelper::Set<T*>(this, property, value.release()); + return value_ptr; +} + } // namespace ui // Macros to declare the property getter/setter template functions. @@ -248,12 +296,17 @@ void PropertyHandler::SetProperty(const ClassProperty<T*>* property, template <> \ EXPORT void PropertyHandler::SetProperty(const ClassProperty<T>* property, \ T value) { \ + /* TODO(kylixrd, pbos): Once all the call-sites are fixed to only use */ \ + /* the unique_ptr version for owned properties, add the following */ \ + /* DCHECK to guard against passing raw pointers for owned properties. */ \ + /* DCHECK(!std::is_pointer<T>::value || */ \ + /* (std::is_pointer<T>::value && !property->deallocator)); */ \ subtle::PropertyHelper::Set<T>(this, property, value); \ } \ template <> \ EXPORT T \ PropertyHandler::GetProperty(const ClassProperty<T>* property) const { \ - return subtle::PropertyHelper::Get<T>(this, property); \ + return subtle::PropertyHelper::Get<T>(this, property, true); \ } \ template <> \ EXPORT void PropertyHandler::ClearProperty( \ @@ -267,18 +320,36 @@ void PropertyHandler::SetProperty(const ClassProperty<T*>* property, #define DEFINE_UI_CLASS_PROPERTY_KEY(TYPE, NAME, DEFAULT) \ static_assert(sizeof(TYPE) <= sizeof(int64_t), "property type too large"); \ - const ::ui::ClassProperty<TYPE> NAME##_Value = {DEFAULT, #NAME, nullptr}; \ + const ::ui::ClassProperty<TYPE> NAME##_Value = {DEFAULT, #NAME, false, \ + nullptr}; \ + const ::ui::ClassProperty<TYPE>* const NAME = &NAME##_Value; + +#define DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(TYPE, NAME, DEFAULT) \ + namespace { \ + void Deallocator##NAME(int64_t p) { \ + enum { type_must_be_complete = sizeof(TYPE) }; \ + delete ::ui::ClassPropertyCaster<TYPE*>::FromInt64(p); \ + } \ + const ::ui::ClassProperty<TYPE*> NAME##_Value = {DEFAULT, #NAME, false, \ + &Deallocator##NAME}; \ + } /* namespace */ \ + const ::ui::ClassProperty<TYPE*>* const NAME = &NAME##_Value; + +#define DEFINE_CASCADING_UI_CLASS_PROPERTY_KEY(TYPE, NAME, DEFAULT) \ + static_assert(sizeof(TYPE) <= sizeof(int64_t), "property type too large"); \ + const ::ui::ClassProperty<TYPE> NAME##_Value = {DEFAULT, #NAME, true, \ + nullptr}; \ const ::ui::ClassProperty<TYPE>* const NAME = &NAME##_Value; -#define DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(TYPE, NAME, DEFAULT) \ - namespace { \ - void Deallocator##NAME(int64_t p) { \ - enum { type_must_be_complete = sizeof(TYPE) }; \ - delete ::ui::ClassPropertyCaster<TYPE*>::FromInt64(p); \ - } \ - const ::ui::ClassProperty<TYPE*> NAME##_Value = {DEFAULT, #NAME, \ - &Deallocator##NAME}; \ - } /* namespace */ \ +#define DEFINE_CASCADING_OWNED_UI_CLASS_PROPERTY_KEY(TYPE, NAME, DEFAULT) \ + namespace { \ + void Deallocator##NAME(int64_t p) { \ + enum { type_must_be_complete = sizeof(TYPE) }; \ + delete ::ui::ClassPropertyCaster<TYPE*>::FromInt64(p); \ + } \ + const ::ui::ClassProperty<TYPE*> NAME##_Value = {DEFAULT, #NAME, true, \ + &Deallocator##NAME}; \ + } /* namespace */ \ const ::ui::ClassProperty<TYPE*>* const NAME = &NAME##_Value; #endif // UI_BASE_CLASS_PROPERTY_H_ diff --git a/chromium/ui/base/class_property_unittest.cc b/chromium/ui/base/class_property_unittest.cc index ffc2dca396d..99500764149 100644 --- a/chromium/ui/base/class_property_unittest.cc +++ b/chromium/ui/base/class_property_unittest.cc @@ -22,7 +22,9 @@ namespace { class TestProperty { public: - TestProperty() {} + TestProperty() = default; + TestProperty(const TestProperty&) = delete; + TestProperty& operator=(const TestProperty&) = delete; ~TestProperty() { last_deleted_ = this; } @@ -30,7 +32,18 @@ class TestProperty { private: static void* last_deleted_; - DISALLOW_COPY_AND_ASSIGN(TestProperty); +}; + +class TestCascadingProperty { + public: + explicit TestCascadingProperty(ui::PropertyHandler* handler) + : handler_(handler) {} + ~TestCascadingProperty() = default; + + ui::PropertyHandler* handler() { return handler_; } + + private: + ui::PropertyHandler* handler_; }; void* TestProperty::last_deleted_ = nullptr; @@ -66,11 +79,15 @@ DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(TestProperty, kOwnedKey, nullptr) DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(AssignableTestProperty, kAssignableKey, nullptr) +DEFINE_CASCADING_OWNED_UI_CLASS_PROPERTY_KEY(TestCascadingProperty, + kCascadingOwnedKey, + nullptr) } // namespace DEFINE_UI_CLASS_PROPERTY_TYPE(TestProperty*) DEFINE_UI_CLASS_PROPERTY_TYPE(AssignableTestProperty*) +DEFINE_UI_CLASS_PROPERTY_TYPE(TestCascadingProperty*) namespace ui { namespace test { @@ -79,15 +96,20 @@ namespace { class TestPropertyHandler : public PropertyHandler { public: + TestPropertyHandler() = default; + explicit TestPropertyHandler(TestPropertyHandler* parent) : parent_(parent) {} + ~TestPropertyHandler() override = default; int num_events() const { return num_events_; } protected: void AfterPropertyChange(const void* key, int64_t old_value) override { ++num_events_; } + PropertyHandler* GetParentHandler() const override { return parent_; } private: int num_events_ = 0; + TestPropertyHandler* parent_ = nullptr; }; const int kDefaultIntValue = -2; @@ -133,24 +155,23 @@ TEST(PropertyTest, OwnedProperty) { { PropertyHandler h; - EXPECT_EQ(NULL, h.GetProperty(kOwnedKey)); + EXPECT_EQ(nullptr, h.GetProperty(kOwnedKey)); void* last_deleted = TestProperty::last_deleted(); - TestProperty* p1 = new TestProperty(); - h.SetProperty(kOwnedKey, p1); + TestProperty* p1 = + h.SetProperty(kOwnedKey, std::make_unique<TestProperty>()); EXPECT_EQ(p1, h.GetProperty(kOwnedKey)); EXPECT_EQ(last_deleted, TestProperty::last_deleted()); - TestProperty* p2 = new TestProperty(); - h.SetProperty(kOwnedKey, p2); + TestProperty* p2 = + h.SetProperty(kOwnedKey, std::make_unique<TestProperty>()); EXPECT_EQ(p2, h.GetProperty(kOwnedKey)); EXPECT_EQ(p1, TestProperty::last_deleted()); h.ClearProperty(kOwnedKey); - EXPECT_EQ(NULL, h.GetProperty(kOwnedKey)); + EXPECT_EQ(nullptr, h.GetProperty(kOwnedKey)); EXPECT_EQ(p2, TestProperty::last_deleted()); - p3 = new TestProperty(); - h.SetProperty(kOwnedKey, p3); + p3 = h.SetProperty(kOwnedKey, std::make_unique<TestProperty>()); EXPECT_EQ(p3, h.GetProperty(kOwnedKey)); EXPECT_EQ(p2, TestProperty::last_deleted()); } @@ -162,8 +183,8 @@ TEST(PropertyTest, AcquireAllPropertiesFrom) { std::unique_ptr<PropertyHandler> src = std::make_unique<PropertyHandler>(); void* last_deleted = TestProperty::last_deleted(); EXPECT_FALSE(src->GetProperty(kOwnedKey)); - TestProperty* p1 = new TestProperty(); - src->SetProperty(kOwnedKey, p1); + TestProperty* p1 = + src->SetProperty(kOwnedKey, std::make_unique<TestProperty>()); src->SetProperty(kIntKey, INT_MAX); // dest will take ownership of the owned property. Existing properties with @@ -288,13 +309,42 @@ TEST(PropertyTest, PropertyChangedEvent) { EXPECT_EQ(4, h.num_events()); // Verify that setting a heap-allocated value also ticks the event counter. - h.SetProperty(kAssignableKey, new AssignableTestProperty{4}); + h.SetProperty(kAssignableKey, std::make_unique<AssignableTestProperty>(4)); EXPECT_EQ(5, h.num_events()); // Verify that overwriting a heap-allocated value ticks the event counter. - h.SetProperty(kAssignableKey, new AssignableTestProperty{5}); + h.SetProperty(kAssignableKey, std::make_unique<AssignableTestProperty>(5)); EXPECT_EQ(6, h.num_events()); } +TEST(PropertyTest, CascadingProperties) { + TestPropertyHandler h; + TestPropertyHandler h2(&h); + + // Set the property on the parent handler. + h.SetProperty(kCascadingOwnedKey, + std::make_unique<TestCascadingProperty>(&h)); + + // Get the property value from the child handler. + auto* value = h2.GetProperty(kCascadingOwnedKey); + + EXPECT_TRUE(value); + // The property value should have a reference to |h|. + EXPECT_EQ(&h, value->handler()); +} + +// TODO(kylixrd, pbos): Once all the call-sites are fixed to only use the +// unique_ptr version for owned properties, enable the following test to ensure +// that passing raw pointers for owned properties will, in fact, DCHECK. +TEST(PropertyTest, DISABLED_CheckedOwnedProperties) { + PropertyHandler h; + + EXPECT_EQ(nullptr, h.GetProperty(kOwnedKey)); + // The following SetProperty call should DCHECK if it's enabled. NOTE: This + // will leak the TestProperty! + EXPECT_DEATH_IF_SUPPORTED(h.SetProperty(kOwnedKey, new TestProperty()), ""); + EXPECT_EQ(nullptr, h.GetProperty(kOwnedKey)); +} + } // namespace test } // namespace ui diff --git a/chromium/ui/base/clipboard/BUILD.gn b/chromium/ui/base/clipboard/BUILD.gn index 42d2fb48145..52d88ba0ffb 100644 --- a/chromium/ui/base/clipboard/BUILD.gn +++ b/chromium/ui/base/clipboard/BUILD.gn @@ -78,6 +78,7 @@ component("clipboard") { "clipboard_monitor.h", "clipboard_observer.cc", "clipboard_observer.h", + "clipboard_sequence_number_token.h", "custom_data_helper.cc", "custom_data_helper.h", "scoped_clipboard_writer.cc", diff --git a/chromium/ui/base/clipboard/DIR_METADATA b/chromium/ui/base/clipboard/DIR_METADATA index 185817f957c..7d0a14efdc4 100644 --- a/chromium/ui/base/clipboard/DIR_METADATA +++ b/chromium/ui/base/clipboard/DIR_METADATA @@ -1,10 +1,10 @@ # Metadata information for this directory. # # For more information on DIR_METADATA files, see: -# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md +# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/README.md # # For the schema of this file, see Metadata message: -# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto +# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto monorail { component: "Blink>DataTransfer" diff --git a/chromium/ui/base/clipboard/OWNERS b/chromium/ui/base/clipboard/OWNERS index 3520663eec7..894511c6deb 100644 --- a/chromium/ui/base/clipboard/OWNERS +++ b/chromium/ui/base/clipboard/OWNERS @@ -1,7 +1,8 @@ +asully@chromium.org dcheng@chromium.org -huangdarwin@chromium.org +per-file clipboard_*android*=gangwu@chromium.org +per-file clipboard_*mac*=avi@chromium.org per-file clipboard_*ozone*=msisov@igalia.com per-file clipboard_*ozone*=adunaev@igalia.com per-file clipboard_*x11*=thomasanderson@chromium.org - diff --git a/chromium/ui/base/clipboard/README.md b/chromium/ui/base/clipboard/README.md index 51d1a61b624..154b208122d 100644 --- a/chromium/ui/base/clipboard/README.md +++ b/chromium/ui/base/clipboard/README.md @@ -1,18 +1,43 @@ +# Browser Clipboard + Platform-neutral clipboard abstractions, to access platform-specific clipboards (copy/paste) without platform-specific code. +## Clipboard Model + +The clipboard can be thought of as an ordered dictionary keyed on format, with +the value as the payload. This dictionary is implemented and accessed +differently on different operating systems (OS’s). + +## Interfaces + Interfaces include: * `Clipboard`: reading/pasting from the clipboard. * `ScopedClipboardWriter`: writing/copying to the clipboard. * `ClipboardObserver`: notifications of clipboard events. * `ClipboardFormatType`: specifying clipboard formats. +## Platform-specific behavior + While most platform-specific behavior should be abstracted away, some may still be exposed. For some notable platform-specific behavior exposed by these interfaces: * `ClipboardAndroid` has a more limited set of supported formats. * `ClipboardObserver` is only supported on some platforms, as other platforms may require (inefficient) polling to implement. -* `ClipboardX11` supports both the usual clipboard buffer, as well as the - selection (middle-click) paste buffer. +* Every platform may have different combinations of clipboard formats written, + or metadata written, for each clipboard format. For example, text in Windows + is written with a carriage return accompanying newlines, and in Linux requires + multiple MIME types to represent. +* `ClipboardX11` supports both the usual clipboard buffer (CLIPBOARD selection), + as well as the middle-click paste buffer (PRIMARY selection). X11 selections + are documented in more detail in + [X11 documentation](https://tronche.com/gui/x/icccm/sec-2.html#s-2.6.1). * `DataTransferPolicyController` is only currently exercised in ChromeOS. +* `ClipboardWin` and `ClipboardX11` have limits to the amount of registered + clipboard formats. Windows has the smallest limit on the number of formats, at + [16384](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-registerclipboardformata#remarks), + and explored in + [this article](https://devblogs.microsoft.com/oldnewthing/20150319-00/?p=44433). + After these system resources are exhausted, the underlying OS may be rendered + unusable.
\ No newline at end of file diff --git a/chromium/ui/base/clipboard/clipboard.cc b/chromium/ui/base/clipboard/clipboard.cc index e5aa1deb231..1916e5c82b9 100644 --- a/chromium/ui/base/clipboard/clipboard.cc +++ b/chromium/ui/base/clipboard/clipboard.cc @@ -10,6 +10,7 @@ #include "base/check.h" #include "base/containers/contains.h" +#include "base/json/json_reader.h" #include "base/memory/ptr_util.h" #include "base/notreached.h" #include "third_party/skia/include/core/SkBitmap.h" @@ -137,6 +138,33 @@ base::Time Clipboard::GetLastModifiedTime() const { void Clipboard::ClearLastModifiedTime() {} +std::map<std::string, std::string> Clipboard::ExtractCustomPlatformNames( + ClipboardBuffer buffer, + const DataTransferEndpoint* data_dst) const { + // Read the JSON metadata payload. + std::map<std::string, std::string> custom_format_names; + if (IsFormatAvailable(ui::ClipboardFormatType::WebCustomFormatMap(), buffer, + data_dst)) { + std::string custom_format_json; + // Read the custom format map. + ReadData(ui::ClipboardFormatType::WebCustomFormatMap(), data_dst, + &custom_format_json); + if (!custom_format_json.empty()) { + absl::optional<base::Value> json_val = + base::JSONReader::Read(custom_format_json); + if (json_val.has_value()) { + for (const auto it : json_val->DictItems()) { + std::string custom_format_name; + if (it.second.GetAsString(&custom_format_name)) { + custom_format_names.emplace(it.first, custom_format_name); + } + } + } + } + } + return custom_format_names; +} + Clipboard::Clipboard() = default; Clipboard::~Clipboard() = default; @@ -203,6 +231,11 @@ void Clipboard::DispatchPortableRepresentation(PortableFormat format, &(params[1].front()), params[1].size()); break; + case PortableFormat::kWebCustomFormatMap: + WriteData(ClipboardFormatType::WebCustomFormatMap(), + &(params[0].front()), params[0].size()); + break; + default: NOTREACHED(); } @@ -211,7 +244,7 @@ void Clipboard::DispatchPortableRepresentation(PortableFormat format, void Clipboard::DispatchPlatformRepresentations( std::vector<Clipboard::PlatformRepresentation> platform_representations) { for (const auto& representation : platform_representations) { - WriteData(ClipboardFormatType::GetType(representation.format), + WriteData(ClipboardFormatType::CustomPlatformType(representation.format), reinterpret_cast<const char*>(representation.data.data()), representation.data.size()); } diff --git a/chromium/ui/base/clipboard/clipboard.h b/chromium/ui/base/clipboard/clipboard.h index 11ce126c3d8..85009c942b8 100644 --- a/chromium/ui/base/clipboard/clipboard.h +++ b/chromium/ui/base/clipboard/clipboard.h @@ -8,6 +8,7 @@ #include <stddef.h> #include <stdint.h> +#include <map> #include <memory> #include <string> #include <vector> @@ -16,8 +17,6 @@ #include "base/compiler_specific.h" #include "base/component_export.h" #include "base/containers/flat_map.h" -#include "base/macros.h" -#include "base/no_destructor.h" #include "base/process/process.h" #include "base/synchronization/lock.h" #include "base/threading/platform_thread.h" @@ -27,6 +26,7 @@ #include "mojo/public/cpp/base/big_buffer.h" #include "ui/base/clipboard/clipboard_buffer.h" #include "ui/base/clipboard/clipboard_format_type.h" +#include "ui/base/clipboard/clipboard_sequence_number_token.h" #include "ui/base/clipboard/file_info.h" #include "ui/base/data_transfer_policy/data_transfer_endpoint.h" @@ -69,6 +69,9 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) Clipboard base::OnceCallback<void(std::u16string title, GURL url)>; using ReadDataCallback = base::OnceCallback<void(std::string result)>; + Clipboard(const Clipboard&) = delete; + Clipboard& operator=(const Clipboard&) = delete; + static bool IsSupportedClipboardBuffer(ClipboardBuffer buffer); // Sets the list of threads that are allowed to access the clipboard. @@ -90,6 +93,8 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) Clipboard // the main UI thread, but Windows has tricky semantics where there have to // be two clipboards: one that lives on the UI thread and one that lives on // the IO thread. + // + // The return value should not be cached. static Clipboard* GetForCurrentThread(); // Removes and transfers ownership of the current thread's clipboard to the @@ -114,10 +119,11 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) Clipboard virtual const DataTransferEndpoint* GetSource( ClipboardBuffer buffer) const = 0; - // Returns a sequence number which uniquely identifies clipboard state. - // This can be used to version the data on the clipboard and determine - // whether it has changed. - virtual uint64_t GetSequenceNumber(ClipboardBuffer buffer) const = 0; + // Returns a token which uniquely identifies clipboard state. + // ClipboardSequenceNumberTokens are used since there may be multiple + // ui::Clipboard instances that have the same sequence number. + virtual const ClipboardSequenceNumberToken& GetSequenceNumber( + ClipboardBuffer buffer) const = 0; // Tests whether the clipboard contains a certain format. virtual bool IsFormatAvailable( @@ -207,7 +213,7 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) Clipboard virtual void ReadBookmark(const DataTransferEndpoint* data_dst, ReadBookmarkCallback callback) const; - // Reads raw data from the clipboard with the given format type. Stores result + // Reads data from the clipboard with the given format type. Stores result // as a byte vector. virtual void ReadData(const ClipboardFormatType& format, const DataTransferEndpoint* data_dst, @@ -260,13 +266,22 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) Clipboard // Resets the clipboard last modified time to Time::Time(). virtual void ClearLastModifiedTime(); + // Reads the web custom format map (which is in JSON format) from the + // clipboard if it's available. Parses the JSON string that has the mapping of + // MIME type to custom format name and fetches the list of custom MIME types. + // e.g. on Windows, the mapping is represented as "text/html":"Web Custom + // Format(0-99)". + std::map<std::string, std::string> ExtractCustomPlatformNames( + ClipboardBuffer buffer, + const DataTransferEndpoint* data_dst) const; + protected: // PortableFormat designates the type of data to be stored in the clipboard. // This designation is shared across all OSes. The system-specific designation // is defined by ClipboardFormatType. A single PortableFormat might be // represented by several system-specific ClipboardFormatTypes. For example, // on Linux the kText PortableFormat maps to "text/plain", "STRING", and - // several other formats. On windows it maps to CF_UNICODETEXT. + // several other formats. On Windows it maps to CF_UNICODETEXT. // // The order below is the order in which data will be written to the // clipboard, so more specific types must be listed before less specific @@ -274,6 +289,8 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) Clipboard // clipboard to contain a bitmap, HTML markup representing the image, a URL to // the image, and the image's alt text. Having the types follow this order // maximizes the amount of data that can be extracted by various programs. + // Documentation on motivation for format ordering is also available here: + // https://docs.microsoft.com/en-us/windows/win32/dataxchg/clipboard-formats#multiple-clipboard-formats enum class PortableFormat { kBitmap, // Bitmap from shared memory. kHtml, @@ -284,6 +301,7 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) Clipboard kData, // Arbitrary block of bytes. kSvg, kFilenames, + kWebCustomFormatMap, }; // TODO (https://crbug.com/994928): Rename ObjectMap-related types. @@ -307,6 +325,7 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) Clipboard // kWebkit none empty vector // kData format char array // data byte array + // kWebCustomFormatMap char array using ObjectMapParam = std::vector<char>; using ObjectMapParams = std::vector<ObjectMapParam>; using ObjectMap = base::flat_map<PortableFormat, ObjectMapParams>; @@ -328,22 +347,14 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) Clipboard Clipboard(); virtual ~Clipboard(); - // Write a bunch of objects to the system clipboard. Copies are made of the - // contents of |objects|. Also, adds the source of the data to the clipboard, - // which can be used when we need to restrict the clipboard data between a set - // of confidential documents. The data source maybe passed as nullptr. - virtual void WritePortableRepresentations( + // Write platform & portable formats, in the order of their appearance in + // `platform_representations` & `ObjectMap`. Also, adds the source of the data + // to the clipboard, which can be used when we need to restrict the clipboard + // data between a set of confidential documents. The data source maybe passed + // as nullptr. + virtual void WritePortableAndPlatformRepresentations( ClipboardBuffer buffer, const ObjectMap& objects, - std::unique_ptr<DataTransferEndpoint> data_src) = 0; - - // Write |platform_representations|, in the order of their appearance in - // |platform_representations|. Also, adds the source of the data to the - // clipboard, which can be used when we need to restrict the clipboard data - // between a set of confidential documents. The data source maybe passed as - // nullptr. - virtual void WritePlatformRepresentations( - ClipboardBuffer buffer, std::vector<Clipboard::PlatformRepresentation> platform_representations, std::unique_ptr<DataTransferEndpoint> data_src) = 0; @@ -411,8 +422,6 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) Clipboard // Mutex that controls access to |g_clipboard_map|. static base::Lock& ClipboardMapLock(); - - DISALLOW_COPY_AND_ASSIGN(Clipboard); }; } // namespace ui diff --git a/chromium/ui/base/clipboard/clipboard_android.cc b/chromium/ui/base/clipboard/clipboard_android.cc index 62e6ef76ac2..3460fd1d40e 100644 --- a/chromium/ui/base/clipboard/clipboard_android.cc +++ b/chromium/ui/base/clipboard/clipboard_android.cc @@ -5,6 +5,7 @@ #include "ui/base/clipboard/clipboard_android.h" #include <algorithm> +#include <cstdint> #include <map> #include <string> #include <utility> @@ -32,6 +33,7 @@ #include "ui/base/data_transfer_policy/data_transfer_endpoint.h" #include "ui/base/ui_base_jni_headers/Clipboard_jni.h" #include "ui/gfx/android/java_bitmap.h" +#include "ui/gfx/codec/png_codec.h" #include "ui/gfx/image/image.h" // TODO:(andrewhayden) Support additional formats in Android: URI, HTML, @@ -67,6 +69,22 @@ constexpr char kPngExtension[] = ".png"; using ReadPngCallback = ClipboardAndroid::ReadPngCallback; using ReadImageCallback = ClipboardAndroid::ReadImageCallback; +// Fetching image data from Java as PNG bytes. +std::vector<uint8_t> GetPngData( + const base::android::ScopedJavaGlobalRef<jobject>& clipboard_manager) { + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef<jbyteArray> jimage_data = + Java_Clipboard_getPng(env, clipboard_manager); + if (jimage_data.is_null()) { + return std::vector<uint8_t>(); + } + DCHECK(jimage_data.obj()); + + std::vector<uint8_t> png_data; + JavaByteArrayToByteVector(env, jimage_data, &png_data); + return png_data; +} + // Fetching image data from Java. SkBitmap GetImageData( const base::android::ScopedJavaGlobalRef<jobject>& clipboard_manager) { @@ -86,14 +104,36 @@ SkBitmap GetImageData( return gfx::CreateSkBitmapFromJavaBitmap(java_bitmap); } +// Add a format:jstr pair to map, if jstr is null or is empty, then remove that +// entry. +void JNI_Clipboard_AddMapEntry(JNIEnv* env, + std::map<ClipboardFormatType, std::string>* map, + const ClipboardFormatType& format, + const ScopedJavaLocalRef<jstring>& jstr) { + if (jstr.is_null()) { + map->erase(format); + return; + } + + std::string str = ConvertJavaStringToUTF8(env, jstr.obj()); + if (!str.empty()) { + (*map)[format] = str; + } else { + map->erase(format); + } +} + class ClipboardMap { public: ClipboardMap(); void SetModifiedCallback(ClipboardAndroid::ModifiedCallback cb); void SetJavaSideNativePtr(Clipboard* clipboard); std::string Get(const ClipboardFormatType& format); + void GetPng(ReadPngCallback callback); void GetImage(ReadImageCallback callback); - uint64_t GetSequenceNumber() const; + void DidGetPng(ReadPngCallback callback, std::vector<uint8_t> result); + void DidGetImage(ReadImageCallback callback, const SkBitmap& result); + const ClipboardSequenceNumberToken& GetSequenceNumber() const; base::Time GetLastModifiedTime() const; void ClearLastModifiedTime(); bool HasFormat(const ClipboardFormatType& format); @@ -130,7 +170,7 @@ class ClipboardMap { // This lock is for read/write |map_|. base::Lock lock_; - uint64_t sequence_number_; + ClipboardSequenceNumberToken sequence_number_; base::Time last_modified_time_; ClipboardAndroid::ModifiedCallback modified_cb_; @@ -162,13 +202,65 @@ std::string ClipboardMap::Get(const ClipboardFormatType& format) { return it == map_.end() ? std::string() : it->second; } +void ClipboardMap::GetPng(ReadPngCallback callback) { + base::ThreadPool::PostTaskAndReplyWithResult( + FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_BLOCKING}, + base::BindOnce(&GetPngData, clipboard_manager_), + base::BindOnce(&ClipboardMap::DidGetPng, base::Unretained(this), + std::move(callback))); +} + void ClipboardMap::GetImage(ReadImageCallback callback) { base::ThreadPool::PostTaskAndReplyWithResult( FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_BLOCKING}, - base::BindOnce(&GetImageData, clipboard_manager_), std::move(callback)); + base::BindOnce(&GetImageData, clipboard_manager_), + base::BindOnce(&ClipboardMap::DidGetImage, base::Unretained(this), + std::move(callback))); +} + +void ClipboardMap::DidGetPng(ReadPngCallback callback, + std::vector<uint8_t> result) { + // GetPngData attempts to read from the Java Clipboard, which sometimes is + // not available (ex. the app is not in focus, such as in unit tests). + if (!result.empty()) { + std::move(callback).Run(std::move(result)); + return; + } + + // Since the The Java Clipboard did not provide a valid bitmap, attempt to + // read from our in-memory clipboard map if the map is up-to-date. + if (map_state_ != MapState::kUpToDate) { + std::move(callback).Run(std::vector<uint8_t>()); + return; + } + std::string png_str = g_map.Get().Get(ClipboardFormatType::PngType()); + std::vector<uint8_t> png_data{png_str.begin(), png_str.end()}; + std::move(callback).Run(png_data); +} + +void ClipboardMap::DidGetImage(ReadImageCallback callback, + const SkBitmap& result) { + // GetImageData attempts to read from the Java Clipboard, which sometimes is + // not available (ex. the app is not in focus, such as in unit tests). + if (!result.isNull()) { + std::move(callback).Run(std::move(result)); + return; + } + + // Since the The Java Clipboard did not provide a valid bitmap, attempt to + // read from our in-memory clipboard map if the map is up-to-date. + if (map_state_ != MapState::kUpToDate) { + std::move(callback).Run(SkBitmap()); + return; + } + std::string png_str = g_map.Get().Get(ClipboardFormatType::PngType()); + SkBitmap bitmap; + gfx::PNGCodec::Decode(reinterpret_cast<const unsigned char*>(png_str.data()), + png_str.size(), &bitmap); + std::move(callback).Run(std::move(bitmap)); } -uint64_t ClipboardMap::GetSequenceNumber() const { +const ClipboardSequenceNumberToken& ClipboardMap::GetSequenceNumber() const { return sequence_number_; } @@ -184,6 +276,12 @@ bool ClipboardMap::HasFormat(const ClipboardFormatType& format) { base::AutoLock lock(lock_); if (map_state_ == MapState::kUpToDate) { // If the 'map_' is up to date, we can just check with it. + // Images can be read if either bitmap or PNG types are available. + if (format == ClipboardFormatType::PngType() || + format == ClipboardFormatType::BitmapType()) { + return base::Contains(map_, ClipboardFormatType::PngType()) || + base::Contains(map_, ClipboardFormatType::BitmapType()); + } return base::Contains(map_, format); } @@ -195,13 +293,14 @@ bool ClipboardMap::HasFormat(const ClipboardFormatType& format) { // up. JNIEnv* env = AttachCurrentThread(); // TODO(crbug.com/1194601): Create a single method for the follow JNI calls. - if (format == ClipboardFormatType::GetPlainTextType()) { + if (format == ClipboardFormatType::PlainTextType()) { return Java_Clipboard_hasCoercedText(env, clipboard_manager_); - } else if (format == ClipboardFormatType::GetHtmlType()) { + } else if (format == ClipboardFormatType::HtmlType()) { return Java_Clipboard_hasHTMLOrStyledText(env, clipboard_manager_); - } else if (format == ClipboardFormatType::GetUrlType()) { + } else if (format == ClipboardFormatType::UrlType()) { return Java_Clipboard_hasUrl(env, clipboard_manager_); - } else if (format == ClipboardFormatType::GetType(kMimeTypeImageURI)) { + } else if (format == ClipboardFormatType::PngType() || + format == ClipboardFormatType::BitmapType()) { return Java_Clipboard_hasImage(env, clipboard_manager_); } @@ -218,16 +317,17 @@ std::vector<ClipboardFormatType> ClipboardMap::GetFormats() { if (map_state_ != MapState::kUpToDate) { JNIEnv* env = AttachCurrentThread(); if (Java_Clipboard_hasCoercedText(env, clipboard_manager_)) { - formats.push_back(ClipboardFormatType::GetPlainTextType()); + formats.push_back(ClipboardFormatType::PlainTextType()); } if (Java_Clipboard_hasHTMLOrStyledText(env, clipboard_manager_)) { - formats.push_back(ClipboardFormatType::GetHtmlType()); + formats.push_back(ClipboardFormatType::HtmlType()); } if (Java_Clipboard_hasUrl(env, clipboard_manager_)) { - formats.push_back(ClipboardFormatType::GetUrlType()); + formats.push_back(ClipboardFormatType::UrlType()); } if (Java_Clipboard_hasImage(env, clipboard_manager_)) { - formats.push_back(ClipboardFormatType::GetBitmapType()); + formats.push_back(ClipboardFormatType::BitmapType()); + formats.push_back(ClipboardFormatType::PngType()); } } @@ -235,10 +335,11 @@ std::vector<ClipboardFormatType> ClipboardMap::GetFormats() { // not synced on any other layer. for (const auto& it : map_) { if (map_state_ != MapState::kUpToDate && - (it.first == ClipboardFormatType::GetPlainTextType() || - it.first == ClipboardFormatType::GetHtmlType() || - it.first == ClipboardFormatType::GetUrlType() || - it.first == ClipboardFormatType::GetType(kMimeTypeImageURI))) { + (it.first == ClipboardFormatType::PlainTextType() || + it.first == ClipboardFormatType::HtmlType() || + it.first == ClipboardFormatType::UrlType() || + it.first == ClipboardFormatType::BitmapType() || + it.first == ClipboardFormatType::PngType())) { continue; } formats.push_back(it.first); @@ -248,7 +349,7 @@ std::vector<ClipboardFormatType> ClipboardMap::GetFormats() { } void ClipboardMap::OnPrimaryClipboardChanged() { - sequence_number_++; + sequence_number_ = ClipboardSequenceNumberToken(); UpdateLastModifiedTime(base::Time::Now()); map_state_ = MapState::kOutOfDate; } @@ -256,7 +357,7 @@ void ClipboardMap::OnPrimaryClipboardChanged() { void ClipboardMap::OnPrimaryClipTimestampInvalidated(int64_t timestamp_ms) { base::Time timestamp = base::Time::FromJavaTime(timestamp_ms); if (GetLastModifiedTime() < timestamp) { - sequence_number_++; + sequence_number_ = ClipboardSequenceNumberToken(); UpdateLastModifiedTime(timestamp); map_state_ = MapState::kOutOfDate; } @@ -272,39 +373,46 @@ void ClipboardMap::Set(const ClipboardFormatType& format, void ClipboardMap::CommitToAndroidClipboard() { JNIEnv* env = AttachCurrentThread(); base::AutoLock lock(lock_); - if (base::Contains(map_, ClipboardFormatType::GetHtmlType())) { + if (base::Contains(map_, ClipboardFormatType::HtmlType())) { // Android's API for storing HTML content on the clipboard requires a plain- // text representation to be available as well. - if (!base::Contains(map_, ClipboardFormatType::GetPlainTextType())) + if (!base::Contains(map_, ClipboardFormatType::PlainTextType())) return; ScopedJavaLocalRef<jstring> html = - ConvertUTF8ToJavaString(env, map_[ClipboardFormatType::GetHtmlType()]); + ConvertUTF8ToJavaString(env, map_[ClipboardFormatType::HtmlType()]); ScopedJavaLocalRef<jstring> text = ConvertUTF8ToJavaString( - env, map_[ClipboardFormatType::GetPlainTextType()]); + env, map_[ClipboardFormatType::PlainTextType()]); DCHECK(html.obj() && text.obj()); Java_Clipboard_setHTMLText(env, clipboard_manager_, html, text); - } else if (base::Contains(map_, ClipboardFormatType::GetPlainTextType())) { + } else if (base::Contains(map_, ClipboardFormatType::PlainTextType())) { ScopedJavaLocalRef<jstring> str = ConvertUTF8ToJavaString( - env, map_[ClipboardFormatType::GetPlainTextType()]); + env, map_[ClipboardFormatType::PlainTextType()]); DCHECK(str.obj()); Java_Clipboard_setText(env, clipboard_manager_, str); - } else if (base::Contains(map_, ClipboardFormatType::GetBitmapType())) { + } else if (base::Contains(map_, ClipboardFormatType::PngType())) { + // Committing the PNG data to the Android clipboard will create an image + // with a corresponding URI. Once this has been created, update the local + // clipboard with this URI. ScopedJavaLocalRef<jbyteArray> image_data = - ToJavaByteArray(env, map_[ClipboardFormatType::GetBitmapType()]); + ToJavaByteArray(env, map_[ClipboardFormatType::PngType()]); ScopedJavaLocalRef<jstring> image_extension = ConvertUTF8ToJavaString(env, kPngExtension); DCHECK(image_data.obj()); + // TODO(crbug.com/1223215) In unit tests, `jimageuri` is empty. Java_Clipboard_setImage(env, clipboard_manager_, image_data, image_extension); + ScopedJavaLocalRef<jstring> jimageuri = + Java_Clipboard_getImageUriString(env, clipboard_manager_); + JNI_Clipboard_AddMapEntry(env, &map_, ClipboardFormatType::BitmapType(), + jimageuri); } else { Java_Clipboard_clear(env, clipboard_manager_); - // TODO(huangdarwin): Implement raw clipboard support for arbitrary formats. NOTIMPLEMENTED(); } map_state_ = MapState::kUpToDate; - sequence_number_++; + sequence_number_ = ClipboardSequenceNumberToken(); UpdateLastModifiedTime(base::Time::Now()); } @@ -314,7 +422,7 @@ void ClipboardMap::Clear() { map_.clear(); Java_Clipboard_clear(env, clipboard_manager_); map_state_ = MapState::kUpToDate; - sequence_number_++; + sequence_number_ = ClipboardSequenceNumberToken(); UpdateLastModifiedTime(base::Time::Now()); } @@ -322,25 +430,6 @@ void ClipboardMap::SetLastModifiedTimeWithoutRunningCallback(base::Time time) { last_modified_time_ = time; } -// Add a format:jstr pair to map, if jstr is null or is empty, then remove that -// entry. -void JNI_Clipboard_AddMapEntry(JNIEnv* env, - std::map<ClipboardFormatType, std::string>* map, - const ClipboardFormatType& format, - const ScopedJavaLocalRef<jstring>& jstr) { - if (jstr.is_null()) { - map->erase(format); - return; - } - - std::string str = ConvertJavaStringToUTF8(env, jstr.obj()); - if (!str.empty()) { - (*map)[format] = str; - } else { - map->erase(format); - } -} - void ClipboardMap::UpdateLastModifiedTime(base::Time time) { last_modified_time_ = time; // |modified_cb_| may be null in tests. @@ -366,14 +455,12 @@ void ClipboardMap::UpdateFromAndroidClipboard() { ScopedJavaLocalRef<jstring> jimageuri = Java_Clipboard_getImageUriString(env, clipboard_manager_); - JNI_Clipboard_AddMapEntry(env, &map_, ClipboardFormatType::GetPlainTextType(), + JNI_Clipboard_AddMapEntry(env, &map_, ClipboardFormatType::PlainTextType(), jtext); - JNI_Clipboard_AddMapEntry(env, &map_, ClipboardFormatType::GetHtmlType(), - jhtml); - JNI_Clipboard_AddMapEntry(env, &map_, ClipboardFormatType::GetUrlType(), - jurl); - JNI_Clipboard_AddMapEntry( - env, &map_, ClipboardFormatType::GetType(kMimeTypeImageURI), jimageuri); + JNI_Clipboard_AddMapEntry(env, &map_, ClipboardFormatType::HtmlType(), jhtml); + JNI_Clipboard_AddMapEntry(env, &map_, ClipboardFormatType::UrlType(), jurl); + JNI_Clipboard_AddMapEntry(env, &map_, ClipboardFormatType::BitmapType(), + jimageuri); map_state_ = MapState::kUpToDate; } @@ -433,7 +520,7 @@ DataTransferEndpoint* ClipboardAndroid::GetSource( return nullptr; } -uint64_t ClipboardAndroid::GetSequenceNumber( +const ClipboardSequenceNumberToken& ClipboardAndroid::GetSequenceNumber( ClipboardBuffer /* buffer */) const { DCHECK(CalledOnValidThread()); return g_map.Get().GetSequenceNumber(); @@ -447,11 +534,6 @@ bool ClipboardAndroid::IsFormatAvailable( const DataTransferEndpoint* data_dst) const { DCHECK(CalledOnValidThread()); DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste); - - if (format == ClipboardFormatType::GetBitmapType()) { - return g_map.Get().HasFormat( - ClipboardFormatType::GetType(kMimeTypeImageURI)); - } return g_map.Get().HasFormat(format); } @@ -475,17 +557,20 @@ void ClipboardAndroid::ReadAvailableTypes( // would be nice to ask the ClipboardMap to enumerate the types it supports, // rather than hardcode the list here. - if (IsFormatAvailable(ClipboardFormatType::GetPlainTextType(), buffer, - data_dst)) + if (IsFormatAvailable(ClipboardFormatType::PlainTextType(), buffer, data_dst)) types->push_back(base::UTF8ToUTF16(kMimeTypeText)); - if (IsFormatAvailable(ClipboardFormatType::GetHtmlType(), buffer, data_dst)) + if (IsFormatAvailable(ClipboardFormatType::HtmlType(), buffer, data_dst)) types->push_back(base::UTF8ToUTF16(kMimeTypeHTML)); - if (IsFormatAvailable(ClipboardFormatType::GetBitmapType(), buffer, data_dst)) + // We can read images from either the Android clipboard or the local map. + if (IsFormatAvailable(ClipboardFormatType::BitmapType(), buffer, data_dst) || + IsFormatAvailable(ClipboardFormatType::PngType(), buffer, data_dst)) { + types->push_back(base::UTF8ToUTF16(kMimeTypeImageURI)); types->push_back(base::UTF8ToUTF16(kMimeTypePNG)); + } // these formats aren't supported by the ClipboardMap currently, but might // be one day? - if (IsFormatAvailable(ClipboardFormatType::GetRtfType(), buffer, data_dst)) + if (IsFormatAvailable(ClipboardFormatType::RtfType(), buffer, data_dst)) types->push_back(base::UTF8ToUTF16(kMimeTypeRTF)); } @@ -526,7 +611,7 @@ void ClipboardAndroid::ReadAsciiText(ClipboardBuffer buffer, DCHECK(CalledOnValidThread()); DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste); RecordRead(ClipboardFormatMetric::kText); - *result = g_map.Get().Get(ClipboardFormatType::GetPlainTextType()); + *result = g_map.Get().Get(ClipboardFormatType::PlainTextType()); } // |src_url| isn't really used. It is only implemented in Windows. @@ -544,7 +629,7 @@ void ClipboardAndroid::ReadHTML(ClipboardBuffer buffer, if (src_url) src_url->clear(); - std::string input = g_map.Get().Get(ClipboardFormatType::GetHtmlType()); + std::string input = g_map.Get().Get(ClipboardFormatType::HtmlType()); *markup = base::UTF8ToUTF16(input); *fragment_start = 0; @@ -558,7 +643,7 @@ void ClipboardAndroid::ReadSvg(ClipboardBuffer buffer, std::u16string* result) const { DCHECK(CalledOnValidThread()); DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste); - std::string utf8 = g_map.Get().Get(ClipboardFormatType::GetSvgType()); + std::string utf8 = g_map.Get().Get(ClipboardFormatType::SvgType()); *result = base::UTF8ToUTF16(utf8); } @@ -577,8 +662,9 @@ void ClipboardAndroid::ReadPng(ClipboardBuffer buffer, const DataTransferEndpoint* data_dst, ReadPngCallback callback) const { DCHECK(CalledOnValidThread()); - // TODO(crbug.com/1201018): Implement this. - NOTIMPLEMENTED(); + DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste); + RecordRead(ClipboardFormatMetric::kPng); + g_map.Get().GetPng(std::move(callback)); } // |data_dst| is not used. It's only passed to be consistent with other @@ -618,7 +704,7 @@ void ClipboardAndroid::ReadBookmark(const DataTransferEndpoint* data_dst, std::string* url) const { DCHECK(CalledOnValidThread()); RecordRead(ClipboardFormatMetric::kBookmark); - *url = g_map.Get().Get(ClipboardFormatType::GetUrlType()); + *url = g_map.Get().Get(ClipboardFormatType::UrlType()); } // |data_dst| is not used. It's only passed to be consistent with other @@ -644,24 +730,9 @@ void ClipboardAndroid::ClearLastModifiedTime() { // Main entry point used to write several values in the clipboard. // |data_src| is not used. It's only passed to be consistent with other // platforms. -void ClipboardAndroid::WritePortableRepresentations( +void ClipboardAndroid::WritePortableAndPlatformRepresentations( ClipboardBuffer buffer, const ObjectMap& objects, - std::unique_ptr<DataTransferEndpoint> data_src) { - DCHECK(CalledOnValidThread()); - DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste); - g_map.Get().Clear(); - - for (const auto& object : objects) - DispatchPortableRepresentation(object.first, object.second); - - g_map.Get().CommitToAndroidClipboard(); -} - -// |data_src| is not used. It's only passed to be consistent with other -// platforms. -void ClipboardAndroid::WritePlatformRepresentations( - ClipboardBuffer buffer, std::vector<Clipboard::PlatformRepresentation> platform_representations, std::unique_ptr<DataTransferEndpoint> data_src) { DCHECK(CalledOnValidThread()); @@ -669,12 +740,14 @@ void ClipboardAndroid::WritePlatformRepresentations( g_map.Get().Clear(); DispatchPlatformRepresentations(std::move(platform_representations)); + for (const auto& object : objects) + DispatchPortableRepresentation(object.first, object.second); g_map.Get().CommitToAndroidClipboard(); } void ClipboardAndroid::WriteText(const char* text_data, size_t text_len) { - g_map.Get().Set(ClipboardFormatType::GetPlainTextType(), + g_map.Get().Set(ClipboardFormatType::PlainTextType(), std::string(text_data, text_len)); } @@ -682,12 +755,12 @@ void ClipboardAndroid::WriteHTML(const char* markup_data, size_t markup_len, const char* url_data, size_t url_len) { - g_map.Get().Set(ClipboardFormatType::GetHtmlType(), + g_map.Get().Set(ClipboardFormatType::HtmlType(), std::string(markup_data, markup_len)); } void ClipboardAndroid::WriteSvg(const char* markup_data, size_t markup_len) { - g_map.Get().Set(ClipboardFormatType::GetSvgType(), + g_map.Get().Set(ClipboardFormatType::SvgType(), std::string(markup_data, markup_len)); } @@ -705,15 +778,14 @@ void ClipboardAndroid::WriteBookmark(const char* title_data, size_t title_len, const char* url_data, size_t url_len) { - g_map.Get().Set(ClipboardFormatType::GetUrlType(), + g_map.Get().Set(ClipboardFormatType::UrlType(), std::string(url_data, url_len)); } // Write an extra flavor that signifies WebKit was the last to modify the // pasteboard. This flavor has no data. void ClipboardAndroid::WriteWebSmartPaste() { - g_map.Get().Set(ClipboardFormatType::GetWebKitSmartPasteType(), - std::string()); + g_map.Get().Set(ClipboardFormatType::WebKitSmartPasteType(), std::string()); } // Encoding SkBitmap to PNG data. Then, |g_map| can commit the PNG data to @@ -723,7 +795,7 @@ void ClipboardAndroid::WriteBitmap(const SkBitmap& sk_bitmap) { gfx::Image::CreateFrom1xBitmap(sk_bitmap).As1xPNGBytes(); std::string packed(image_memory->front_as<char>(), image_memory->size()); - g_map.Get().Set(ClipboardFormatType::GetBitmapType(), packed); + g_map.Get().Set(ClipboardFormatType::PngType(), packed); } void ClipboardAndroid::WriteData(const ClipboardFormatType& format, diff --git a/chromium/ui/base/clipboard/clipboard_android.h b/chromium/ui/base/clipboard/clipboard_android.h index 870ee348b14..ee7865653c6 100644 --- a/chromium/ui/base/clipboard/clipboard_android.h +++ b/chromium/ui/base/clipboard/clipboard_android.h @@ -14,17 +14,21 @@ #include "base/android/scoped_java_ref.h" #include "base/callback_forward.h" #include "base/component_export.h" -#include "base/macros.h" #include "base/time/time.h" namespace ui { +// Documentation on the underlying Android API this ultimately abstracts is +// available at https://developer.android.com/guide/topics/text/copy-paste. class ClipboardAndroid : public Clipboard { public: // Callback called whenever the clipboard is modified. The parameter // represents the time of the modification. using ModifiedCallback = base::RepeatingCallback<void(base::Time)>; + ClipboardAndroid(const ClipboardAndroid&) = delete; + ClipboardAndroid& operator=(const ClipboardAndroid&) = delete; + // Called by Java when the Java Clipboard is notified that the clipboard has // changed. void OnPrimaryClipChanged(JNIEnv* env, @@ -59,7 +63,8 @@ class ClipboardAndroid : public Clipboard { // Clipboard overrides: void OnPreShutdown() override; DataTransferEndpoint* GetSource(ClipboardBuffer buffer) const override; - uint64_t GetSequenceNumber(ClipboardBuffer buffer) const override; + const ClipboardSequenceNumberToken& GetSequenceNumber( + ClipboardBuffer buffer) const override; bool IsFormatAvailable(const ClipboardFormatType& format, ClipboardBuffer buffer, const DataTransferEndpoint* data_dst) const override; @@ -109,12 +114,9 @@ class ClipboardAndroid : public Clipboard { std::string* result) const override; base::Time GetLastModifiedTime() const override; void ClearLastModifiedTime() override; - void WritePortableRepresentations( + void WritePortableAndPlatformRepresentations( ClipboardBuffer buffer, const ObjectMap& objects, - std::unique_ptr<DataTransferEndpoint> data_src) override; - void WritePlatformRepresentations( - ClipboardBuffer buffer, std::vector<Clipboard::PlatformRepresentation> platform_representations, std::unique_ptr<DataTransferEndpoint> data_src) override; void WriteText(const char* text_data, size_t text_len) override; @@ -134,8 +136,6 @@ class ClipboardAndroid : public Clipboard { void WriteData(const ClipboardFormatType& format, const char* data_data, size_t data_len) override; - - DISALLOW_COPY_AND_ASSIGN(ClipboardAndroid); }; } // namespace ui diff --git a/chromium/ui/base/clipboard/clipboard_android_test_support.cc b/chromium/ui/base/clipboard/clipboard_android_test_support.cc index b7f126afcfb..d821fdd28be 100644 --- a/chromium/ui/base/clipboard/clipboard_android_test_support.cc +++ b/chromium/ui/base/clipboard/clipboard_android_test_support.cc @@ -33,10 +33,10 @@ jboolean JNI_ClipboardAndroidTestSupport_NativeWriteHtml( clipboard_writer.WriteText(html_text); } auto* clipboard = Clipboard::GetForCurrentThread(); - return clipboard->IsFormatAvailable(ClipboardFormatType::GetHtmlType(), + return clipboard->IsFormatAvailable(ClipboardFormatType::HtmlType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr) && - clipboard->IsFormatAvailable(ClipboardFormatType::GetPlainTextType(), + clipboard->IsFormatAvailable(ClipboardFormatType::PlainTextType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr); } @@ -48,14 +48,14 @@ jboolean JNI_ClipboardAndroidTestSupport_NativeClipboardContains( // ClipboardManager. This should update the native side of the clipboard as // well as the Android side. auto* clipboard = Clipboard::GetForCurrentThread(); - if (clipboard->IsFormatAvailable(ClipboardFormatType::GetHtmlType(), + if (clipboard->IsFormatAvailable(ClipboardFormatType::HtmlType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)) { LOG(ERROR) << "HTML still in clipboard."; return false; } - if (!clipboard->IsFormatAvailable(ClipboardFormatType::GetPlainTextType(), + if (!clipboard->IsFormatAvailable(ClipboardFormatType::PlainTextType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)) { LOG(ERROR) << "Plain text not in clipboard."; diff --git a/chromium/ui/base/clipboard/clipboard_data.cc b/chromium/ui/base/clipboard/clipboard_data.cc index 0bc1e93ec74..f78d9f67b91 100644 --- a/chromium/ui/base/clipboard/clipboard_data.cc +++ b/chromium/ui/base/clipboard/clipboard_data.cc @@ -58,6 +58,31 @@ bool ClipboardData::operator!=(const ClipboardData& that) const { return !(*this == that); } +absl::optional<size_t> ClipboardData::size() const { + if (format_ & static_cast<int>(ClipboardInternalFormat::kFilenames)) + return absl::nullopt; + size_t total_size = 0; + if (format_ & static_cast<int>(ClipboardInternalFormat::kText)) + total_size += text_.size(); + if (format_ & static_cast<int>(ClipboardInternalFormat::kHtml)) { + total_size += markup_data_.size(); + total_size += url_.size(); + } + if (format_ & static_cast<int>(ClipboardInternalFormat::kSvg)) + total_size += svg_data_.size(); + if (format_ & static_cast<int>(ClipboardInternalFormat::kRtf)) + total_size += rtf_data_.size(); + if (format_ & static_cast<int>(ClipboardInternalFormat::kBookmark)) { + total_size += bookmark_title_.size(); + total_size += bookmark_url_.size(); + } + if (format_ & static_cast<int>(ClipboardInternalFormat::kPng)) + total_size += png_.size(); + if (format_ & static_cast<int>(ClipboardInternalFormat::kCustom)) + total_size += custom_data_data_.size(); + return total_size; +} + void ClipboardData::SetPngData(std::vector<uint8_t> png) { png_ = std::move(png); format_ |= static_cast<int>(ClipboardInternalFormat::kPng); diff --git a/chromium/ui/base/clipboard/clipboard_data.h b/chromium/ui/base/clipboard/clipboard_data.h index 3f4a550655a..3bf22dbe16a 100644 --- a/chromium/ui/base/clipboard/clipboard_data.h +++ b/chromium/ui/base/clipboard/clipboard_data.h @@ -9,6 +9,7 @@ #include <vector> #include "base/component_export.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/base/clipboard/file_info.h" #include "ui/base/data_transfer_policy/data_transfer_endpoint.h" @@ -46,6 +47,10 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) ClipboardData { // Bitmask of ClipboardInternalFormat types. int format() const { return format_; } + // Returns the total size of the data in clipboard, or absl::nullopt if it + // can't be determined. + absl::optional<size_t> size() const; + const std::string& text() const { return text_; } void set_text(const std::string& text) { text_ = text; diff --git a/chromium/ui/base/clipboard/clipboard_format_type.h b/chromium/ui/base/clipboard/clipboard_format_type.h index 03451964918..e3ca57b66ea 100644 --- a/chromium/ui/base/clipboard/clipboard_format_type.h +++ b/chromium/ui/base/clipboard/clipboard_format_type.h @@ -14,7 +14,7 @@ #include "build/build_config.h" #if defined(OS_WIN) -#include <objidl.h> +#include "base/win/windows_types.h" #endif #if defined(OS_APPLE) @@ -45,48 +45,57 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES) ClipboardFormatType { std::string Serialize() const; static ClipboardFormatType Deserialize(const std::string& serialization); - // Gets the ClipboardFormatType corresponding to an arbitrary format string, - // registering it with the system if needed. Due to Windows/Linux - // limitations, please place limits on the amount of GetType calls with unique - // |format_string| arguments, when ingesting |format_string| from - // untrusted sources, such as renderer processes. In Windows, a failure will - // return an invalid format with Deserialize()'ed value of "0". + // Gets the ClipboardFormatType corresponding to the standard formats. static ClipboardFormatType GetType(const std::string& format_string); // Get format identifiers for various types. - static const ClipboardFormatType& GetFilenamesType(); - static const ClipboardFormatType& GetUrlType(); - static const ClipboardFormatType& GetPlainTextType(); - static const ClipboardFormatType& GetWebKitSmartPasteType(); + static const ClipboardFormatType& FilenamesType(); + static const ClipboardFormatType& UrlType(); + static const ClipboardFormatType& PlainTextType(); + static const ClipboardFormatType& WebKitSmartPasteType(); // Win: MS HTML Format, Other: Generic HTML format - static const ClipboardFormatType& GetHtmlType(); - static const ClipboardFormatType& GetSvgType(); - static const ClipboardFormatType& GetRtfType(); - static const ClipboardFormatType& GetPngType(); + static const ClipboardFormatType& HtmlType(); + static const ClipboardFormatType& SvgType(); + static const ClipboardFormatType& RtfType(); + static const ClipboardFormatType& PngType(); // TODO(crbug.com/1201018): Remove this type. - static const ClipboardFormatType& GetBitmapType(); - static const ClipboardFormatType& GetWebCustomDataType(); + static const ClipboardFormatType& BitmapType(); + static const ClipboardFormatType& WebCustomDataType(); #if defined(OS_WIN) // ANSI formats. Only Windows differentiates between ANSI and UNICODE formats // in ClipboardFormatType. Reference: // https://docs.microsoft.com/en-us/windows/win32/learnwin32/working-with-strings - static const ClipboardFormatType& GetUrlAType(); - static const ClipboardFormatType& GetPlainTextAType(); - static const ClipboardFormatType& GetFilenameAType(); + static const ClipboardFormatType& UrlAType(); + static const ClipboardFormatType& PlainTextAType(); + static const ClipboardFormatType& FilenameAType(); // Firefox text/html - static const ClipboardFormatType& GetTextHtmlType(); - static const ClipboardFormatType& GetCFHDropType(); - static const ClipboardFormatType& GetFileDescriptorAType(); - static const ClipboardFormatType& GetFileDescriptorType(); - static const ClipboardFormatType& GetFileContentZeroType(); - static const ClipboardFormatType& GetFileContentAtIndexType(LONG index); - static const ClipboardFormatType& GetFilenameType(); - static const ClipboardFormatType& GetIDListType(); - static const ClipboardFormatType& GetMozUrlType(); + static const ClipboardFormatType& TextHtmlType(); + static const ClipboardFormatType& CFHDropType(); + static const ClipboardFormatType& FileDescriptorAType(); + static const ClipboardFormatType& FileDescriptorType(); + static const ClipboardFormatType& FileContentZeroType(); + static const ClipboardFormatType& FileContentAtIndexType(LONG index); + static const ClipboardFormatType& FilenameType(); + static const ClipboardFormatType& IDListType(); + static const ClipboardFormatType& MozUrlType(); #endif + // For custom formats we hardcode the web custom format prefix and the index. + // Due to Windows/Linux limitations, please place limits on the amount of + // `WebCustomFormatName` calls with unique `index` argument. + static std::string WebCustomFormatName(int index); + // Gets the ClipboardFormatType corresponding to a format string, + // registering it with the system if needed. + static ClipboardFormatType CustomPlatformType( + const std::string& format_string); + // Returns the web custom format map that has the mapping of MIME types to + // custom format names. + static const ClipboardFormatType& WebCustomFormatMap(); + // Returns the web custom format map name. + static std::string WebCustomFormatMapName(); + // ClipboardFormatType can be used in a set on some platforms. bool operator<(const ClipboardFormatType& other) const; @@ -94,7 +103,7 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES) ClipboardFormatType { // if the format isn't found. std::string GetName() const; #if defined(OS_WIN) - const FORMATETC& ToFormatEtc() const { return data_; } + const FORMATETC& ToFormatEtc() const { return *ChromeToWindowsType(&data_); } #elif defined(OS_APPLE) NSString* ToNSString() const { return data_; } // Custom copy and assignment constructor to handle NSString. @@ -126,11 +135,11 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD_TYPES) ClipboardFormatType { // an index into the data (the first file corresponds to index 0). This // function returns a map of index to CFSTR_FILECONTENTS clipboard format // type. - static std::map<LONG, ClipboardFormatType>& GetFileContentTypeMap(); + static std::map<LONG, ClipboardFormatType>& FileContentTypeMap(); // FORMATETC: // https://docs.microsoft.com/en-us/windows/desktop/com/the-formatetc-structure - FORMATETC data_; + CHROME_FORMATETC data_; #elif defined(USE_AURA) || defined(OS_ANDROID) || defined(OS_FUCHSIA) explicit ClipboardFormatType(const std::string& native_format); std::string data_; diff --git a/chromium/ui/base/clipboard/clipboard_format_type_android.cc b/chromium/ui/base/clipboard/clipboard_format_type_android.cc index ded6f0c1d1a..7af255bc24c 100644 --- a/chromium/ui/base/clipboard/clipboard_format_type_android.cc +++ b/chromium/ui/base/clipboard/clipboard_format_type_android.cc @@ -4,6 +4,9 @@ #include "ui/base/clipboard/clipboard_format_type.h" +#include "base/strings/strcat.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" #include "ui/base/clipboard/clipboard_constants.h" namespace ui { @@ -38,6 +41,31 @@ bool ClipboardFormatType::operator==(const ClipboardFormatType& other) const { return data_ == other.data_; } +// static +std::string ClipboardFormatType::WebCustomFormatName(int index) { + return base::StrCat({"application/web;type=\"custom/format", + base::NumberToString(index), "\""}); +} + +// static +std::string ClipboardFormatType::WebCustomFormatMapName() { + return "application/web;type=\"custom/formatmap\""; +} + +// static +ClipboardFormatType ClipboardFormatType::CustomPlatformType( + const std::string& format_string) { + DCHECK(base::IsStringASCII(format_string)); + return ClipboardFormatType::Deserialize(format_string); +} + +// static +const ClipboardFormatType& ClipboardFormatType::WebCustomFormatMap() { + static base::NoDestructor<ClipboardFormatType> type( + ClipboardFormatType::WebCustomFormatMapName()); + return *type; +} + // Various predefined ClipboardFormatTypes. // static @@ -47,61 +75,62 @@ ClipboardFormatType ClipboardFormatType::GetType( } // static -const ClipboardFormatType& ClipboardFormatType::GetFilenamesType() { +const ClipboardFormatType& ClipboardFormatType::FilenamesType() { static base::NoDestructor<ClipboardFormatType> type(kMimeTypeURIList); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetUrlType() { +const ClipboardFormatType& ClipboardFormatType::UrlType() { static base::NoDestructor<ClipboardFormatType> type(kMimeTypeMozillaURL); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetPlainTextType() { +const ClipboardFormatType& ClipboardFormatType::PlainTextType() { static base::NoDestructor<ClipboardFormatType> type(kMimeTypeText); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetWebKitSmartPasteType() { +const ClipboardFormatType& ClipboardFormatType::WebKitSmartPasteType() { static base::NoDestructor<ClipboardFormatType> type( kMimeTypeWebkitSmartPaste); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetHtmlType() { +const ClipboardFormatType& ClipboardFormatType::HtmlType() { static base::NoDestructor<ClipboardFormatType> type(kMimeTypeHTML); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetSvgType() { +const ClipboardFormatType& ClipboardFormatType::SvgType() { static base::NoDestructor<ClipboardFormatType> type(kMimeTypeSvg); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetRtfType() { +const ClipboardFormatType& ClipboardFormatType::RtfType() { static base::NoDestructor<ClipboardFormatType> type(kMimeTypeRTF); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetPngType() { +const ClipboardFormatType& ClipboardFormatType::PngType() { static base::NoDestructor<ClipboardFormatType> type(kMimeTypePNG); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetBitmapType() { - return ClipboardFormatType::GetPngType(); +const ClipboardFormatType& ClipboardFormatType::BitmapType() { + static base::NoDestructor<ClipboardFormatType> type(kMimeTypeImageURI); + return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetWebCustomDataType() { +const ClipboardFormatType& ClipboardFormatType::WebCustomDataType() { static base::NoDestructor<ClipboardFormatType> type(kMimeTypeWebCustomData); return *type; } diff --git a/chromium/ui/base/clipboard/clipboard_format_type_aura.cc b/chromium/ui/base/clipboard/clipboard_format_type_aura.cc index a47fd9ae246..078fa98592f 100644 --- a/chromium/ui/base/clipboard/clipboard_format_type_aura.cc +++ b/chromium/ui/base/clipboard/clipboard_format_type_aura.cc @@ -4,14 +4,17 @@ #include "ui/base/clipboard/clipboard_format_type.h" +#include "base/strings/strcat.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" #include "ui/base/clipboard/clipboard_constants.h" namespace ui { -// TODO(huangdarwin): Investigate creating a new clipboard_format_type_x11 as a -// wrapper around an X11 ::Atom. This wasn't possible in the past, because unit -// tests spawned a new X11 server for each test, meaning Atom numeric values -// didn't persist across tests. +// TODO(crbug.com/1123230): Investigate creating a new clipboard_format_type_x11 +// as a wrapper around an X11 ::Atom. This wasn't possible in the past, because +// unit tests spawned a new X11 server for each test, meaning Atom numeric +// values didn't persist across tests. ClipboardFormatType::ClipboardFormatType() = default; ClipboardFormatType::~ClipboardFormatType() = default; @@ -41,6 +44,31 @@ bool ClipboardFormatType::operator==(const ClipboardFormatType& other) const { return data_ == other.data_; } +// static +ClipboardFormatType ClipboardFormatType::CustomPlatformType( + const std::string& format_string) { + DCHECK(base::IsStringASCII(format_string)); + return ClipboardFormatType::Deserialize(format_string); +} + +// static +std::string ClipboardFormatType::WebCustomFormatName(int index) { + return base::StrCat({"application/web;type=\"custom/format", + base::NumberToString(index), "\""}); +} + +// static +std::string ClipboardFormatType::WebCustomFormatMapName() { + return "application/web;type=\"custom/formatmap\""; +} + +// static +const ClipboardFormatType& ClipboardFormatType::WebCustomFormatMap() { + static base::NoDestructor<ClipboardFormatType> type( + ClipboardFormatType::WebCustomFormatMapName()); + return *type; +} + // Various predefined ClipboardFormatTypes. // static @@ -50,61 +78,61 @@ ClipboardFormatType ClipboardFormatType::GetType( } // static -const ClipboardFormatType& ClipboardFormatType::GetFilenamesType() { +const ClipboardFormatType& ClipboardFormatType::FilenamesType() { static base::NoDestructor<ClipboardFormatType> type(kMimeTypeURIList); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetUrlType() { +const ClipboardFormatType& ClipboardFormatType::UrlType() { static base::NoDestructor<ClipboardFormatType> type(kMimeTypeMozillaURL); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetPlainTextType() { +const ClipboardFormatType& ClipboardFormatType::PlainTextType() { static base::NoDestructor<ClipboardFormatType> type(kMimeTypeText); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetHtmlType() { +const ClipboardFormatType& ClipboardFormatType::HtmlType() { static base::NoDestructor<ClipboardFormatType> type(kMimeTypeHTML); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetSvgType() { +const ClipboardFormatType& ClipboardFormatType::SvgType() { static base::NoDestructor<ClipboardFormatType> type(kMimeTypeSvg); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetRtfType() { +const ClipboardFormatType& ClipboardFormatType::RtfType() { static base::NoDestructor<ClipboardFormatType> type(kMimeTypeRTF); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetPngType() { +const ClipboardFormatType& ClipboardFormatType::PngType() { static base::NoDestructor<ClipboardFormatType> type(kMimeTypePNG); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetBitmapType() { - return ClipboardFormatType::GetPngType(); +const ClipboardFormatType& ClipboardFormatType::BitmapType() { + return ClipboardFormatType::PngType(); } // static -const ClipboardFormatType& ClipboardFormatType::GetWebKitSmartPasteType() { +const ClipboardFormatType& ClipboardFormatType::WebKitSmartPasteType() { static base::NoDestructor<ClipboardFormatType> type( kMimeTypeWebkitSmartPaste); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetWebCustomDataType() { +const ClipboardFormatType& ClipboardFormatType::WebCustomDataType() { static base::NoDestructor<ClipboardFormatType> type(kMimeTypeWebCustomData); return *type; } diff --git a/chromium/ui/base/clipboard/clipboard_format_type_mac.mm b/chromium/ui/base/clipboard/clipboard_format_type_mac.mm index 719582aa1da..f370fc8745e 100644 --- a/chromium/ui/base/clipboard/clipboard_format_type_mac.mm +++ b/chromium/ui/base/clipboard/clipboard_format_type_mac.mm @@ -6,12 +6,17 @@ #import <Cocoa/Cocoa.h> +#include "base/strings/strcat.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" #include "base/strings/sys_string_conversions.h" #include "ui/base/clipboard/clipboard_constants.h" namespace ui { // ClipboardFormatType implementation. +// MacOS formats are implemented via Uniform Type Identifiers, documented here: +// https://developer.apple.com/library/archive/documentation/General/Conceptual/DevPedia-CocoaCore/UniformTypeIdentifier.html#//apple_ref/doc/uid/TP40008195-CH60 ClipboardFormatType::ClipboardFormatType() : data_(nil) {} ClipboardFormatType::ClipboardFormatType(NSString* native_format) @@ -55,6 +60,30 @@ bool ClipboardFormatType::operator<(const ClipboardFormatType& other) const { return [data_ compare:other.data_] == NSOrderedAscending; } +// static +std::string ClipboardFormatType::WebCustomFormatName(int index) { + return base::StrCat({"com.web.custom.format", base::NumberToString(index)}); +} + +// static +std::string ClipboardFormatType::WebCustomFormatMapName() { + return "com.web.custom.format.map"; +} + +// static +const ClipboardFormatType& ClipboardFormatType::WebCustomFormatMap() { + static base::NoDestructor<ClipboardFormatType> type( + base::SysUTF8ToNSString(ClipboardFormatType::WebCustomFormatMapName())); + return *type; +} + +// static +ClipboardFormatType ClipboardFormatType::CustomPlatformType( + const std::string& format_string) { + DCHECK(base::IsStringASCII(format_string)); + return ClipboardFormatType::Deserialize(format_string); +} + // Various predefined ClipboardFormatTypes. // static @@ -64,60 +93,60 @@ ClipboardFormatType ClipboardFormatType::GetType( } // static -const ClipboardFormatType& ClipboardFormatType::GetFilenamesType() { +const ClipboardFormatType& ClipboardFormatType::FilenamesType() { static base::NoDestructor<ClipboardFormatType> type(NSFilenamesPboardType); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetUrlType() { +const ClipboardFormatType& ClipboardFormatType::UrlType() { static base::NoDestructor<ClipboardFormatType> type(NSURLPboardType); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetPlainTextType() { +const ClipboardFormatType& ClipboardFormatType::PlainTextType() { static base::NoDestructor<ClipboardFormatType> type(NSPasteboardTypeString); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetHtmlType() { +const ClipboardFormatType& ClipboardFormatType::HtmlType() { static base::NoDestructor<ClipboardFormatType> type(NSHTMLPboardType); return *type; } -const ClipboardFormatType& ClipboardFormatType::GetSvgType() { +const ClipboardFormatType& ClipboardFormatType::SvgType() { static base::NoDestructor<ClipboardFormatType> type(kImageSvg); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetRtfType() { +const ClipboardFormatType& ClipboardFormatType::RtfType() { static base::NoDestructor<ClipboardFormatType> type(NSRTFPboardType); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetPngType() { +const ClipboardFormatType& ClipboardFormatType::PngType() { static base::NoDestructor<ClipboardFormatType> type(NSPasteboardTypePNG); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetBitmapType() { +const ClipboardFormatType& ClipboardFormatType::BitmapType() { static base::NoDestructor<ClipboardFormatType> type(NSTIFFPboardType); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetWebKitSmartPasteType() { +const ClipboardFormatType& ClipboardFormatType::WebKitSmartPasteType() { static base::NoDestructor<ClipboardFormatType> type(kWebSmartPastePboardType); return *type; } // static -const ClipboardFormatType& ClipboardFormatType::GetWebCustomDataType() { +const ClipboardFormatType& ClipboardFormatType::WebCustomDataType() { static base::NoDestructor<ClipboardFormatType> type(kWebCustomDataPboardType); return *type; } diff --git a/chromium/ui/base/clipboard/clipboard_format_type_win.cc b/chromium/ui/base/clipboard/clipboard_format_type_win.cc index 8834346f7ae..b700f40eba7 100644 --- a/chromium/ui/base/clipboard/clipboard_format_type_win.cc +++ b/chromium/ui/base/clipboard/clipboard_format_type_win.cc @@ -9,74 +9,19 @@ #include "base/containers/flat_map.h" #include "base/memory/ptr_util.h" -#include "base/metrics/histogram_functions.h" #include "base/no_destructor.h" #include "base/notreached.h" +#include "base/strings/strcat.h" #include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" - -namespace { - -const base::flat_map<UINT, std::string>& PredefinedFormatToNameMap() { - // These formats are described in winuser.h and - // https://docs.microsoft.com/en-us/windows/win32/dataxchg/standard-clipboard-formats - static const base::NoDestructor<base::flat_map<UINT, std::string>> - format_to_name({ - {CF_TEXT, "CF_TEXT"}, - {CF_DIF, "CF_DIF"}, - {CF_TIFF, "CF_TIFF"}, - {CF_OEMTEXT, "CF_OEMTEXT"}, - {CF_DIB, "CF_DIB"}, - {CF_PENDATA, "CF_PENDATA"}, - {CF_RIFF, "CF_RIFF"}, - {CF_WAVE, "CF_WAVE"}, - {CF_UNICODETEXT, "CF_UNICODETEXT"}, - {CF_DIBV5, "CF_DIBV5"}, - {CF_OWNERDISPLAY, "CF_OWNERDISPLAY"}, - - // These formats are predefined but explicitly blocked from use, - // either due to passing along handles, or concerns regarding exposing - // private information. - // {CF_MAX, "CF_MAX"}, - // {CF_PRIVATEFIRST, "CF_PRIVATEFIRST"}, - // {CF_PRIVATELAST, "CF_PRIVATELAST"}, - // {CF_GDIOBJFIRST, "CF_GDIOBJFIRST"}, - // {CF_GDIOBJLAST, "CF_GDIOBJLAST"}, - // {CF_BITMAP, "CF_BITMAP"}, - // {CF_SYLK, "CF_SYLK"}, - // {CF_METAFILEPICT, "CF_METAFILEPICT"}, - // {CF_DSPTEXT, "CF_DSPTEXT"}, - // {CF_DSPBITMAP, "CF_DSPBITMAP"}, - // {CF_DSPMETAFILEPICT, "CF_DSPMETAFILEPICT"}, - // {CF_DSPENHMETAFILE, "CF_DSPENHMETAFILE"}, - // {CF_ENHMETAFILE, "CF_ENHMETAFILE"}, - // {CF_HDROP, "CF_HDROP"}, - // {CF_LOCALE, "CF_LOCALE"}, - // {CF_PALETTE, "CF_PALETTE"}, - - }); - return *format_to_name; -} - -const base::flat_map<std::string, UINT>& PredefinedNameToFormatMap() { - // Use lambda constructor for thread-safe initialization of name_to_format. - static const base::NoDestructor<base::flat_map<std::string, UINT>> - name_to_format([] { - base::flat_map<std::string, UINT> new_name_to_format; - const auto& format_to_name = PredefinedFormatToNameMap(); - new_name_to_format.reserve(format_to_name.size()); - for (const auto& it : format_to_name) - new_name_to_format.emplace(it.second, it.first); - return new_name_to_format; - }()); - return *name_to_format; -} - -} // namespace +#include "ui/base/clipboard/clipboard_constants.h" namespace ui { // ClipboardFormatType implementation. +// Windows formats are backed by "Clipboard Formats", documented here: +// https://docs.microsoft.com/en-us/windows/win32/dataxchg/clipboard-formats ClipboardFormatType::ClipboardFormatType() = default; ClipboardFormatType::ClipboardFormatType(UINT native_format) @@ -91,17 +36,7 @@ ClipboardFormatType::ClipboardFormatType(UINT native_format, DWORD tymed) : data_{/* .cfFormat */ static_cast<CLIPFORMAT>(native_format), /* .ptd */ nullptr, /* .dwAspect */ DVASPECT_CONTENT, - /* .lindex */ index, /* .tymed*/ tymed} { - // Log the frequency of invalid formats being input into the constructor. - if (!native_format) { - static int error_count = 0; - ++error_count; - // TODO(https://crbug.com/1000919): Evaluate and remove UMA metrics after - // enough data is gathered. - base::UmaHistogramCounts100("Clipboard.RegisterClipboardFormatFailure", - error_count); - } -} + /* .lindex */ index, /* .tymed*/ tymed} {} ClipboardFormatType::~ClipboardFormatType() = default; @@ -122,22 +57,59 @@ ClipboardFormatType ClipboardFormatType::Deserialize( return ClipboardFormatType(clipboard_format); } +// static +std::string ClipboardFormatType::WebCustomFormatName(int index) { + return base::StrCat({"Web Custom Format", base::NumberToString(index)}); +} + +// static +std::string ClipboardFormatType::WebCustomFormatMapName() { + return "Web Custom Format Map"; +} + +// static +ClipboardFormatType ClipboardFormatType::CustomPlatformType( + const std::string& format_string) { + // Once these formats are registered, `RegisterClipboardFormat` just returns + // the `cfFormat` associated with it and doesn't register a new format. + DCHECK(base::IsStringASCII(format_string)); + return ClipboardFormatType( + ::RegisterClipboardFormat(base::ASCIIToWide(format_string).c_str())); +} + +// static +const ClipboardFormatType& ClipboardFormatType::WebCustomFormatMap() { + static base::NoDestructor<ClipboardFormatType> format( + ::RegisterClipboardFormat( + base::ASCIIToWide(ClipboardFormatType::WebCustomFormatMapName()) + .c_str())); + return *format; +} + std::string ClipboardFormatType::GetName() const { - const auto& predefined_format_to_name = PredefinedFormatToNameMap(); - const auto it = predefined_format_to_name.find(data_.cfFormat); - if (it != predefined_format_to_name.end()) - return it->second; - - constexpr size_t kMaxFormatSize = 1024; - static base::NoDestructor<std::vector<wchar_t>> name_buffer(kMaxFormatSize); - int name_size = GetClipboardFormatName(data_.cfFormat, name_buffer->data(), - kMaxFormatSize); - if (!name_size) { - // Input format doesn't exist or is predefined. - return std::string(); + if (ClipboardFormatType::PlainTextAType().ToFormatEtc().cfFormat == + data_.cfFormat) { + return kMimeTypeText; } - - return base::WideToUTF8(std::wstring(name_buffer->data(), name_size)); + if (ClipboardFormatType::HtmlType().ToFormatEtc().cfFormat == + data_.cfFormat) { + return kMimeTypeHTML; + } + if (ClipboardFormatType::RtfType().ToFormatEtc().cfFormat == data_.cfFormat) { + return kMimeTypeRTF; + } + if (CF_DIB == data_.cfFormat) + return kMimeTypePNG; + if (ClipboardFormatType::CFHDropType().ToFormatEtc().cfFormat == + data_.cfFormat || + ClipboardFormatType::FilenameType().ToFormatEtc().cfFormat == + data_.cfFormat || + ClipboardFormatType::FilenameAType().ToFormatEtc().cfFormat == + data_.cfFormat) { + return kMimeTypeURIList; + } + // Not a standard format type. + return std::string(); } bool ClipboardFormatType::operator<(const ClipboardFormatType& other) const { @@ -153,11 +125,6 @@ bool ClipboardFormatType::operator==(const ClipboardFormatType& other) const { // static ClipboardFormatType ClipboardFormatType::GetType( const std::string& format_string) { - const auto& predefined_name_to_format = PredefinedNameToFormatMap(); - const auto it = predefined_name_to_format.find(format_string); - if (it != predefined_name_to_format.end()) - return ClipboardFormatType(it->second); - return ClipboardFormatType( ::RegisterClipboardFormat(base::ASCIIToWide(format_string).c_str())); } @@ -169,19 +136,19 @@ ClipboardFormatType ClipboardFormatType::GetType( // ClipboardFormatTypes thread-safe on all platforms. // static -const ClipboardFormatType& ClipboardFormatType::GetFilenamesType() { - return GetFilenameType(); +const ClipboardFormatType& ClipboardFormatType::FilenamesType() { + return FilenameType(); } // static -const ClipboardFormatType& ClipboardFormatType::GetUrlType() { +const ClipboardFormatType& ClipboardFormatType::UrlType() { static base::NoDestructor<ClipboardFormatType> format( ::RegisterClipboardFormat(CFSTR_INETURLW)); return *format; } // static -const ClipboardFormatType& ClipboardFormatType::GetPlainTextType() { +const ClipboardFormatType& ClipboardFormatType::PlainTextType() { static base::NoDestructor<ClipboardFormatType> format(CF_UNICODETEXT); return *format; } @@ -189,14 +156,14 @@ const ClipboardFormatType& ClipboardFormatType::GetPlainTextType() { // MS HTML Format // static -const ClipboardFormatType& ClipboardFormatType::GetHtmlType() { +const ClipboardFormatType& ClipboardFormatType::HtmlType() { static base::NoDestructor<ClipboardFormatType> format( ::RegisterClipboardFormat(L"HTML Format")); return *format; } // static -const ClipboardFormatType& ClipboardFormatType::GetSvgType() { +const ClipboardFormatType& ClipboardFormatType::SvgType() { static base::NoDestructor<ClipboardFormatType> format( ::RegisterClipboardFormat(CFSTR_MIME_SVG_XML)); return *format; @@ -205,40 +172,40 @@ const ClipboardFormatType& ClipboardFormatType::GetSvgType() { // MS RTF Format // static -const ClipboardFormatType& ClipboardFormatType::GetRtfType() { +const ClipboardFormatType& ClipboardFormatType::RtfType() { static base::NoDestructor<ClipboardFormatType> format( ::RegisterClipboardFormat(L"Rich Text Format")); return *format; } // static -const ClipboardFormatType& ClipboardFormatType::GetPngType() { +const ClipboardFormatType& ClipboardFormatType::PngType() { static base::NoDestructor<ClipboardFormatType> format( ::RegisterClipboardFormat(L"PNG")); return *format; } // static -const ClipboardFormatType& ClipboardFormatType::GetBitmapType() { +const ClipboardFormatType& ClipboardFormatType::BitmapType() { static base::NoDestructor<ClipboardFormatType> format(CF_DIBV5); return *format; } // static -const ClipboardFormatType& ClipboardFormatType::GetUrlAType() { +const ClipboardFormatType& ClipboardFormatType::UrlAType() { static base::NoDestructor<ClipboardFormatType> format( ::RegisterClipboardFormat(CFSTR_INETURLA)); return *format; } // static -const ClipboardFormatType& ClipboardFormatType::GetPlainTextAType() { +const ClipboardFormatType& ClipboardFormatType::PlainTextAType() { static base::NoDestructor<ClipboardFormatType> format(CF_TEXT); return *format; } // static -const ClipboardFormatType& ClipboardFormatType::GetFilenameAType() { +const ClipboardFormatType& ClipboardFormatType::FilenameAType() { static base::NoDestructor<ClipboardFormatType> format( ::RegisterClipboardFormat(CFSTR_FILENAMEA)); return *format; @@ -246,14 +213,14 @@ const ClipboardFormatType& ClipboardFormatType::GetFilenameAType() { // Firefox text/html // static -const ClipboardFormatType& ClipboardFormatType::GetTextHtmlType() { +const ClipboardFormatType& ClipboardFormatType::TextHtmlType() { static base::NoDestructor<ClipboardFormatType> format( ::RegisterClipboardFormat(L"text/html")); return *format; } // static -const ClipboardFormatType& ClipboardFormatType::GetCFHDropType() { +const ClipboardFormatType& ClipboardFormatType::CFHDropType() { static base::NoDestructor<ClipboardFormatType> format(CF_HDROP); return *format; } @@ -262,24 +229,24 @@ const ClipboardFormatType& ClipboardFormatType::GetCFHDropType() { // ANSI format (e.g., it could be that it doesn't support Unicode). So need to // register both the ANSI and Unicode file group descriptors. // static -const ClipboardFormatType& ClipboardFormatType::GetFileDescriptorAType() { +const ClipboardFormatType& ClipboardFormatType::FileDescriptorAType() { static base::NoDestructor<ClipboardFormatType> format( ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORA)); return *format; } // static -const ClipboardFormatType& ClipboardFormatType::GetFileDescriptorType() { +const ClipboardFormatType& ClipboardFormatType::FileDescriptorType() { static base::NoDestructor<ClipboardFormatType> format( ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW)); return *format; } // static -const ClipboardFormatType& ClipboardFormatType::GetFileContentZeroType() { +const ClipboardFormatType& ClipboardFormatType::FileContentZeroType() { // This uses a storage media type of TYMED_HGLOBAL, which is not commonly // used with CFSTR_FILECONTENTS (but used in Chromium--see - // OSExchangeDataProviderWin::SetFileContents). Use GetFileContentAtIndexType + // OSExchangeDataProviderWin::SetFileContents). Use FileContentAtIndexType // if TYMED_ISTREAM and TYMED_ISTORAGE are needed. // TODO(https://crbug.com/950756): Should TYMED_ISTREAM / TYMED_ISTORAGE be // used instead of TYMED_HGLOBAL in @@ -292,17 +259,16 @@ const ClipboardFormatType& ClipboardFormatType::GetFileContentZeroType() { } // static -std::map<LONG, ClipboardFormatType>& -ClipboardFormatType::GetFileContentTypeMap() { +std::map<LONG, ClipboardFormatType>& ClipboardFormatType::FileContentTypeMap() { static base::NoDestructor<std::map<LONG, ClipboardFormatType>> index_to_type_map; return *index_to_type_map; } // static -const ClipboardFormatType& ClipboardFormatType::GetFileContentAtIndexType( +const ClipboardFormatType& ClipboardFormatType::FileContentAtIndexType( LONG index) { - auto& index_to_type_map = GetFileContentTypeMap(); + auto& index_to_type_map = FileContentTypeMap(); auto insert_or_assign_result = index_to_type_map.insert( {index, @@ -312,35 +278,35 @@ const ClipboardFormatType& ClipboardFormatType::GetFileContentAtIndexType( } // static -const ClipboardFormatType& ClipboardFormatType::GetFilenameType() { +const ClipboardFormatType& ClipboardFormatType::FilenameType() { static base::NoDestructor<ClipboardFormatType> format( ::RegisterClipboardFormat(CFSTR_FILENAMEW)); return *format; } // static -const ClipboardFormatType& ClipboardFormatType::GetIDListType() { +const ClipboardFormatType& ClipboardFormatType::IDListType() { static base::NoDestructor<ClipboardFormatType> format( ::RegisterClipboardFormat(CFSTR_SHELLIDLIST)); return *format; } // static -const ClipboardFormatType& ClipboardFormatType::GetMozUrlType() { +const ClipboardFormatType& ClipboardFormatType::MozUrlType() { static base::NoDestructor<ClipboardFormatType> format( ::RegisterClipboardFormat(L"text/x-moz-url")); return *format; } // static -const ClipboardFormatType& ClipboardFormatType::GetWebKitSmartPasteType() { +const ClipboardFormatType& ClipboardFormatType::WebKitSmartPasteType() { static base::NoDestructor<ClipboardFormatType> format( ::RegisterClipboardFormat(L"WebKit Smart Paste Format")); return *format; } // static -const ClipboardFormatType& ClipboardFormatType::GetWebCustomDataType() { +const ClipboardFormatType& ClipboardFormatType::WebCustomDataType() { // TODO(http://crbug.com/106449): Standardize this name. static base::NoDestructor<ClipboardFormatType> format( ::RegisterClipboardFormat(L"Chromium Web Custom MIME Data Format")); diff --git a/chromium/ui/base/clipboard/clipboard_mac.h b/chromium/ui/base/clipboard/clipboard_mac.h index f45d165368a..45d9d4b4e11 100644 --- a/chromium/ui/base/clipboard/clipboard_mac.h +++ b/chromium/ui/base/clipboard/clipboard_mac.h @@ -10,19 +10,31 @@ #include "base/component_export.h" #include "base/gtest_prod_util.h" -#include "base/macros.h" +#include "base/mac/foundation_util.h" #include "ui/base/clipboard/clipboard.h" @class NSPasteboard; namespace ui { +// Documentation on the underlying MacOS API this ultimately abstracts is +// available at https://developer.apple.com/documentation/appkit/nspasteboard +// and +// https://developer.apple.com/library/archive/documentation/General/Conceptual/Devpedia-CocoaApp/Pasteboard.html. class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) ClipboardMac : public Clipboard { + public: + ClipboardMac(const ClipboardMac&) = delete; + ClipboardMac& operator=(const ClipboardMac&) = delete; + private: - FRIEND_TEST_ALL_PREFIXES(ClipboardMacTest, ReadImageRetina); - FRIEND_TEST_ALL_PREFIXES(ClipboardMacTest, ReadImageNonRetina); - FRIEND_TEST_ALL_PREFIXES(ClipboardMacTest, EmptyImage); - FRIEND_TEST_ALL_PREFIXES(ClipboardMacTest, PDFImage); + FRIEND_TEST_ALL_PREFIXES(ClipboardMacTest, ReadImageRetina_Bitmap); + FRIEND_TEST_ALL_PREFIXES(ClipboardMacTest, ReadImageNonRetina_Bitmap); + FRIEND_TEST_ALL_PREFIXES(ClipboardMacTest, EmptyImage_Bitmap); + FRIEND_TEST_ALL_PREFIXES(ClipboardMacTest, PDFImage_Bitmap); + FRIEND_TEST_ALL_PREFIXES(ClipboardMacTest, ReadImageRetina_Png); + FRIEND_TEST_ALL_PREFIXES(ClipboardMacTest, ReadImageNonRetina_Png); + FRIEND_TEST_ALL_PREFIXES(ClipboardMacTest, EmptyImage_Png); + FRIEND_TEST_ALL_PREFIXES(ClipboardMacTest, PDFImage_Png); friend class Clipboard; ClipboardMac(); @@ -31,7 +43,8 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) ClipboardMac : public Clipboard { // Clipboard overrides: void OnPreShutdown() override; DataTransferEndpoint* GetSource(ClipboardBuffer buffer) const override; - uint64_t GetSequenceNumber(ClipboardBuffer buffer) const override; + const ClipboardSequenceNumberToken& GetSequenceNumber( + ClipboardBuffer buffer) const override; bool IsFormatAvailable(const ClipboardFormatType& format, ClipboardBuffer buffer, const DataTransferEndpoint* data_dst) const override; @@ -81,12 +94,9 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) ClipboardMac : public Clipboard { void ReadData(const ClipboardFormatType& format, const DataTransferEndpoint* data_dst, std::string* result) const override; - void WritePortableRepresentations( + void WritePortableAndPlatformRepresentations( ClipboardBuffer buffer, const ObjectMap& objects, - std::unique_ptr<DataTransferEndpoint> data_src) override; - void WritePlatformRepresentations( - ClipboardBuffer buffer, std::vector<Clipboard::PlatformRepresentation> platform_representations, std::unique_ptr<DataTransferEndpoint> data_src) override; void WriteText(const char* text_data, size_t text_len) override; @@ -107,10 +117,16 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) ClipboardMac : public Clipboard { const char* data_data, size_t data_len) override; + std::vector<uint8_t> ReadPngInternal(ClipboardBuffer buffer, + NSPasteboard* pasteboard) const; SkBitmap ReadImageInternal(ClipboardBuffer buffer, NSPasteboard* pasteboard) const; - DISALLOW_COPY_AND_ASSIGN(ClipboardMac); + // Mapping of OS-provided sequence number to a unique token. + mutable struct { + NSInteger sequence_number; + ClipboardSequenceNumberToken token; + } clipboard_sequence_; }; } // namespace ui diff --git a/chromium/ui/base/clipboard/clipboard_mac.mm b/chromium/ui/base/clipboard/clipboard_mac.mm index 76b08ef78e5..0419eb5beb8 100644 --- a/chromium/ui/base/clipboard/clipboard_mac.mm +++ b/chromium/ui/base/clipboard/clipboard_mac.mm @@ -6,19 +6,19 @@ #import <Cocoa/Cocoa.h> #include <stdint.h> +#include "ui/base/clipboard/clipboard.h" #include <limits> -#include "base/feature_list.h" #include "base/files/file_path.h" #include "base/logging.h" #include "base/mac/foundation_util.h" #include "base/mac/mac_util.h" #include "base/mac/scoped_cftyperef.h" #include "base/mac/scoped_nsobject.h" +#include "base/memory/ref_counted_memory.h" #include "base/no_destructor.h" #include "base/notreached.h" -#include "base/stl_util.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "base/strings/sys_string_conversions.h" @@ -29,13 +29,15 @@ #import "third_party/mozilla/NSPasteboard+Utils.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/base/clipboard/clipboard_constants.h" +#include "ui/base/clipboard/clipboard_format_type.h" #include "ui/base/clipboard/clipboard_metrics.h" #include "ui/base/clipboard/clipboard_util_mac.h" #include "ui/base/clipboard/custom_data_helper.h" #include "ui/base/data_transfer_policy/data_transfer_endpoint.h" -#include "ui/base/ui_base_features.h" #include "ui/gfx/canvas.h" +#include "ui/gfx/codec/png_codec.h" #include "ui/gfx/geometry/size.h" +#include "ui/gfx/image/image.h" #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h" #include "url/gurl.h" @@ -50,6 +52,23 @@ NSPasteboard* GetPasteboard() { return pasteboard; } +base::scoped_nsobject<NSImage> GetNSImage(NSPasteboard* pasteboard) { + // If the pasteboard's image data is not to its liking, the guts of NSImage + // may throw, and that exception will leak. Prevent a crash in that case; + // a blank image is better. + base::scoped_nsobject<NSImage> image; + @try { + if (pasteboard) + image.reset([[NSImage alloc] initWithPasteboard:pasteboard]); + } @catch (id exception) { + } + if (!image) + return base::scoped_nsobject<NSImage>(); + if ([[image representations] count] == 0u) + return base::scoped_nsobject<NSImage>(); + return image; +} + } // namespace // Clipboard factory method. @@ -76,11 +95,17 @@ DataTransferEndpoint* ClipboardMac::GetSource(ClipboardBuffer buffer) const { return nullptr; } -uint64_t ClipboardMac::GetSequenceNumber(ClipboardBuffer buffer) const { +const ClipboardSequenceNumberToken& ClipboardMac::GetSequenceNumber( + ClipboardBuffer buffer) const { DCHECK(CalledOnValidThread()); DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste); - return [GetPasteboard() changeCount]; + NSInteger sequence_number = [GetPasteboard() changeCount]; + if (sequence_number != clipboard_sequence_.sequence_number) { + // Generate a unique token associated with the current sequence number. + clipboard_sequence_ = {sequence_number, ClipboardSequenceNumberToken()}; + } + return clipboard_sequence_.token; } // |data_dst| is not used. It's only passed to be consistent with other @@ -92,21 +117,21 @@ bool ClipboardMac::IsFormatAvailable( DCHECK(CalledOnValidThread()); DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste); - // Only support filenames if chrome://flags#clipboard-filenames is enabled. - if (format == ClipboardFormatType::GetFilenamesType() && - !base::FeatureList::IsEnabled(features::kClipboardFilenames)) { - return false; - } - // https://crbug.com/1016740#c21 base::scoped_nsobject<NSArray> types([[GetPasteboard() types] retain]); // Safari only places RTF on the pasteboard, never HTML. We can convert RTF // to HTML, so the presence of either indicates success when looking for HTML. - if ([format.ToNSString() isEqualToString:NSHTMLPboardType]) { + if (format == ClipboardFormatType::HtmlType()) { return [types containsObject:NSHTMLPboardType] || [types containsObject:NSRTFPboardType]; } + // Chrome can retrieve an image from the clipboard as either a bitmap or PNG. + if (format == ClipboardFormatType::PngType() || + format == ClipboardFormatType::BitmapType()) { + return [types containsObject:NSPasteboardTypePNG] || + [types containsObject:NSTIFFPboardType]; + } return [types containsObject:format.ToNSString()]; } @@ -146,16 +171,15 @@ void ClipboardMac::ReadAvailableTypes( NSPasteboard* pb = GetPasteboard(); types->clear(); - if (IsFormatAvailable(ClipboardFormatType::GetPlainTextType(), buffer, - data_dst)) + if (IsFormatAvailable(ClipboardFormatType::PlainTextType(), buffer, data_dst)) types->push_back(base::UTF8ToUTF16(kMimeTypeText)); - if (IsFormatAvailable(ClipboardFormatType::GetHtmlType(), buffer, data_dst)) + if (IsFormatAvailable(ClipboardFormatType::HtmlType(), buffer, data_dst)) types->push_back(base::UTF8ToUTF16(kMimeTypeHTML)); - if (IsFormatAvailable(ClipboardFormatType::GetSvgType(), buffer, data_dst)) + if (IsFormatAvailable(ClipboardFormatType::SvgType(), buffer, data_dst)) types->push_back(base::UTF8ToUTF16(kMimeTypeSvg)); - if (IsFormatAvailable(ClipboardFormatType::GetRtfType(), buffer, data_dst)) + if (IsFormatAvailable(ClipboardFormatType::RtfType(), buffer, data_dst)) types->push_back(base::UTF8ToUTF16(kMimeTypeRTF)); - if (IsFormatAvailable(ClipboardFormatType::GetFilenamesType(), buffer, + if (IsFormatAvailable(ClipboardFormatType::FilenamesType(), buffer, data_dst)) { types->push_back(base::UTF8ToUTF16(kMimeTypeURIList)); } else if (pb && [NSImage canInitWithPasteboard:pb]) { @@ -275,7 +299,7 @@ void ClipboardMac::ReadRTF(ClipboardBuffer buffer, DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste); RecordRead(ClipboardFormatMetric::kRtf); - return ReadData(ClipboardFormatType::GetRtfType(), data_dst, result); + return ReadData(ClipboardFormatType::RtfType(), data_dst, result); } // |data_dst| is not used. It's only passed to be consistent with other @@ -283,8 +307,8 @@ void ClipboardMac::ReadRTF(ClipboardBuffer buffer, void ClipboardMac::ReadPng(ClipboardBuffer buffer, const DataTransferEndpoint* data_dst, ReadPngCallback callback) const { - // TODO(crbug.com/1201018): Implement this. - NOTIMPLEMENTED(); + RecordRead(ClipboardFormatMetric::kPng); + std::move(callback).Run(ReadPngInternal(buffer, GetPasteboard())); } // |data_dst| is not used. It's only passed to be consistent with other @@ -367,23 +391,9 @@ void ClipboardMac::ReadData(const ClipboardFormatType& format, // |data_src| is not used. It's only passed to be consistent with other // platforms. -void ClipboardMac::WritePortableRepresentations( +void ClipboardMac::WritePortableAndPlatformRepresentations( ClipboardBuffer buffer, const ObjectMap& objects, - std::unique_ptr<DataTransferEndpoint> data_src) { - DCHECK(CalledOnValidThread()); - DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste); - - [GetPasteboard() declareTypes:@[] owner:nil]; - - for (const auto& object : objects) - DispatchPortableRepresentation(object.first, object.second); -} - -// |data_src| is not used. It's only passed to be consistent with other -// platforms. -void ClipboardMac::WritePlatformRepresentations( - ClipboardBuffer buffer, std::vector<Clipboard::PlatformRepresentation> platform_representations, std::unique_ptr<DataTransferEndpoint> data_src) { DCHECK(CalledOnValidThread()); @@ -392,6 +402,8 @@ void ClipboardMac::WritePlatformRepresentations( [GetPasteboard() declareTypes:@[] owner:nil]; DispatchPlatformRepresentations(std::move(platform_representations)); + for (const auto& object : objects) + DispatchPortableRepresentation(object.first, object.second); } void ClipboardMac::WriteText(const char* text_data, size_t text_len) { @@ -420,7 +432,7 @@ void ClipboardMac::WriteSvg(const char* markup_data, size_t markup_len) { } void ClipboardMac::WriteRTF(const char* rtf_data, size_t data_len) { - WriteData(ClipboardFormatType::GetRtfType(), rtf_data, data_len); + WriteData(ClipboardFormatType::RtfType(), rtf_data, data_len); } void ClipboardMac::WriteFilenames(std::vector<ui::FileInfo> filenames) { @@ -458,15 +470,7 @@ void ClipboardMac::WriteBitmap(const SkBitmap& bitmap) { NOTREACHED() << "SkBitmapToNSImageWithColorSpace failed"; return; } - // TODO (https://crbug.com/971916): Write NSImage directly to clipboard. - // An API to ask the NSImage to write itself to the clipboard comes in 10.6 :( - // For now, spit out the image as a TIFF. - NSData* tiff_data = [image TIFFRepresentation]; - LOG_IF(ERROR, tiff_data == nullptr) - << "Failed to allocate image for clipboard"; - if (tiff_data) { - [GetPasteboard() setData:tiff_data forType:NSTIFFPboardType]; - } + [GetPasteboard() writeObjects:@[ image ]]; } void ClipboardMac::WriteData(const ClipboardFormatType& format, @@ -479,43 +483,33 @@ void ClipboardMac::WriteData(const ClipboardFormatType& format, // Write an extra flavor that signifies WebKit was the last to modify the // pasteboard. This flavor has no data. void ClipboardMac::WriteWebSmartPaste() { - NSString* format = - ClipboardFormatType::GetWebKitSmartPasteType().ToNSString(); + NSString* format = ClipboardFormatType::WebKitSmartPasteType().ToNSString(); [GetPasteboard() setData:nil forType:format]; } +std::vector<uint8_t> ClipboardMac::ReadPngInternal( + ClipboardBuffer buffer, + NSPasteboard* pasteboard) const { + DCHECK(CalledOnValidThread()); + DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste); + + base::scoped_nsobject<NSImage> image = GetNSImage(pasteboard); + if (!image) + return std::vector<uint8_t>(); + + scoped_refptr<base::RefCountedMemory> mem = gfx::Image(image).As1xPNGBytes(); + std::vector<uint8_t> image_data(mem->data(), mem->data() + mem->size()); + return image_data; +} + SkBitmap ClipboardMac::ReadImageInternal(ClipboardBuffer buffer, NSPasteboard* pasteboard) const { DCHECK(CalledOnValidThread()); DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste); - // If the pasteboard's image data is not to its liking, the guts of NSImage - // may throw, and that exception will leak. Prevent a crash in that case; - // a blank image is better. - base::scoped_nsobject<NSImage> image; - @try { - // TODO(crbug.com/1175483): remove first branch of this code when - // ClipboardFilenames feature flag is removed. - if ([[pasteboard types] containsObject:NSFilenamesPboardType]) { - // -[NSImage initWithPasteboard:] gets confused with copies of a single - // file from the Finder, so extract the path ourselves. - // http://crbug.com/553686 - NSArray* paths = [pasteboard propertyListForType:NSFilenamesPboardType]; - if ([paths count]) { - // If N number of files are selected from finder, choose the last one. - image.reset([[NSImage alloc] - initWithContentsOfURL:[NSURL fileURLWithPath:[paths lastObject]]]); - } - } else { - if (pasteboard) - image.reset([[NSImage alloc] initWithPasteboard:pasteboard]); - } - } @catch (id exception) { - } + base::scoped_nsobject<NSImage> image = GetNSImage(pasteboard); if (!image) return SkBitmap(); - if ([[image representations] count] == 0u) - return SkBitmap(); // This logic prevents loss of pixels from retina images, where size != pixel // size. In an ideal world, the concept of "retina-ness" would be plumbed all diff --git a/chromium/ui/base/clipboard/clipboard_mac_unittest.mm b/chromium/ui/base/clipboard/clipboard_mac_unittest.mm index 7e84fa95d1d..4b4886495d9 100644 --- a/chromium/ui/base/clipboard/clipboard_mac_unittest.mm +++ b/chromium/ui/base/clipboard/clipboard_mac_unittest.mm @@ -4,6 +4,8 @@ #import "ui/base/clipboard/clipboard_mac.h" +#include <vector> + #import <AppKit/AppKit.h> #include "base/mac/scoped_cftyperef.h" @@ -14,6 +16,7 @@ #include "third_party/skia/include/core/SkBitmap.h" #include "ui/base/clipboard/clipboard_buffer.h" #include "ui/base/clipboard/clipboard_util_mac.h" +#include "ui/gfx/codec/png_codec.h" @interface RedView : NSView @end @@ -69,7 +72,7 @@ class ClipboardMacTest : public PlatformTest { } }; -TEST_F(ClipboardMacTest, ReadImageRetina) { +TEST_F(ClipboardMacTest, ReadImageRetina_Bitmap) { int32_t width = 99; int32_t height = 101; scoped_refptr<UniquePasteboard> pasteboard = new UniquePasteboard; @@ -85,7 +88,25 @@ TEST_F(ClipboardMacTest, ReadImageRetina) { EXPECT_EQ(2 * height, bitmap.height()); } -TEST_F(ClipboardMacTest, ReadImageNonRetina) { +TEST_F(ClipboardMacTest, ReadImageRetina_Png) { + int32_t width = 99; + int32_t height = 101; + scoped_refptr<UniquePasteboard> pasteboard = new UniquePasteboard; + base::scoped_nsobject<NSImage> image = CreateImage(width, height, true); + [pasteboard->get() writeObjects:@[ image.get() ]]; + + Clipboard* clipboard = Clipboard::GetForCurrentThread(); + ClipboardMac* clipboard_mac = static_cast<ClipboardMac*>(clipboard); + + std::vector<uint8_t> png_data = clipboard_mac->ReadPngInternal( + ClipboardBuffer::kCopyPaste, pasteboard->get()); + SkBitmap bitmap; + gfx::PNGCodec::Decode(png_data.data(), png_data.size(), &bitmap); + EXPECT_EQ(2 * width, bitmap.width()); + EXPECT_EQ(2 * height, bitmap.height()); +} + +TEST_F(ClipboardMacTest, ReadImageNonRetina_Bitmap) { int32_t width = 99; int32_t height = 101; scoped_refptr<UniquePasteboard> pasteboard = new UniquePasteboard; @@ -101,7 +122,25 @@ TEST_F(ClipboardMacTest, ReadImageNonRetina) { EXPECT_EQ(height, bitmap.height()); } -TEST_F(ClipboardMacTest, EmptyImage) { +TEST_F(ClipboardMacTest, ReadImageNonRetina_Png) { + int32_t width = 99; + int32_t height = 101; + scoped_refptr<UniquePasteboard> pasteboard = new UniquePasteboard; + base::scoped_nsobject<NSImage> image = CreateImage(width, height, false); + [pasteboard->get() writeObjects:@[ image.get() ]]; + + Clipboard* clipboard = Clipboard::GetForCurrentThread(); + ClipboardMac* clipboard_mac = static_cast<ClipboardMac*>(clipboard); + + std::vector<uint8_t> png_data = clipboard_mac->ReadPngInternal( + ClipboardBuffer::kCopyPaste, pasteboard->get()); + SkBitmap bitmap; + gfx::PNGCodec::Decode(png_data.data(), png_data.size(), &bitmap); + EXPECT_EQ(width, bitmap.width()); + EXPECT_EQ(height, bitmap.height()); +} + +TEST_F(ClipboardMacTest, EmptyImage_Bitmap) { base::scoped_nsobject<NSImage> image([[NSImage alloc] init]); scoped_refptr<UniquePasteboard> pasteboard = new UniquePasteboard; [pasteboard->get() writeObjects:@[ image.get() ]]; @@ -115,7 +154,23 @@ TEST_F(ClipboardMacTest, EmptyImage) { EXPECT_EQ(0, bitmap.height()); } -TEST_F(ClipboardMacTest, PDFImage) { +TEST_F(ClipboardMacTest, EmptyImage_Png) { + base::scoped_nsobject<NSImage> image([[NSImage alloc] init]); + scoped_refptr<UniquePasteboard> pasteboard = new UniquePasteboard; + [pasteboard->get() writeObjects:@[ image.get() ]]; + + Clipboard* clipboard = Clipboard::GetForCurrentThread(); + ClipboardMac* clipboard_mac = static_cast<ClipboardMac*>(clipboard); + + std::vector<uint8_t> png_data = clipboard_mac->ReadPngInternal( + ClipboardBuffer::kCopyPaste, pasteboard->get()); + SkBitmap bitmap; + gfx::PNGCodec::Decode(png_data.data(), png_data.size(), &bitmap); + EXPECT_EQ(0, bitmap.width()); + EXPECT_EQ(0, bitmap.height()); +} + +TEST_F(ClipboardMacTest, PDFImage_Bitmap) { int32_t width = 99; int32_t height = 101; NSRect frame = NSMakeRect(0, 0, width, height); @@ -138,4 +193,29 @@ TEST_F(ClipboardMacTest, PDFImage) { EXPECT_EQ(height, bitmap.height()); } +TEST_F(ClipboardMacTest, PDFImage_Png) { + int32_t width = 99; + int32_t height = 101; + NSRect frame = NSMakeRect(0, 0, width, height); + + // This seems like a round-about way of getting a NSPDFImageRep to shove into + // an NSPasteboard. However, I haven't found any other way of generating a + // "PDF" image that makes NSPasteboard happy. + base::scoped_nsobject<NSView> v([[RedView alloc] initWithFrame:frame]); + NSData* data = [v dataWithPDFInsideRect:frame]; + + scoped_refptr<UniquePasteboard> pasteboard = new UniquePasteboard; + [pasteboard->get() setData:data forType:NSPasteboardTypePDF]; + + Clipboard* clipboard = Clipboard::GetForCurrentThread(); + ClipboardMac* clipboard_mac = static_cast<ClipboardMac*>(clipboard); + + std::vector<uint8_t> png_data = clipboard_mac->ReadPngInternal( + ClipboardBuffer::kCopyPaste, pasteboard->get()); + SkBitmap bitmap; + gfx::PNGCodec::Decode(png_data.data(), png_data.size(), &bitmap); + EXPECT_EQ(width, bitmap.width()); + EXPECT_EQ(height, bitmap.height()); +} + } // namespace ui diff --git a/chromium/ui/base/clipboard/clipboard_monitor.h b/chromium/ui/base/clipboard/clipboard_monitor.h index 5ba97a41eaf..5a7ad27ea28 100644 --- a/chromium/ui/base/clipboard/clipboard_monitor.h +++ b/chromium/ui/base/clipboard/clipboard_monitor.h @@ -6,7 +6,6 @@ #define UI_BASE_CLIPBOARD_CLIPBOARD_MONITOR_H_ #include "base/component_export.h" -#include "base/macros.h" #include "base/no_destructor.h" #include "base/observer_list.h" #include "base/threading/thread_checker.h" @@ -23,6 +22,9 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) ClipboardMonitor { public: static ClipboardMonitor* GetInstance(); + ClipboardMonitor(const ClipboardMonitor&) = delete; + ClipboardMonitor& operator=(const ClipboardMonitor&) = delete; + // Adds an observer. void AddObserver(ClipboardObserver* observer); @@ -46,8 +48,6 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) ClipboardMonitor { base::ObserverList<ClipboardObserver>::Unchecked observers_; THREAD_CHECKER(thread_checker_); - - DISALLOW_COPY_AND_ASSIGN(ClipboardMonitor); }; } // namespace ui diff --git a/chromium/ui/base/clipboard/clipboard_non_backed.cc b/chromium/ui/base/clipboard/clipboard_non_backed.cc index 3cacd71de81..89bdb1068bb 100644 --- a/chromium/ui/base/clipboard/clipboard_non_backed.cc +++ b/chromium/ui/base/clipboard/clipboard_non_backed.cc @@ -13,9 +13,8 @@ #include <utility> #include "base/check_op.h" -#include "base/feature_list.h" +#include "base/containers/contains.h" #include "base/files/file_path.h" -#include "base/macros.h" #include "base/memory/ptr_util.h" #include "base/no_destructor.h" #include "base/notreached.h" @@ -32,7 +31,6 @@ #include "ui/base/clipboard/custom_data_helper.h" #include "ui/base/data_transfer_policy/data_transfer_endpoint.h" #include "ui/base/data_transfer_policy/data_transfer_policy_controller.h" -#include "ui/base/ui_base_features.h" #include "ui/gfx/geometry/size.h" namespace ui { @@ -88,16 +86,19 @@ bool IsRegisteredInstance(const Clipboard* clipboard) { class ClipboardInternal { public: ClipboardInternal() = default; - + ClipboardInternal(const ClipboardInternal&) = delete; + ClipboardInternal& operator=(const ClipboardInternal&) = delete; ~ClipboardInternal() = default; void Clear() { - ++sequence_number_; + sequence_number_ = ClipboardSequenceNumberToken(); data_.reset(); ClipboardMonitor::GetInstance()->NotifyClipboardDataChanged(); } - uint64_t sequence_number() const { return sequence_number_; } + const ClipboardSequenceNumberToken& sequence_number() const { + return sequence_number_; + } // Returns the current clipboard data, which may be nullptr if nothing has // been written since the last Clear(). @@ -180,6 +181,14 @@ class ClipboardInternal { *result = data->rtf_data(); } + // Reads png from the ClipboardData. + std::vector<uint8_t> ReadPng() const { + if (!HasFormat(ClipboardInternalFormat::kPng)) + return std::vector<uint8_t>(); + + return GetData()->png(); + } + // Reads image from the ClipboardData. SkBitmap ReadImage() const { SkBitmap img; @@ -243,7 +252,7 @@ class ClipboardInternal { DCHECK(data); std::unique_ptr<ClipboardData> previous_data = std::move(data_); data_ = std::move(data); - ++sequence_number_; + sequence_number_ = ClipboardSequenceNumberToken(); ClipboardMonitor::GetInstance()->NotifyClipboardDataChanged(); return previous_data; } @@ -254,7 +263,8 @@ class ClipboardInternal { auto* data = GetData(); if (!policy_controller || !data) return true; - return policy_controller->IsClipboardReadAllowed(data->source(), data_dst); + return policy_controller->IsClipboardReadAllowed(data->source(), data_dst, + data->size()); } private: @@ -268,9 +278,7 @@ class ClipboardInternal { std::unique_ptr<ClipboardData> data_; // Sequence number uniquely identifying clipboard state. - uint64_t sequence_number_ = 0; - - DISALLOW_COPY_AND_ASSIGN(ClipboardInternal); + ClipboardSequenceNumberToken sequence_number_; }; // Helper class to build a ClipboardData object and write it to clipboard. @@ -407,7 +415,8 @@ DataTransferEndpoint* ClipboardNonBacked::GetSource( return data ? data->source() : nullptr; } -uint64_t ClipboardNonBacked::GetSequenceNumber(ClipboardBuffer buffer) const { +const ClipboardSequenceNumberToken& ClipboardNonBacked::GetSequenceNumber( + ClipboardBuffer buffer) const { DCHECK(CalledOnValidThread()); return clipboard_internal_->sequence_number(); } @@ -422,29 +431,27 @@ bool ClipboardNonBacked::IsFormatAvailable( if (!clipboard_internal_->IsReadAllowed(data_dst)) return false; - if (format == ClipboardFormatType::GetPlainTextType() || - format == ClipboardFormatType::GetUrlType()) + if (format == ClipboardFormatType::PlainTextType() || + format == ClipboardFormatType::UrlType()) return clipboard_internal_->IsFormatAvailable( ClipboardInternalFormat::kText); - if (format == ClipboardFormatType::GetHtmlType()) + if (format == ClipboardFormatType::HtmlType()) return clipboard_internal_->IsFormatAvailable( ClipboardInternalFormat::kHtml); - if (format == ClipboardFormatType::GetSvgType()) + if (format == ClipboardFormatType::SvgType()) return clipboard_internal_->IsFormatAvailable( ClipboardInternalFormat::kSvg); - if (format == ClipboardFormatType::GetRtfType()) + if (format == ClipboardFormatType::RtfType()) return clipboard_internal_->IsFormatAvailable( ClipboardInternalFormat::kRtf); - if (format == ClipboardFormatType::GetPngType() || - format == ClipboardFormatType::GetBitmapType()) + if (format == ClipboardFormatType::PngType() || + format == ClipboardFormatType::BitmapType()) return clipboard_internal_->IsFormatAvailable( ClipboardInternalFormat::kPng); - if (format == ClipboardFormatType::GetWebKitSmartPasteType()) + if (format == ClipboardFormatType::WebKitSmartPasteType()) return clipboard_internal_->IsFormatAvailable( ClipboardInternalFormat::kWeb); - // Only support filenames if chrome://flags#clipboard-filenames is enabled. - if (format == ClipboardFormatType::GetFilenamesType() && - base::FeatureList::IsEnabled(features::kClipboardFilenames)) + if (format == ClipboardFormatType::FilenamesType()) return clipboard_internal_->IsFormatAvailable( ClipboardInternalFormat::kFilenames); const ClipboardData* data = clipboard_internal_->GetData(); @@ -468,20 +475,18 @@ void ClipboardNonBacked::ReadAvailableTypes( return; types->clear(); - if (IsFormatAvailable(ClipboardFormatType::GetPlainTextType(), buffer, - data_dst)) + if (IsFormatAvailable(ClipboardFormatType::PlainTextType(), buffer, data_dst)) types->push_back( - base::UTF8ToUTF16(ClipboardFormatType::GetPlainTextType().GetName())); - if (IsFormatAvailable(ClipboardFormatType::GetHtmlType(), buffer, data_dst)) + base::UTF8ToUTF16(ClipboardFormatType::PlainTextType().GetName())); + if (IsFormatAvailable(ClipboardFormatType::HtmlType(), buffer, data_dst)) types->push_back( - base::UTF8ToUTF16(ClipboardFormatType::GetHtmlType().GetName())); - if (IsFormatAvailable(ClipboardFormatType::GetRtfType(), buffer, data_dst)) + base::UTF8ToUTF16(ClipboardFormatType::HtmlType().GetName())); + if (IsFormatAvailable(ClipboardFormatType::RtfType(), buffer, data_dst)) types->push_back( - base::UTF8ToUTF16(ClipboardFormatType::GetRtfType().GetName())); - if (IsFormatAvailable(ClipboardFormatType::GetBitmapType(), buffer, data_dst)) + base::UTF8ToUTF16(ClipboardFormatType::RtfType().GetName())); + if (IsFormatAvailable(ClipboardFormatType::BitmapType(), buffer, data_dst)) types->push_back(base::UTF8ToUTF16(kMimeTypePNG)); - if (IsFormatAvailable(ClipboardFormatType::GetFilenamesType(), buffer, - data_dst)) + if (IsFormatAvailable(ClipboardFormatType::FilenamesType(), buffer, data_dst)) types->push_back(base::UTF8ToUTF16(kMimeTypeURIList)); if (clipboard_internal_->IsFormatAvailable( @@ -505,21 +510,20 @@ ClipboardNonBacked::ReadAvailablePlatformSpecificFormatNames( return types; // Includes all non-pickled AvailableTypes. - if (IsFormatAvailable(ClipboardFormatType::GetPlainTextType(), buffer, + if (IsFormatAvailable(ClipboardFormatType::PlainTextType(), buffer, data_dst)) { types.push_back( - base::UTF8ToUTF16(ClipboardFormatType::GetPlainTextType().GetName())); + base::UTF8ToUTF16(ClipboardFormatType::PlainTextType().GetName())); } - if (IsFormatAvailable(ClipboardFormatType::GetHtmlType(), buffer, data_dst)) { + if (IsFormatAvailable(ClipboardFormatType::HtmlType(), buffer, data_dst)) { types.push_back( - base::UTF8ToUTF16(ClipboardFormatType::GetHtmlType().GetName())); + base::UTF8ToUTF16(ClipboardFormatType::HtmlType().GetName())); } - if (IsFormatAvailable(ClipboardFormatType::GetRtfType(), buffer, data_dst)) { + if (IsFormatAvailable(ClipboardFormatType::RtfType(), buffer, data_dst)) { types.push_back( - base::UTF8ToUTF16(ClipboardFormatType::GetRtfType().GetName())); + base::UTF8ToUTF16(ClipboardFormatType::RtfType().GetName())); } - if (IsFormatAvailable(ClipboardFormatType::GetBitmapType(), buffer, - data_dst)) { + if (IsFormatAvailable(ClipboardFormatType::BitmapType(), buffer, data_dst)) { types.push_back(base::UTF8ToUTF16(kMimeTypePNG)); } @@ -609,8 +613,18 @@ void ClipboardNonBacked::ReadPng(ClipboardBuffer buffer, const DataTransferEndpoint* data_dst, ReadPngCallback callback) const { DCHECK(CalledOnValidThread()); - // TODO(crbug.com/1201018): Implement this. - NOTIMPLEMENTED(); + + if (!clipboard_internal_->IsReadAllowed(data_dst)) { + std::move(callback).Run(std::vector<uint8_t>()); + return; + } + + RecordRead(ClipboardFormatMetric::kPng); + std::move(callback).Run(clipboard_internal_->ReadPng()); + +#if BUILDFLAG(IS_CHROMEOS_ASH) + ClipboardMonitor::GetInstance()->NotifyClipboardDataRead(); +#endif } void ClipboardNonBacked::ReadImage(ClipboardBuffer buffer, @@ -707,26 +721,17 @@ bool ClipboardNonBacked::IsSelectionBufferAvailable() const { } #endif // defined(USE_OZONE) -void ClipboardNonBacked::WritePortableRepresentations( +void ClipboardNonBacked::WritePortableAndPlatformRepresentations( ClipboardBuffer buffer, const ObjectMap& objects, - std::unique_ptr<DataTransferEndpoint> data_src) { - DCHECK(CalledOnValidThread()); - DCHECK(IsSupportedClipboardBuffer(buffer)); - for (const auto& object : objects) - DispatchPortableRepresentation(object.first, object.second); - ClipboardDataBuilder::CommitToClipboard(clipboard_internal_.get(), - std::move(data_src)); -} - -void ClipboardNonBacked::WritePlatformRepresentations( - ClipboardBuffer buffer, std::vector<Clipboard::PlatformRepresentation> platform_representations, std::unique_ptr<DataTransferEndpoint> data_src) { DCHECK(CalledOnValidThread()); DCHECK(IsSupportedClipboardBuffer(buffer)); DispatchPlatformRepresentations(std::move(platform_representations)); + for (const auto& object : objects) + DispatchPortableRepresentation(object.first, object.second); ClipboardDataBuilder::CommitToClipboard(clipboard_internal_.get(), std::move(data_src)); diff --git a/chromium/ui/base/clipboard/clipboard_non_backed.h b/chromium/ui/base/clipboard/clipboard_non_backed.h index 17403e75391..7b67ca07b02 100644 --- a/chromium/ui/base/clipboard/clipboard_non_backed.h +++ b/chromium/ui/base/clipboard/clipboard_non_backed.h @@ -10,7 +10,6 @@ #include "base/component_export.h" #include "base/gtest_prod_util.h" -#include "base/macros.h" #include "ui/base/clipboard/clipboard.h" namespace ui { @@ -32,6 +31,9 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) ClipboardNonBacked // the current thread is in fact an instance of ClipboardNonBacked. static ClipboardNonBacked* GetForCurrentThread(); + ClipboardNonBacked(const ClipboardNonBacked&) = delete; + ClipboardNonBacked& operator=(const ClipboardNonBacked&) = delete; + // Returns the current ClipboardData. const ClipboardData* GetClipboardData(DataTransferEndpoint* data_dst) const; @@ -42,7 +44,8 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) ClipboardNonBacked // Clipboard overrides: DataTransferEndpoint* GetSource(ClipboardBuffer buffer) const override; - uint64_t GetSequenceNumber(ClipboardBuffer buffer) const override; + const ClipboardSequenceNumberToken& GetSequenceNumber( + ClipboardBuffer buffer) const override; private: friend class Clipboard; @@ -103,12 +106,9 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) ClipboardNonBacked #if defined(USE_OZONE) bool IsSelectionBufferAvailable() const override; #endif // defined(USE_OZONE) - void WritePortableRepresentations( + void WritePortableAndPlatformRepresentations( ClipboardBuffer buffer, const ObjectMap& objects, - std::unique_ptr<DataTransferEndpoint> data_src) override; - void WritePlatformRepresentations( - ClipboardBuffer buffer, std::vector<Clipboard::PlatformRepresentation> platform_representations, std::unique_ptr<DataTransferEndpoint> data_src) override; void WriteText(const char* text_data, size_t text_len) override; @@ -130,8 +130,6 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) ClipboardNonBacked size_t data_len) override; const std::unique_ptr<ClipboardInternal> clipboard_internal_; - - DISALLOW_COPY_AND_ASSIGN(ClipboardNonBacked); }; } // namespace ui diff --git a/chromium/ui/base/clipboard/clipboard_non_backed_unittest.cc b/chromium/ui/base/clipboard/clipboard_non_backed_unittest.cc index 963dbc4297f..9cba3aa94d3 100644 --- a/chromium/ui/base/clipboard/clipboard_non_backed_unittest.cc +++ b/chromium/ui/base/clipboard/clipboard_non_backed_unittest.cc @@ -8,12 +8,12 @@ #include <string> #include <vector> +#include "base/pickle.h" #include "base/strings/utf_string_conversions.h" #include "base/test/metrics/histogram_tester.h" -#include "base/test/scoped_feature_list.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/clipboard/clipboard_data.h" -#include "ui/base/ui_base_features.h" +#include "ui/base/clipboard/custom_data_helper.h" namespace ui { namespace { @@ -92,9 +92,7 @@ TEST_F(ClipboardNonBackedTest, AdminWriteDoesNotRecordHistograms) { // Tests that site bookmark URLs are accessed as text, and // IsFormatAvailable('text/uri-list') is only true for files. TEST_F(ClipboardNonBackedTest, TextURIList) { - base::test::ScopedFeatureList features; - features.InitWithFeatures({features::kClipboardFilenames}, {}); - EXPECT_EQ("text/uri-list", ClipboardFormatType::GetFilenamesType().GetName()); + EXPECT_EQ("text/uri-list", ClipboardFormatType::FilenamesType().GetName()); auto data = std::make_unique<ClipboardData>(); data->set_bookmark_url("http://example.com"); @@ -103,27 +101,47 @@ TEST_F(ClipboardNonBackedTest, TextURIList) { clipboard()->ReadAvailableTypes(ClipboardBuffer::kCopyPaste, /*data_dst=*/nullptr, &types); - // With bookmark data, available types should be only 'text/plain'. + // Bookmark data uses mime type 'text/plain'. EXPECT_EQ(std::vector<std::string>({"text/plain"}), UTF8Types(types)); - EXPECT_TRUE(clipboard()->IsFormatAvailable(ClipboardFormatType::GetUrlType(), + EXPECT_TRUE(clipboard()->IsFormatAvailable(ClipboardFormatType::UrlType(), ClipboardBuffer::kCopyPaste, /*data_dst=*/nullptr)); EXPECT_FALSE(clipboard()->IsFormatAvailable( - ClipboardFormatType::GetFilenamesType(), ClipboardBuffer::kCopyPaste, + ClipboardFormatType::FilenamesType(), ClipboardBuffer::kCopyPaste, /*data_dst=*/nullptr)); - // With filenames data, available types should be 'text/uri-list'. + // Filenames data uses mime type 'text/uri-list'. data = std::make_unique<ClipboardData>(); data->set_filenames({FileInfo(base::FilePath("/path"), base::FilePath())}); clipboard()->WriteClipboardData(std::move(data)); clipboard()->ReadAvailableTypes(ClipboardBuffer::kCopyPaste, /*data_dst=*/nullptr, &types); EXPECT_EQ(std::vector<std::string>({"text/uri-list"}), UTF8Types(types)); - EXPECT_FALSE(clipboard()->IsFormatAvailable(ClipboardFormatType::GetUrlType(), + EXPECT_FALSE(clipboard()->IsFormatAvailable(ClipboardFormatType::UrlType(), ClipboardBuffer::kCopyPaste, /*data_dst=*/nullptr)); EXPECT_TRUE(clipboard()->IsFormatAvailable( - ClipboardFormatType::GetFilenamesType(), ClipboardBuffer::kCopyPaste, + ClipboardFormatType::FilenamesType(), ClipboardBuffer::kCopyPaste, + /*data_dst=*/nullptr)); + + // Filenames data uses mime type 'text/uri-list', but clients can also set + // 'text/uri-list' as one of the custom data types. When it is set as a custom + // type, it will be returned from ReadAvailableTypes(), but + // IsFormatAvailable(FilenamesType()) is false. + data = std::make_unique<ClipboardData>(); + base::flat_map<std::u16string, std::u16string> custom_data; + custom_data[u"text/uri-list"] = u"data"; + base::Pickle pickle; + ui::WriteCustomDataToPickle(custom_data, &pickle); + data->SetCustomData( + ui::ClipboardFormatType::WebCustomDataType().Serialize(), + std::string(static_cast<const char*>(pickle.data()), pickle.size())); + clipboard()->WriteClipboardData(std::move(data)); + clipboard()->ReadAvailableTypes(ClipboardBuffer::kCopyPaste, + /*data_dst=*/nullptr, &types); + EXPECT_EQ(std::vector<std::string>({"text/uri-list"}), UTF8Types(types)); + EXPECT_FALSE(clipboard()->IsFormatAvailable( + ClipboardFormatType::FilenamesType(), ClipboardBuffer::kCopyPaste, /*data_dst=*/nullptr)); } diff --git a/chromium/ui/base/clipboard/clipboard_ozone.cc b/chromium/ui/base/clipboard/clipboard_ozone.cc index 12e3371b4ee..62823f77885 100644 --- a/chromium/ui/base/clipboard/clipboard_ozone.cc +++ b/chromium/ui/base/clipboard/clipboard_ozone.cc @@ -13,7 +13,6 @@ #include "base/containers/contains.h" #include "base/containers/flat_map.h" #include "base/containers/span.h" -#include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/notreached.h" #include "base/run_loop.h" @@ -99,7 +98,8 @@ class ClipboardOzone::AsyncClipboardOzone { platform_clipboard_->SetClipboardDataChangedCallback( std::move(update_sequence_cb)); } - + AsyncClipboardOzone(const AsyncClipboardOzone&) = delete; + AsyncClipboardOzone& operator=(const AsyncClipboardOzone&) = delete; ~AsyncClipboardOzone() = default; bool IsSelectionBufferAvailable() const { @@ -164,7 +164,8 @@ class ClipboardOzone::AsyncClipboardOzone { } } - uint64_t GetSequenceNumber(ClipboardBuffer buffer) { + const ClipboardSequenceNumberToken& GetSequenceNumber( + ClipboardBuffer buffer) const { return buffer == ClipboardBuffer::kCopyPaste ? clipboard_sequence_number_ : selection_sequence_number_; } @@ -275,9 +276,9 @@ class ClipboardOzone::AsyncClipboardOzone { DCHECK(buffer == ClipboardBuffer::kCopyPaste || platform_clipboard_->IsSelectionBufferAvailable()); if (buffer == ClipboardBuffer::kCopyPaste) - clipboard_sequence_number_++; + clipboard_sequence_number_ = ClipboardSequenceNumberToken(); else - selection_sequence_number_++; + selection_sequence_number_ = ClipboardSequenceNumberToken(); ClipboardMonitor::GetInstance()->NotifyClipboardDataChanged(); } @@ -296,12 +297,10 @@ class ClipboardOzone::AsyncClipboardOzone { // Provides communication to a system clipboard under ozone level. PlatformClipboard* const platform_clipboard_ = nullptr; - uint64_t clipboard_sequence_number_ = 0; - uint64_t selection_sequence_number_ = 0; + ClipboardSequenceNumberToken clipboard_sequence_number_; + ClipboardSequenceNumberToken selection_sequence_number_; base::WeakPtrFactory<AsyncClipboardOzone> weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(AsyncClipboardOzone); }; // ClipboardOzone implementation. @@ -329,7 +328,8 @@ DataTransferEndpoint* ClipboardOzone::GetSource(ClipboardBuffer buffer) const { return it == data_src_.end() ? nullptr : it->second.get(); } -uint64_t ClipboardOzone::GetSequenceNumber(ClipboardBuffer buffer) const { +const ClipboardSequenceNumberToken& ClipboardOzone::GetSequenceNumber( + ClipboardBuffer buffer) const { return async_clipboard_ozone_->GetSequenceNumber(buffer); } @@ -364,9 +364,9 @@ void ClipboardOzone::ReadAvailableTypes( // Special handling for chromium/x-web-custom-data. // We must read the data and deserialize it to find the list // of mime types to report. - if (mime_type == ClipboardFormatType::GetWebCustomDataType().GetName()) { + if (mime_type == ClipboardFormatType::WebCustomDataType().GetName()) { auto data = async_clipboard_ozone_->ReadClipboardDataAndWait( - buffer, ClipboardFormatType::GetWebCustomDataType().GetName()); + buffer, ClipboardFormatType::WebCustomDataType().GetName()); ReadCustomDataTypes(data.data(), data.size(), types); } else { types->push_back(base::UTF8ToUTF16(mime_type)); @@ -468,8 +468,8 @@ void ClipboardOzone::ReadRTF(ClipboardBuffer buffer, void ClipboardOzone::ReadPng(ClipboardBuffer buffer, const DataTransferEndpoint* data_dst, ReadPngCallback callback) const { - // TODO(crbug.com/1201018): Implement this. - NOTIMPLEMENTED(); + RecordRead(ClipboardFormatMetric::kPng); + std::move(callback).Run(ReadPngInternal(buffer)); } // TODO(crbug.com/1103194): |data_dst| should be supported. @@ -477,7 +477,10 @@ void ClipboardOzone::ReadImage(ClipboardBuffer buffer, const DataTransferEndpoint* data_dst, ReadImageCallback callback) const { RecordRead(ClipboardFormatMetric::kImage); - std::move(callback).Run(ReadImageInternal(buffer)); + auto png_data = ReadPngInternal(buffer); + SkBitmap bitmap; + gfx::PNGCodec::Decode(png_data.data(), png_data.size(), &bitmap); + std::move(callback).Run(bitmap); } // TODO(crbug.com/1103194): |data_dst| should be supported. @@ -531,18 +534,8 @@ bool ClipboardOzone::IsSelectionBufferAvailable() const { return async_clipboard_ozone_->IsSelectionBufferAvailable(); } -// TODO(crbug.com/1103194): |data_src| should be supported -void ClipboardOzone::WritePortableRepresentations( - ClipboardBuffer buffer, - const ObjectMap& objects, - std::unique_ptr<DataTransferEndpoint> data_src) { - DCHECK(CalledOnValidThread()); - - async_clipboard_ozone_->PrepareForWriting(); - for (const auto& object : objects) - DispatchPortableRepresentation(object.first, object.second); - async_clipboard_ozone_->OfferData(buffer); - +void ClipboardOzone::WritePortableTextRepresentation(ClipboardBuffer buffer, + const ObjectMap& objects) { // Just like Non-Backed/X11 implementation does, copy text data from the // copy/paste selection to the primary selection. if (buffer == ClipboardBuffer::kCopyPaste && IsSelectionBufferAvailable()) { @@ -555,21 +548,24 @@ void ClipboardOzone::WritePortableRepresentations( async_clipboard_ozone_->OfferData(ClipboardBuffer::kSelection); } } - - data_src_[buffer] = std::move(data_src); } // TODO(crbug.com/1103194): |data_src| should be supported -void ClipboardOzone::WritePlatformRepresentations( +void ClipboardOzone::WritePortableAndPlatformRepresentations( ClipboardBuffer buffer, + const ObjectMap& objects, std::vector<Clipboard::PlatformRepresentation> platform_representations, std::unique_ptr<DataTransferEndpoint> data_src) { DCHECK(CalledOnValidThread()); async_clipboard_ozone_->PrepareForWriting(); DispatchPlatformRepresentations(std::move(platform_representations)); + for (const auto& object : objects) + DispatchPortableRepresentation(object.first, object.second); async_clipboard_ozone_->OfferData(buffer); + WritePortableTextRepresentation(buffer, objects); + data_src_[buffer] = std::move(data_src); } @@ -637,16 +633,13 @@ void ClipboardOzone::WriteData(const ClipboardFormatType& format, async_clipboard_ozone_->InsertData(std::move(data), {format.GetName()}); } -SkBitmap ClipboardOzone::ReadImageInternal(ClipboardBuffer buffer) const { +std::vector<uint8_t> ClipboardOzone::ReadPngInternal( + ClipboardBuffer buffer) const { DCHECK(CalledOnValidThread()); - auto clipboard_data = + base::span<uint8_t> clipboard_data = async_clipboard_ozone_->ReadClipboardDataAndWait(buffer, kMimeTypePNG); - SkBitmap bitmap; - if (!gfx::PNGCodec::Decode(clipboard_data.data(), clipboard_data.size(), - &bitmap)) - return {}; - return SkBitmap(bitmap); + return std::vector<uint8_t>(clipboard_data.begin(), clipboard_data.end()); } } // namespace ui diff --git a/chromium/ui/base/clipboard/clipboard_ozone.h b/chromium/ui/base/clipboard/clipboard_ozone.h index 730cf5d318c..a4b1f2a2788 100644 --- a/chromium/ui/base/clipboard/clipboard_ozone.h +++ b/chromium/ui/base/clipboard/clipboard_ozone.h @@ -9,7 +9,6 @@ #include <string> #include <vector> -#include "base/macros.h" #include "ui/base/clipboard/clipboard.h" namespace ui { @@ -17,6 +16,10 @@ namespace ui { // ClipboardOzone is not yet shipped in production. It is a work in progress // for desktop Linux Wayland support. class ClipboardOzone : public Clipboard { + public: + ClipboardOzone(const ClipboardOzone&) = delete; + ClipboardOzone& operator=(const ClipboardOzone&) = delete; + private: friend class Clipboard; @@ -26,7 +29,8 @@ class ClipboardOzone : public Clipboard { // Clipboard overrides: void OnPreShutdown() override; DataTransferEndpoint* GetSource(ClipboardBuffer buffer) const override; - uint64_t GetSequenceNumber(ClipboardBuffer buffer) const override; + const ClipboardSequenceNumberToken& GetSequenceNumber( + ClipboardBuffer buffer) const override; bool IsFormatAvailable(const ClipboardFormatType& format, ClipboardBuffer buffer, const DataTransferEndpoint* data_dst) const override; @@ -75,12 +79,11 @@ class ClipboardOzone : public Clipboard { const DataTransferEndpoint* data_dst, std::string* result) const override; bool IsSelectionBufferAvailable() const override; - void WritePortableRepresentations( + void WritePortableTextRepresentation(ClipboardBuffer buffer, + const ObjectMap& objects); + void WritePortableAndPlatformRepresentations( ClipboardBuffer buffer, const ObjectMap& objects, - std::unique_ptr<DataTransferEndpoint> data_src) override; - void WritePlatformRepresentations( - ClipboardBuffer buffer, std::vector<Clipboard::PlatformRepresentation> platform_representations, std::unique_ptr<DataTransferEndpoint> data_src) override; void WriteText(const char* text_data, size_t text_len) override; @@ -101,15 +104,13 @@ class ClipboardOzone : public Clipboard { const char* data_data, size_t data_len) override; - SkBitmap ReadImageInternal(ClipboardBuffer buffer) const; + std::vector<uint8_t> ReadPngInternal(ClipboardBuffer buffer) const; class AsyncClipboardOzone; std::unique_ptr<AsyncClipboardOzone> async_clipboard_ozone_; base::flat_map<ClipboardBuffer, std::unique_ptr<DataTransferEndpoint>> data_src_; - - DISALLOW_COPY_AND_ASSIGN(ClipboardOzone); }; } // namespace ui diff --git a/chromium/ui/base/clipboard/clipboard_sequence_number_token.h b/chromium/ui/base/clipboard/clipboard_sequence_number_token.h new file mode 100644 index 00000000000..b76e1630f60 --- /dev/null +++ b/chromium/ui/base/clipboard/clipboard_sequence_number_token.h @@ -0,0 +1,19 @@ +// Copyright (c) 2021 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_BASE_CLIPBOARD_CLIPBOARD_SEQUENCE_NUMBER_TOKEN_H_ +#define UI_BASE_CLIPBOARD_CLIPBOARD_SEQUENCE_NUMBER_TOKEN_H_ + +#include "base/types/token_type.h" + +namespace ui { + +// Identifies a unique clipboard state. This can be used to version the data on +// the clipboard and determine whether it has changed. +using ClipboardSequenceNumberToken = + base::TokenType<class ClipboardSequenceNumberTokenTypeMarker>; + +} // namespace ui + +#endif // UI_BASE_CLIPBOARD_CLIPBOARD_SEQUENCE_NUMBER_TOKEN_H_ diff --git a/chromium/ui/base/clipboard/clipboard_test_template.h b/chromium/ui/base/clipboard/clipboard_test_template.h index e89e6263a52..494b05094c7 100644 --- a/chromium/ui/base/clipboard/clipboard_test_template.h +++ b/chromium/ui/base/clipboard/clipboard_test_template.h @@ -20,6 +20,7 @@ #include <array> #include <memory> #include <string> +#include <vector> #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" @@ -27,7 +28,6 @@ #include "base/run_loop.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" -#include "base/test/scoped_feature_list.h" #include "base/threading/thread_restrictions.h" #include "build/build_config.h" #include "build/chromecast_buildflags.h" @@ -47,7 +47,7 @@ #include "ui/base/clipboard/test/test_clipboard.h" #include "ui/base/data_transfer_policy/data_transfer_endpoint.h" #include "ui/base/data_transfer_policy/data_transfer_policy_controller.h" -#include "ui/base/ui_base_features.h" +#include "ui/gfx/codec/png_codec.h" #include "ui/gfx/geometry/size.h" #include "ui/gfx/half_float.h" #include "url/origin.h" @@ -112,13 +112,15 @@ class MockPolicyController : public DataTransferPolicyController { MockPolicyController(); ~MockPolicyController() override; - MOCK_METHOD2(IsClipboardReadAllowed, + MOCK_METHOD3(IsClipboardReadAllowed, bool(const DataTransferEndpoint* const data_src, - const DataTransferEndpoint* const data_dst)); - MOCK_METHOD4(PasteIfAllowed, + const DataTransferEndpoint* const data_dst, + const absl::optional<size_t> size)); + MOCK_METHOD5(PasteIfAllowed, void(const DataTransferEndpoint* const data_src, const DataTransferEndpoint* const data_dst, - content::WebContents* web_contents, + const absl::optional<size_t> size, + content::RenderFrameHost* rfh, base::OnceCallback<void(bool)> callback)); MOCK_METHOD3(IsDragDropAllowed, bool(const DataTransferEndpoint* const data_src, @@ -149,16 +151,22 @@ TYPED_TEST(ClipboardTest, ClearTest) { EXPECT_TRUE(this->GetAvailableTypes(ClipboardBuffer::kCopyPaste).empty()); EXPECT_FALSE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetPlainTextType(), ClipboardBuffer::kCopyPaste, + ClipboardFormatType::PlainTextType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)); #if defined(OS_WIN) EXPECT_FALSE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetPlainTextAType(), ClipboardBuffer::kCopyPaste, + ClipboardFormatType::PlainTextAType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)); #endif } -TYPED_TEST(ClipboardTest, TextTest) { +// crbug.com/1224904: Flaky on Mac. +#if defined(OS_MAC) +#define MAYBE_TextTest DISABLED_TextTest +#else +#define MAYBE_TextTest TextTest +#endif +TYPED_TEST(ClipboardTest, MAYBE_TextTest) { std::u16string text(u"This is a std::u16string!#$"), text_result; std::string ascii_text; @@ -180,11 +188,11 @@ TYPED_TEST(ClipboardTest, TextTest) { } #endif EXPECT_TRUE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetPlainTextType(), ClipboardBuffer::kCopyPaste, + ClipboardFormatType::PlainTextType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)); #if defined(OS_WIN) EXPECT_TRUE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetPlainTextAType(), ClipboardBuffer::kCopyPaste, + ClipboardFormatType::PlainTextAType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)); #endif @@ -211,7 +219,7 @@ TYPED_TEST(ClipboardTest, HTMLTest) { EXPECT_THAT(this->GetAvailableTypes(ClipboardBuffer::kCopyPaste), Contains(ASCIIToUTF16(kMimeTypeHTML))); EXPECT_TRUE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetHtmlType(), ClipboardBuffer::kCopyPaste, + ClipboardFormatType::HtmlType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)); uint32_t fragment_start; uint32_t fragment_end; @@ -237,7 +245,7 @@ TYPED_TEST(ClipboardTest, SvgTest) { } EXPECT_TRUE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetSvgType(), ClipboardBuffer::kCopyPaste, + ClipboardFormatType::SvgType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)); std::u16string markup_result; @@ -264,7 +272,7 @@ TYPED_TEST(ClipboardTest, RTFTest) { EXPECT_THAT(this->GetAvailableTypes(ClipboardBuffer::kCopyPaste), Contains(ASCIIToUTF16(kMimeTypeRTF))); EXPECT_TRUE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetRtfType(), ClipboardBuffer::kCopyPaste, + ClipboardFormatType::RtfType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)); std::string result; this->clipboard().ReadRTF(ClipboardBuffer::kCopyPaste, @@ -302,17 +310,17 @@ TYPED_TEST(ClipboardTest, MultipleBufferTest) { Contains(ASCIIToUTF16(kMimeTypeHTML))); EXPECT_TRUE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetPlainTextType(), ClipboardBuffer::kCopyPaste, + ClipboardFormatType::PlainTextType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)); EXPECT_FALSE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetPlainTextType(), ClipboardBuffer::kSelection, + ClipboardFormatType::PlainTextType(), ClipboardBuffer::kSelection, /* data_dst = */ nullptr)); EXPECT_FALSE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetHtmlType(), ClipboardBuffer::kCopyPaste, + ClipboardFormatType::HtmlType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)); EXPECT_TRUE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetHtmlType(), ClipboardBuffer::kSelection, + ClipboardFormatType::HtmlType(), ClipboardBuffer::kSelection, /* data_dst = */ nullptr)); this->clipboard().ReadText(ClipboardBuffer::kCopyPaste, @@ -345,7 +353,7 @@ TYPED_TEST(ClipboardTest, TrickyHTMLTest) { EXPECT_THAT(this->GetAvailableTypes(ClipboardBuffer::kCopyPaste), Contains(ASCIIToUTF16(kMimeTypeHTML))); EXPECT_TRUE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetHtmlType(), ClipboardBuffer::kCopyPaste, + ClipboardFormatType::HtmlType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)); uint32_t fragment_start; uint32_t fragment_end; @@ -362,9 +370,15 @@ TYPED_TEST(ClipboardTest, TrickyHTMLTest) { #endif // defined(OS_WIN) } +// crbug.com/1224904: Flaky on Mac. +#if defined(OS_MAC) +#define MAYBE_UnicodeHTMLTest DISABLED_UnicodeHTMLTest +#else +#define MAYBE_UnicodeHTMLTest UnicodeHTMLTest +#endif // Some platforms store HTML as UTF-8 internally. Make sure fragment indices are // adjusted appropriately when converting back to UTF-16. -TYPED_TEST(ClipboardTest, UnicodeHTMLTest) { +TYPED_TEST(ClipboardTest, MAYBE_UnicodeHTMLTest) { std::u16string markup(u"<div>A ø 水</div>"), markup_result; std::string url, url_result; @@ -380,7 +394,7 @@ TYPED_TEST(ClipboardTest, UnicodeHTMLTest) { EXPECT_THAT(this->GetAvailableTypes(ClipboardBuffer::kCopyPaste), Contains(ASCIIToUTF16(kMimeTypeHTML))); EXPECT_TRUE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetHtmlType(), ClipboardBuffer::kCopyPaste, + ClipboardFormatType::HtmlType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)); uint32_t fragment_start; uint32_t fragment_end; @@ -407,7 +421,7 @@ TYPED_TEST(ClipboardTest, BookmarkTest) { } EXPECT_TRUE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetUrlType(), ClipboardBuffer::kCopyPaste, + ClipboardFormatType::UrlType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)); this->clipboard().ReadBookmark(/* data_dst = */ nullptr, &title_result, &url_result); @@ -417,10 +431,14 @@ TYPED_TEST(ClipboardTest, BookmarkTest) { #endif // !defined(OS_POSIX) || defined(OS_APPLE) #if !defined(OS_ANDROID) +// crbug.com/1224904: Flaky on Mac. +#if defined(OS_MAC) +#define MAYBE_FilenamesTest DISABLED_FilenamesTest +#else +#define MAYBE_FilenamesTest FilenamesTest +#endif // Filenames is not implemented in ClipboardAndroid. -TYPED_TEST(ClipboardTest, FilenamesTest) { - base::test::ScopedFeatureList features; - features.InitWithFeatures({features::kClipboardFilenames}, {}); +TYPED_TEST(ClipboardTest, MAYBE_FilenamesTest) { base::ScopedAllowBlockingForTesting allow_blocking; base::ScopedTempDir temp_dir; ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); @@ -434,7 +452,7 @@ TYPED_TEST(ClipboardTest, FilenamesTest) { } EXPECT_TRUE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetFilenamesType(), ClipboardBuffer::kCopyPaste, + ClipboardFormatType::FilenamesType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)); std::vector<std::u16string> types; @@ -451,7 +469,13 @@ TYPED_TEST(ClipboardTest, FilenamesTest) { } #endif // !defined(OS_ANDROID) -TYPED_TEST(ClipboardTest, MultiFormatTest) { +// crbug.com/1224904: Flaky on Mac. +#if defined(OS_MAC) +#define MAYBE_MultiFormatTest DISABLED_MultiFormatTest +#else +#define MAYBE_MultiFormatTest MultiFormatTest +#endif +TYPED_TEST(ClipboardTest, MAYBE_MultiFormatTest) { std::u16string text(u"Hi!"), text_result; std::u16string markup(u"<strong>Hi!</string>"), markup_result; std::string url("http://www.example.com/"), url_result; @@ -468,14 +492,14 @@ TYPED_TEST(ClipboardTest, MultiFormatTest) { EXPECT_THAT(this->GetAvailableTypes(ClipboardBuffer::kCopyPaste), Contains(ASCIIToUTF16(kMimeTypeText))); EXPECT_TRUE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetHtmlType(), ClipboardBuffer::kCopyPaste, + ClipboardFormatType::HtmlType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)); EXPECT_TRUE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetPlainTextType(), ClipboardBuffer::kCopyPaste, + ClipboardFormatType::PlainTextType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)); #if defined(OS_WIN) EXPECT_TRUE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetPlainTextAType(), ClipboardBuffer::kCopyPaste, + ClipboardFormatType::PlainTextAType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)); #endif uint32_t fragment_start; @@ -510,11 +534,11 @@ TYPED_TEST(ClipboardTest, URLTest) { EXPECT_THAT(this->GetAvailableTypes(ClipboardBuffer::kCopyPaste), Contains(ASCIIToUTF16(kMimeTypeText))); EXPECT_TRUE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetPlainTextType(), ClipboardBuffer::kCopyPaste, + ClipboardFormatType::PlainTextType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)); #if defined(OS_WIN) EXPECT_TRUE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetPlainTextAType(), ClipboardBuffer::kCopyPaste, + ClipboardFormatType::PlainTextAType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)); #endif std::u16string text_result; @@ -544,24 +568,21 @@ namespace { using U8x4 = std::array<uint8_t, 4>; using F16x4 = std::array<gfx::HalfFloat, 4>; -template <typename T> -static void TestBitmapWrite(Clipboard* clipboard, - const SkImageInfo& info, - const T* bitmap_data, - const U8x4* expect_data) { +void WriteBitmap(Clipboard* clipboard, + const SkImageInfo& info, + const void* bitmap_data) { { - ScopedClipboardWriter scw(ClipboardBuffer::kCopyPaste); + ScopedClipboardWriter clipboard_writer(ClipboardBuffer::kCopyPaste); SkBitmap bitmap; ASSERT_TRUE(bitmap.setInfo(info)); - bitmap.setPixels( - const_cast<void*>(reinterpret_cast<const void*>(bitmap_data))); - scw.WriteImage(bitmap); + bitmap.setPixels(const_cast<void*>(bitmap_data)); + clipboard_writer.WriteImage(bitmap); } +} - EXPECT_TRUE(clipboard->IsFormatAvailable(ClipboardFormatType::GetBitmapType(), - ClipboardBuffer::kCopyPaste, - /* data_dst = */ nullptr)); - const SkBitmap& image = clipboard_test_util::ReadImage(clipboard); +void AssertBitmapMatchesExpected(const SkBitmap& image, + const SkImageInfo& info, + const U8x4* expect_data) { ASSERT_EQ(image.info().colorType(), kN32_SkColorType); ASSERT_NE(image.info().alphaType(), kUnpremul_SkAlphaType); EXPECT_EQ(gfx::Size(info.width(), info.height()), @@ -576,9 +597,42 @@ static void TestBitmapWrite(Clipboard* clipboard, } } +template <typename T> +static void TestBitmapWrite(Clipboard* clipboard, + const SkImageInfo& info, + const T* bitmap_data, + const U8x4* expect_data) { + WriteBitmap(clipboard, info, reinterpret_cast<const void*>(bitmap_data)); + + EXPECT_TRUE(clipboard->IsFormatAvailable(ClipboardFormatType::BitmapType(), + ClipboardBuffer::kCopyPaste, + /* data_dst = */ nullptr)); + const SkBitmap& image = clipboard_test_util::ReadImage(clipboard); + AssertBitmapMatchesExpected(image, info, expect_data); +} + +template <typename T> +static void TestBitmapWriteAndPngRead(Clipboard* clipboard, + const SkImageInfo& info, + const T* bitmap_data, + const U8x4* expect_data) { + WriteBitmap(clipboard, info, reinterpret_cast<const void*>(bitmap_data)); + + // Expect to be able to read images as either bitmaps or PNGs. + EXPECT_TRUE(clipboard->IsFormatAvailable(ClipboardFormatType::BitmapType(), + ClipboardBuffer::kCopyPaste, + /* data_dst = */ nullptr)); + EXPECT_TRUE(clipboard->IsFormatAvailable(ClipboardFormatType::PngType(), + ClipboardBuffer::kCopyPaste, + /* data_dst = */ nullptr)); + std::vector<uint8_t> result = clipboard_test_util::ReadPng(clipboard); + SkBitmap image; + gfx::PNGCodec::Decode(result.data(), result.size(), &image); + AssertBitmapMatchesExpected(image, info, expect_data); +} + #if !defined(OS_ANDROID) -// TODO(https://crbug.com/1056650): Re-enable these tests after fixing the root -// cause. This test only fails on Android. +// TODO(crbug.com/815537): Re-enable this test once death tests work on Android. // Only kN32_SkColorType bitmaps are allowed in the clipboard to prevent // surprising buffer overflows due to bits-per-pixel assumptions. @@ -591,8 +645,15 @@ TYPED_TEST(ClipboardTest, Bitmap_F16_Premul) { &kRGBAF16Premul, &kRGBAPremul), ""); } +#endif // !defined(OS_ANDROID) -TYPED_TEST(ClipboardTest, Bitmap_N32_Premul) { +// crbug.com/1224904: Flaky on Mac. +#if defined(OS_MAC) +#define MAYBE_Bitmap_N32_Premul DISABLED_Bitmap_N32_Premul +#else +#define MAYBE_Bitmap_N32_Premul Bitmap_N32_Premul +#endif +TYPED_TEST(ClipboardTest, MAYBE_Bitmap_N32_Premul) { constexpr U8x4 b[4 * 3] = { {0x26, 0x16, 0x06, 0x46}, {0x88, 0x59, 0x9f, 0xf6}, {0x37, 0x29, 0x3f, 0x79}, {0x86, 0xb9, 0x55, 0xfa}, @@ -603,6 +664,7 @@ TYPED_TEST(ClipboardTest, Bitmap_N32_Premul) { }; TestBitmapWrite(&this->clipboard(), SkImageInfo::MakeN32Premul(4, 3), b, b); } + TYPED_TEST(ClipboardTest, Bitmap_N32_Premul_2x7) { constexpr U8x4 b[2 * 7] = { {0x26, 0x16, 0x06, 0x46}, {0x88, 0x59, 0x9f, 0xf6}, @@ -615,8 +677,51 @@ TYPED_TEST(ClipboardTest, Bitmap_N32_Premul_2x7) { }; TestBitmapWrite(&this->clipboard(), SkImageInfo::MakeN32Premul(2, 7), b, b); } + +#if !defined(OS_ANDROID) +// TODO(crbug.com/815537): Re-enable this test once death tests work on Android. + +// Only kN32_SkColorType bitmaps are allowed into the clipboard to prevent +// surprising buffer overflows due to bits-per-pixel assumptions. +TYPED_TEST(ClipboardTest, BitmapWriteAndPngRead_F16_Premul) { + constexpr F16x4 kRGBAF16Premul = {0x30c5, 0x2d86, 0x2606, 0x3464}; + constexpr U8x4 kRGBAPremul = {0x26, 0x16, 0x06, 0x46}; + EXPECT_DEATH( + TestBitmapWriteAndPngRead( + &this->clipboard(), + SkImageInfo::Make(1, 1, kRGBA_F16_SkColorType, kPremul_SkAlphaType), + &kRGBAF16Premul, &kRGBAPremul), + ""); +} #endif // !defined(OS_ANDROID) +TYPED_TEST(ClipboardTest, BitmapWriteAndPngRead_N32_Premul) { + constexpr U8x4 b[4 * 3] = { + {0x26, 0x16, 0x06, 0x46}, {0x88, 0x59, 0x9f, 0xf6}, + {0x37, 0x29, 0x3f, 0x79}, {0x86, 0xb9, 0x55, 0xfa}, + {0x52, 0x21, 0x77, 0x78}, {0x30, 0x2a, 0x69, 0x87}, + {0x25, 0x2a, 0x32, 0x36}, {0x1b, 0x40, 0x20, 0x43}, + {0x21, 0x8c, 0x84, 0x91}, {0x3c, 0x7b, 0x17, 0xc3}, + {0x5c, 0x15, 0x46, 0x69}, {0x52, 0x19, 0x17, 0x64}, + }; + TestBitmapWriteAndPngRead(&this->clipboard(), + SkImageInfo::MakeN32Premul(4, 3), b, b); +} + +TYPED_TEST(ClipboardTest, BitmapWriteAndPngRead_N32_Premul_2x7) { + constexpr U8x4 b[2 * 7] = { + {0x26, 0x16, 0x06, 0x46}, {0x88, 0x59, 0x9f, 0xf6}, + {0x37, 0x29, 0x3f, 0x79}, {0x86, 0xb9, 0x55, 0xfa}, + {0x52, 0x21, 0x77, 0x78}, {0x30, 0x2a, 0x69, 0x87}, + {0x25, 0x2a, 0x32, 0x36}, {0x1b, 0x40, 0x20, 0x43}, + {0x21, 0x8c, 0x84, 0x91}, {0x3c, 0x7b, 0x17, 0xc3}, + {0x5c, 0x15, 0x46, 0x69}, {0x52, 0x19, 0x17, 0x64}, + {0x13, 0x03, 0x91, 0xa6}, {0x3e, 0x32, 0x02, 0x83}, + }; + TestBitmapWriteAndPngRead(&this->clipboard(), + SkImageInfo::MakeN32Premul(2, 7), b, b); +} + } // namespace TYPED_TEST(ClipboardTest, PickleTest) { @@ -637,7 +742,7 @@ TYPED_TEST(ClipboardTest, PickleTest) { this->clipboard().ReadData(kFormat, /* data_dst = */ nullptr, &output); ASSERT_FALSE(output.empty()); - base::Pickle read_pickle(output.data(), static_cast<int>(output.size())); + base::Pickle read_pickle(output.data(), output.size()); base::PickleIterator iter(read_pickle); std::string unpickled_string; ASSERT_TRUE(iter.ReadString(&unpickled_string)); @@ -674,7 +779,7 @@ TYPED_TEST(ClipboardTest, MultiplePickleTest) { this->clipboard().ReadData(kFormat2, /* data_dst = */ nullptr, &output2); ASSERT_FALSE(output2.empty()); - base::Pickle read_pickle2(output2.data(), static_cast<int>(output2.size())); + base::Pickle read_pickle2(output2.data(), output2.size()); base::PickleIterator iter2(read_pickle2); std::string unpickled_string2; ASSERT_TRUE(iter2.ReadString(&unpickled_string2)); @@ -697,18 +802,18 @@ TYPED_TEST(ClipboardTest, MultiplePickleTest) { this->clipboard().ReadData(kFormat1, /* data_dst = */ nullptr, &output1); ASSERT_FALSE(output1.empty()); - base::Pickle read_pickle1(output1.data(), static_cast<int>(output1.size())); + base::Pickle read_pickle1(output1.data(), output1.size()); base::PickleIterator iter1(read_pickle1); std::string unpickled_string1; ASSERT_TRUE(iter1.ReadString(&unpickled_string1)); EXPECT_EQ(payload1, unpickled_string1); } +// TODO(crbug.com/106449): Implement custom formats on other platforms. +#if defined(OS_WIN) TYPED_TEST(ClipboardTest, DataTest) { const std::string kFormatString = "chromium/x-test-format"; const std::u16string kFormatString16 = u"chromium/x-test-format"; - const ClipboardFormatType kFormat = - ClipboardFormatType::GetType(kFormatString); const std::string payload = "test string"; base::span<const uint8_t> payload_span( reinterpret_cast<const uint8_t*>(payload.data()), payload.size()); @@ -719,33 +824,28 @@ TYPED_TEST(ClipboardTest, DataTest) { mojo_base::BigBuffer(payload_span)); } - ASSERT_TRUE(this->clipboard().IsFormatAvailable( - kFormat, ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)); + std::map<std::string, std::string> custom_format_names = + this->clipboard().ExtractCustomPlatformNames(ClipboardBuffer::kCopyPaste, + /* data_dst = */ nullptr); + EXPECT_TRUE(custom_format_names.find(kFormatString) != + custom_format_names.end()); std::string output; - this->clipboard().ReadData(kFormat, /* data_dst = */ nullptr, &output); + this->clipboard().ReadData(ClipboardFormatType::CustomPlatformType( + custom_format_names[kFormatString]), + /* data_dst = */ nullptr, &output); EXPECT_EQ(payload, output); } -// TODO(https://crbug.com/1032161): Implement multiple raw types for -// ClipboardInternal. This test currently doesn't run on ClipboardInternal -// because ClipboardInternal only supports one raw type. -#if (!defined(USE_AURA) || defined(OS_WIN) || defined(USE_OZONE) || \ - defined(USE_X11)) && \ - !BUILDFLAG(IS_CHROMEOS_ASH) TYPED_TEST(ClipboardTest, MultipleDataTest) { const std::string kFormatString1 = "chromium/x-test-format1"; const std::u16string kFormatString116 = u"chromium/x-test-format1"; - const ClipboardFormatType kFormat1 = - ClipboardFormatType::GetType(kFormatString1); const std::string payload1("test string1"); base::span<const uint8_t> payload_span1( reinterpret_cast<const uint8_t*>(payload1.data()), payload1.size()); const std::string kFormatString2 = "chromium/x-test-format2"; const std::u16string kFormatString216 = u"chromium/x-test-format2"; - const ClipboardFormatType kFormat2 = - ClipboardFormatType::GetType(kFormatString2); const std::string payload2("test string2"); base::span<const uint8_t> payload_span2( reinterpret_cast<const uint8_t*>(payload2.data()), payload2.size()); @@ -763,29 +863,112 @@ TYPED_TEST(ClipboardTest, MultipleDataTest) { EXPECT_THAT(this->clipboard().ReadAvailablePlatformSpecificFormatNames( ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr), Contains(kFormatString116)); - EXPECT_TRUE(this->clipboard().IsFormatAvailable( - kFormat1, ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)); + std::string custom_format_json; + this->clipboard().ReadData(ClipboardFormatType::WebCustomFormatMap(), + /* data_dst = */ nullptr, &custom_format_json); + std::map<std::string, std::string> custom_format_names = + this->clipboard().ExtractCustomPlatformNames(ClipboardBuffer::kCopyPaste, + /* data_dst = */ nullptr); + EXPECT_TRUE(custom_format_names.find(kFormatString1) != + custom_format_names.end()); std::string output1; - this->clipboard().ReadData(kFormat1, /* data_dst = */ nullptr, &output1); + this->clipboard().ReadData(ClipboardFormatType::CustomPlatformType( + custom_format_names[kFormatString1]), + /* data_dst = */ nullptr, &output1); EXPECT_EQ(payload1, output1); // Check format 2. EXPECT_THAT(this->clipboard().ReadAvailablePlatformSpecificFormatNames( ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr), Contains(kFormatString216)); - EXPECT_TRUE(this->clipboard().IsFormatAvailable( - kFormat2, ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)); + EXPECT_TRUE(custom_format_names.find(kFormatString2) != + custom_format_names.end()); std::string output2; - this->clipboard().ReadData(kFormat2, /* data_dst = */ nullptr, &output2); + this->clipboard().ReadData(ClipboardFormatType::CustomPlatformType( + custom_format_names[kFormatString2]), + /* data_dst = */ nullptr, &output2); EXPECT_EQ(payload2, output2); } + +TYPED_TEST(ClipboardTest, DataAndPortableFormatTest) { + const std::string kFormatString1 = "chromium/x-test-format1"; + const std::u16string kFormatString116 = u"chromium/x-test-format1"; + const std::string payload1("test string1"); + base::span<const uint8_t> payload_span1( + reinterpret_cast<const uint8_t*>(payload1.data()), payload1.size()); + + const std::string kFormatString2 = "text/plain"; + const std::u16string kFormatString216 = u"text/plain"; + const std::string payload2("test string2"); + base::span<const uint8_t> payload_span2( + reinterpret_cast<const uint8_t*>(payload2.data()), payload2.size()); + + { + ScopedClipboardWriter clipboard_writer(ClipboardBuffer::kCopyPaste); + // Both payloads should write successfully and not overwrite one another. + clipboard_writer.WriteData(kFormatString116, + mojo_base::BigBuffer(payload_span1)); + clipboard_writer.WriteData(kFormatString216, + mojo_base::BigBuffer(payload_span2)); + } + + // Check format 1. + EXPECT_THAT(this->clipboard().ReadAvailablePlatformSpecificFormatNames( + ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr), + Contains(kFormatString116)); + std::string custom_format_json; + this->clipboard().ReadData(ClipboardFormatType::WebCustomFormatMap(), + /* data_dst = */ nullptr, &custom_format_json); + std::map<std::string, std::string> custom_format_names = + this->clipboard().ExtractCustomPlatformNames(ClipboardBuffer::kCopyPaste, + /* data_dst = */ nullptr); + EXPECT_TRUE(custom_format_names.find(kFormatString1) != + custom_format_names.end()); + std::string output1; + this->clipboard().ReadData(ClipboardFormatType::CustomPlatformType( + custom_format_names[kFormatString1]), + /* data_dst = */ nullptr, &output1); + EXPECT_EQ(payload1, output1); + + // Check format 2. + EXPECT_THAT(this->clipboard().ReadAvailablePlatformSpecificFormatNames( + ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr), + Contains(kFormatString216)); + EXPECT_TRUE(custom_format_names.find(kFormatString2) != + custom_format_names.end()); + std::string output2; + this->clipboard().ReadData(ClipboardFormatType::CustomPlatformType( + custom_format_names[kFormatString2]), + /* data_dst = */ nullptr, &output2); + EXPECT_EQ(payload2, output2); +} +#endif + +// crbug.com/1224904: Flaky on Mac. +#if defined(OS_MAC) +#define MAYBE_ReadAvailablePlatformSpecificFormatNamesTest \ + DISABLED_ReadAvailablePlatformSpecificFormatNamesTest +#else +#define MAYBE_ReadAvailablePlatformSpecificFormatNamesTest \ + ReadAvailablePlatformSpecificFormatNamesTest #endif +TYPED_TEST(ClipboardTest, MAYBE_ReadAvailablePlatformSpecificFormatNamesTest) { + // We're testing platform-specific behavior, so use PlatformClipboardTest. + // TODO(https://crbug.com/1083050): The template shouldn't know about its + // instantiations. Move this information up using a flag, virtual method, or + // creating separate test files for different platforms. + std::string test_suite_name = ::testing::UnitTest::GetInstance() + ->current_test_info() + ->test_suite_name(); + // TODO(crbug.com/106449): Update other platforms to support custom formats. + if (test_suite_name != std::string("ClipboardTest/PlatformClipboardTest")) + return; -TYPED_TEST(ClipboardTest, ReadAvailablePlatformSpecificFormatNamesTest) { std::u16string text = u"Test String"; std::string ascii_text; { ScopedClipboardWriter clipboard_writer(ClipboardBuffer::kCopyPaste); + // `WriteText` uses `ClipboardFormatType::PlainTextType` format. clipboard_writer.WriteText(text); } @@ -795,7 +978,7 @@ TYPED_TEST(ClipboardTest, ReadAvailablePlatformSpecificFormatNamesTest) { #if defined(OS_APPLE) EXPECT_THAT(raw_types, Contains(u"public.utf8-plain-text")); EXPECT_THAT(raw_types, Contains(u"NSStringPboardType")); - EXPECT_EQ(raw_types.size(), static_cast<uint64_t>(2)); + EXPECT_EQ(raw_types.size(), 2u); // TODO(crbug.com/1052397): Revisit the macro expression once build flag switch // of lacros-chrome is complete. #elif defined(OS_LINUX) && !BUILDFLAG(IS_CHROMEOS_ASH) && \ @@ -807,22 +990,17 @@ TYPED_TEST(ClipboardTest, ReadAvailablePlatformSpecificFormatNamesTest) { #if defined(USE_OZONE) if (features::IsUsingOzonePlatform()) { EXPECT_THAT(raw_types, Contains(ASCIIToUTF16(kMimeTypeTextUtf8))); - EXPECT_EQ(raw_types.size(), static_cast<uint64_t>(5)); + EXPECT_EQ(raw_types.size(), 5u); return; } #endif // USE_OZONE #if defined(USE_X11) EXPECT_FALSE(features::IsUsingOzonePlatform()); - EXPECT_EQ(raw_types.size(), static_cast<uint64_t>(4)); + EXPECT_EQ(raw_types.size(), 4u); #endif // USE_X11 -#elif defined(OS_WIN) - EXPECT_THAT(raw_types, Contains(u"CF_UNICODETEXT")); - EXPECT_THAT(raw_types, Contains(u"CF_TEXT")); - EXPECT_THAT(raw_types, Contains(u"CF_OEMTEXT")); - EXPECT_EQ(raw_types.size(), static_cast<uint64_t>(3)); -#elif defined(USE_AURA) || defined(OS_ANDROID) +#elif defined(USE_AURA) || defined(OS_ANDROID) || defined(OS_WIN) EXPECT_THAT(raw_types, Contains(ASCIIToUTF16(kMimeTypeText))); - EXPECT_EQ(raw_types.size(), static_cast<uint64_t>(1)); + EXPECT_EQ(raw_types.size(), 1u); #else #error Unsupported platform #endif @@ -845,13 +1023,11 @@ TYPED_TEST(ClipboardTest, PlatformSpecificDataTest) { return; const std::string text = "test string"; + const std::string kFormatString = "text/plain"; #if defined(OS_WIN) - // Windows pre-defined ANSI text format. - const std::string kFormatString = "CF_TEXT"; // Windows requires an extra '\0' at the end for a raw write. const std::string kPlatformSpecificText = text + '\0'; #elif defined(USE_X11) - const std::string kFormatString = "text/plain"; // X11 text format const std::string kPlatformSpecificText = text; #endif base::span<const uint8_t> text_span( @@ -863,38 +1039,18 @@ TYPED_TEST(ClipboardTest, PlatformSpecificDataTest) { mojo_base::BigBuffer(text_span)); } - const std::vector<std::u16string> raw_types = - this->clipboard().ReadAvailablePlatformSpecificFormatNames( - ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr); - - EXPECT_THAT(raw_types, Contains(ASCIIToUTF16(kFormatString))); - -#if defined(OS_WIN) - // Only Windows ClipboardFormatType recognizes ANSI formats. - EXPECT_TRUE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetPlainTextAType(), ClipboardBuffer::kCopyPaste, - /* data_dst = */ nullptr)); -#endif // defined(OS_WIN) - - EXPECT_TRUE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetPlainTextType(), ClipboardBuffer::kCopyPaste, - /* data_dst = */ nullptr)); - - std::string text_result; - this->clipboard().ReadAsciiText(ClipboardBuffer::kCopyPaste, - /* data_dst = */ nullptr, &text_result); - EXPECT_EQ(text_result, text); - // Windows will automatically convert CF_TEXT to its UNICODE version. - EXPECT_TRUE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetPlainTextType(), ClipboardBuffer::kCopyPaste, - /* data_dst = */ nullptr)); - std::u16string text_result16; - this->clipboard().ReadText(ClipboardBuffer::kCopyPaste, - /* data_dst = */ nullptr, &text_result16); - EXPECT_EQ(text_result16, base::ASCIIToUTF16(text)); + std::string custom_format_json; + this->clipboard().ReadData(ClipboardFormatType::WebCustomFormatMap(), + /* data_dst = */ nullptr, &custom_format_json); + std::map<std::string, std::string> custom_format_names = + this->clipboard().ExtractCustomPlatformNames(ClipboardBuffer::kCopyPaste, + /* data_dst = */ nullptr); + EXPECT_TRUE(custom_format_names.find(kFormatString) != + custom_format_names.end()); std::string platform_specific_result; - this->clipboard().ReadData(ClipboardFormatType::GetType(kFormatString), + this->clipboard().ReadData(ClipboardFormatType::CustomPlatformType( + custom_format_names[kFormatString]), /* data_dst = */ nullptr, &platform_specific_result); EXPECT_EQ(platform_specific_result, kPlatformSpecificText); @@ -917,7 +1073,7 @@ TYPED_TEST(ClipboardTest, HyperlinkTest) { } EXPECT_TRUE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetHtmlType(), ClipboardBuffer::kCopyPaste, + ClipboardFormatType::HtmlType(), ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)); uint32_t fragment_start; uint32_t fragment_end; @@ -937,8 +1093,8 @@ TYPED_TEST(ClipboardTest, WebSmartPasteTest) { } EXPECT_TRUE(this->clipboard().IsFormatAvailable( - ClipboardFormatType::GetWebKitSmartPasteType(), - ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr)); + ClipboardFormatType::WebKitSmartPasteType(), ClipboardBuffer::kCopyPaste, + /* data_dst = */ nullptr)); } #if defined(OS_WIN) // Windows only tests. @@ -1011,7 +1167,7 @@ TYPED_TEST(ClipboardTest, WriteEverything) { // written to. // TODO(dcheng): Add a version to test ClipboardBuffer::kSelection. TYPED_TEST(ClipboardTest, GetSequenceNumber) { - const uint64_t first_sequence_number = + const ClipboardSequenceNumberToken first_sequence_number = this->clipboard().GetSequenceNumber(ClipboardBuffer::kCopyPaste); { @@ -1023,7 +1179,7 @@ TYPED_TEST(ClipboardTest, GetSequenceNumber) { // the message loop to make sure we get the notification. base::RunLoop().RunUntilIdle(); - const uint64_t second_sequence_number = + const ClipboardSequenceNumberToken second_sequence_number = this->clipboard().GetSequenceNumber(ClipboardBuffer::kCopyPaste); EXPECT_NE(first_sequence_number, second_sequence_number); @@ -1064,7 +1220,7 @@ TYPED_TEST(ClipboardTest, WriteHyperlinkEmptyParams) { TYPED_TEST(ClipboardTest, WritePickledData) { ScopedClipboardWriter scw(ClipboardBuffer::kCopyPaste); - scw.WritePickledData(base::Pickle(), ClipboardFormatType::GetPlainTextType()); + scw.WritePickledData(base::Pickle(), ClipboardFormatType::PlainTextType()); } TYPED_TEST(ClipboardTest, WriteImageEmptyParams) { diff --git a/chromium/ui/base/clipboard/clipboard_util_mac_unittest.mm b/chromium/ui/base/clipboard/clipboard_util_mac_unittest.mm index 13a2ef789a9..1156d26a91e 100644 --- a/chromium/ui/base/clipboard/clipboard_util_mac_unittest.mm +++ b/chromium/ui/base/clipboard/clipboard_util_mac_unittest.mm @@ -6,6 +6,7 @@ #include "base/mac/scoped_nsobject.h" #include "base/memory/ref_counted.h" +#include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest_mac.h" #include "testing/platform_test.h" @@ -18,13 +19,28 @@ class ClipboardUtilMacTest : public PlatformTest { public: ClipboardUtilMacTest() { } - NSDictionary* DictionaryFromPasteboard(NSPasteboard* pboard) { - NSArray* types = [pboard types]; - NSMutableDictionary* data = [NSMutableDictionary dictionary]; - for (NSString* type in types) { - data[type] = [pboard dataForType:type]; + // Given a pasteboard, returns a dictionary of the contents of the pasteboard + // for use in deep comparisons. This fully unpacks any plist-encoded items. + NSDictionary* DictionaryFromPasteboardForDeepComparisons( + NSPasteboard* pboard) { + NSMutableDictionary* result = [NSMutableDictionary dictionary]; + for (NSString* type in [pboard types]) { + NSData* data = [pboard dataForType:type]; + // Try to unpack the data as a plist, and if it succeeds, use that in the + // resulting dictionary rather than the raw NSData. This is needed because + // plists have multiple encodings, and the comparison should be made on + // the underlying data rather than the specific encoding used by the OS. + NSDictionary* unpacked_data = [NSPropertyListSerialization + propertyListWithData:data + options:NSPropertyListImmutable + format:nil + error:nil]; + if (unpacked_data) + result[type] = unpacked_data; + else + result[type] = data; } - return data; + return result; } }; @@ -126,8 +142,10 @@ TEST_F(ClipboardUtilMacTest, CompareToWriteToPasteboard) { scoped_refptr<UniquePasteboard> pboard = new UniquePasteboard; [pboard->get() setDataForURL:urlString title:urlString]; - NSDictionary* data1 = DictionaryFromPasteboard(pasteboard->get()); - NSDictionary* data2 = DictionaryFromPasteboard(pboard->get()); + NSDictionary* data1 = + DictionaryFromPasteboardForDeepComparisons(pasteboard->get()); + NSDictionary* data2 = + DictionaryFromPasteboardForDeepComparisons(pboard->get()); EXPECT_NSEQ(data1, data2); } diff --git a/chromium/ui/base/clipboard/clipboard_util_win.cc b/chromium/ui/base/clipboard/clipboard_util_win.cc index 747923cb6d4..4acce28b16a 100644 --- a/chromium/ui/base/clipboard/clipboard_util_win.cc +++ b/chromium/ui/base/clipboard/clipboard_util_win.cc @@ -11,9 +11,9 @@ #include <limits> #include <utility> +#include "base/cxx17_backports.h" #include "base/files/file_util.h" #include "base/logging.h" -#include "base/stl_util.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/strings/sys_string_conversions.h" @@ -55,7 +55,7 @@ bool GetUrlFromHDrop(IDataObject* data_object, bool success = false; STGMEDIUM medium; - if (!GetData(data_object, ClipboardFormatType::GetCFHDropType(), &medium)) + if (!GetData(data_object, ClipboardFormatType::CFHDropType(), &medium)) return false; { @@ -250,13 +250,12 @@ HGLOBAL CopyFileContentsToHGlobal(IDataObject* data_object, LONG index) { DCHECK(data_object); HGLOBAL hdata = nullptr; - if (!HasData(data_object, - ClipboardFormatType::GetFileContentAtIndexType(index))) + if (!HasData(data_object, ClipboardFormatType::FileContentAtIndexType(index))) return hdata; STGMEDIUM content; - if (!GetData(data_object, - ClipboardFormatType::GetFileContentAtIndexType(index), &content)) + if (!GetData(data_object, ClipboardFormatType::FileContentAtIndexType(index), + &content)) return hdata; HRESULT hr = S_OK; @@ -365,7 +364,7 @@ struct FileGroupDescriptorData; template <> struct FileGroupDescriptorData<FILEGROUPDESCRIPTORW> { static bool get(IDataObject* data_object, STGMEDIUM* medium) { - return GetData(data_object, ClipboardFormatType::GetFileDescriptorType(), + return GetData(data_object, ClipboardFormatType::FileDescriptorType(), medium); } }; @@ -373,7 +372,7 @@ struct FileGroupDescriptorData<FILEGROUPDESCRIPTORW> { template <> struct FileGroupDescriptorData<FILEGROUPDESCRIPTORA> { static bool get(IDataObject* data_object, STGMEDIUM* medium) { - return GetData(data_object, ClipboardFormatType::GetFileDescriptorAType(), + return GetData(data_object, ClipboardFormatType::FileDescriptorAType(), medium); } }; @@ -448,46 +447,45 @@ bool GetFileNameFromFirstDescriptor(IDataObject* data_object, bool ClipboardUtil::HasUrl(IDataObject* data_object, bool convert_filenames) { DCHECK(data_object); - return HasData(data_object, ClipboardFormatType::GetMozUrlType()) || - HasData(data_object, ClipboardFormatType::GetUrlType()) || - HasData(data_object, ClipboardFormatType::GetUrlAType()) || + return HasData(data_object, ClipboardFormatType::MozUrlType()) || + HasData(data_object, ClipboardFormatType::UrlType()) || + HasData(data_object, ClipboardFormatType::UrlAType()) || (convert_filenames && HasFilenames(data_object)); } bool ClipboardUtil::HasFilenames(IDataObject* data_object) { DCHECK(data_object); - return HasData(data_object, ClipboardFormatType::GetCFHDropType()) || - HasData(data_object, ClipboardFormatType::GetFilenameType()) || - HasData(data_object, ClipboardFormatType::GetFilenameAType()); + return HasData(data_object, ClipboardFormatType::CFHDropType()) || + HasData(data_object, ClipboardFormatType::FilenameType()) || + HasData(data_object, ClipboardFormatType::FilenameAType()); } bool ClipboardUtil::HasVirtualFilenames(IDataObject* data_object) { DCHECK(data_object); // Favor real files on the file system over virtual files. return !HasFilenames(data_object) && - HasData(data_object, - ClipboardFormatType::GetFileContentAtIndexType(0)) && - (HasData(data_object, ClipboardFormatType::GetFileDescriptorType()) || - HasData(data_object, ClipboardFormatType::GetFileDescriptorAType())); + HasData(data_object, ClipboardFormatType::FileContentAtIndexType(0)) && + (HasData(data_object, ClipboardFormatType::FileDescriptorType()) || + HasData(data_object, ClipboardFormatType::FileDescriptorAType())); } bool ClipboardUtil::HasFileContents(IDataObject* data_object) { DCHECK(data_object); - return HasData(data_object, ClipboardFormatType::GetFileContentZeroType()) && - (HasData(data_object, ClipboardFormatType::GetFileDescriptorType()) || - HasData(data_object, ClipboardFormatType::GetFileDescriptorAType())); + return HasData(data_object, ClipboardFormatType::FileContentZeroType()) && + (HasData(data_object, ClipboardFormatType::FileDescriptorType()) || + HasData(data_object, ClipboardFormatType::FileDescriptorAType())); } bool ClipboardUtil::HasHtml(IDataObject* data_object) { DCHECK(data_object); - return HasData(data_object, ClipboardFormatType::GetHtmlType()) || - HasData(data_object, ClipboardFormatType::GetTextHtmlType()); + return HasData(data_object, ClipboardFormatType::HtmlType()) || + HasData(data_object, ClipboardFormatType::TextHtmlType()); } bool ClipboardUtil::HasPlainText(IDataObject* data_object) { DCHECK(data_object); - return HasData(data_object, ClipboardFormatType::GetPlainTextType()) || - HasData(data_object, ClipboardFormatType::GetPlainTextAType()); + return HasData(data_object, ClipboardFormatType::PlainTextType()) || + HasData(data_object, ClipboardFormatType::PlainTextAType()); } bool ClipboardUtil::GetUrl(IDataObject* data_object, @@ -503,8 +501,8 @@ bool ClipboardUtil::GetUrl(IDataObject* data_object, if (GetUrlFromHDrop(data_object, url, title)) return true; - if (GetData(data_object, ClipboardFormatType::GetMozUrlType(), &store) || - GetData(data_object, ClipboardFormatType::GetUrlType(), &store)) { + if (GetData(data_object, ClipboardFormatType::MozUrlType(), &store) || + GetData(data_object, ClipboardFormatType::UrlType(), &store)) { { // Mozilla URL format or Unicode URL base::win::ScopedHGlobal<wchar_t*> data(store.hGlobal); @@ -514,7 +512,7 @@ bool ClipboardUtil::GetUrl(IDataObject* data_object, return url->is_valid(); } - if (GetData(data_object, ClipboardFormatType::GetUrlAType(), &store)) { + if (GetData(data_object, ClipboardFormatType::UrlAType(), &store)) { { // URL using ASCII base::win::ScopedHGlobal<char*> data(store.hGlobal); @@ -543,7 +541,7 @@ bool ClipboardUtil::GetFilenames(IDataObject* data_object, return false; STGMEDIUM medium; - if (GetData(data_object, ClipboardFormatType::GetCFHDropType(), &medium)) { + if (GetData(data_object, ClipboardFormatType::CFHDropType(), &medium)) { { base::win::ScopedHGlobal<HDROP> hdrop(medium.hGlobal); if (!hdrop.get()) @@ -562,7 +560,7 @@ bool ClipboardUtil::GetFilenames(IDataObject* data_object, return !filenames->empty(); } - if (GetData(data_object, ClipboardFormatType::GetFilenameType(), &medium)) { + if (GetData(data_object, ClipboardFormatType::FilenameType(), &medium)) { { // filename using Unicode base::win::ScopedHGlobal<wchar_t*> data(medium.hGlobal); @@ -573,7 +571,7 @@ bool ClipboardUtil::GetFilenames(IDataObject* data_object, return true; } - if (GetData(data_object, ClipboardFormatType::GetFilenameAType(), &medium)) { + if (GetData(data_object, ClipboardFormatType::FilenameAType(), &medium)) { { // filename using ASCII base::win::ScopedHGlobal<char*> data(medium.hGlobal); @@ -692,7 +690,7 @@ bool ClipboardUtil::GetPlainText(IDataObject* data_object, return false; STGMEDIUM store; - if (GetData(data_object, ClipboardFormatType::GetPlainTextType(), &store)) { + if (GetData(data_object, ClipboardFormatType::PlainTextType(), &store)) { { // Unicode text base::win::ScopedHGlobal<wchar_t*> data(store.hGlobal); @@ -702,7 +700,7 @@ bool ClipboardUtil::GetPlainText(IDataObject* data_object, return true; } - if (GetData(data_object, ClipboardFormatType::GetPlainTextAType(), &store)) { + if (GetData(data_object, ClipboardFormatType::PlainTextAType(), &store)) { { // ASCII text base::win::ScopedHGlobal<char*> data(store.hGlobal); @@ -729,8 +727,8 @@ bool ClipboardUtil::GetHtml(IDataObject* data_object, DCHECK(data_object && html && base_url); STGMEDIUM store; - if (HasData(data_object, ClipboardFormatType::GetHtmlType()) && - GetData(data_object, ClipboardFormatType::GetHtmlType(), &store)) { + if (HasData(data_object, ClipboardFormatType::HtmlType()) && + GetData(data_object, ClipboardFormatType::HtmlType(), &store)) { { // MS CF html base::win::ScopedHGlobal<char*> data(store.hGlobal); @@ -743,10 +741,10 @@ bool ClipboardUtil::GetHtml(IDataObject* data_object, return true; } - if (!HasData(data_object, ClipboardFormatType::GetTextHtmlType())) + if (!HasData(data_object, ClipboardFormatType::TextHtmlType())) return false; - if (!GetData(data_object, ClipboardFormatType::GetTextHtmlType(), &store)) + if (!GetData(data_object, ClipboardFormatType::TextHtmlType(), &store)) return false; { @@ -768,7 +766,7 @@ bool ClipboardUtil::GetFileContents(IDataObject* data_object, STGMEDIUM content; // The call to GetData can be very slow depending on what is in // |data_object|. - if (GetData(data_object, ClipboardFormatType::GetFileContentZeroType(), + if (GetData(data_object, ClipboardFormatType::FileContentZeroType(), &content)) { if (TYMED_HGLOBAL == content.tymed) { base::win::ScopedHGlobal<char*> data(content.hGlobal); @@ -800,12 +798,11 @@ bool ClipboardUtil::GetWebCustomData( std::unordered_map<std::u16string, std::u16string>* custom_data) { DCHECK(data_object && custom_data); - if (!HasData(data_object, ClipboardFormatType::GetWebCustomDataType())) + if (!HasData(data_object, ClipboardFormatType::WebCustomDataType())) return false; STGMEDIUM store; - if (GetData(data_object, ClipboardFormatType::GetWebCustomDataType(), - &store)) { + if (GetData(data_object, ClipboardFormatType::WebCustomDataType(), &store)) { { base::win::ScopedHGlobal<char*> data(store.hGlobal); ReadCustomDataIntoMap(data.get(), data.Size(), custom_data); diff --git a/chromium/ui/base/clipboard/clipboard_win.cc b/chromium/ui/base/clipboard/clipboard_win.cc index 868de231d10..9422954679f 100644 --- a/chromium/ui/base/clipboard/clipboard_win.cc +++ b/chromium/ui/base/clipboard/clipboard_win.cc @@ -10,16 +10,15 @@ #include <objidl.h> #include <shellapi.h> #include <shlobj.h> +#include <cstdint> +#include <vector> #include "base/bind.h" #include "base/check_op.h" -#include "base/feature_list.h" #include "base/files/file_path.h" #include "base/lazy_instance.h" -#include "base/macros.h" #include "base/notreached.h" #include "base/numerics/safe_conversions.h" -#include "base/stl_util.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" @@ -40,7 +39,6 @@ #include "ui/base/clipboard/clipboard_util_win.h" #include "ui/base/clipboard/custom_data_helper.h" #include "ui/base/data_transfer_policy/data_transfer_endpoint.h" -#include "ui/base/ui_base_features.h" #include "ui/gfx/canvas.h" #include "ui/gfx/codec/png_codec.h" #include "ui/gfx/geometry/size.h" @@ -56,7 +54,8 @@ class AnonymousImpersonator { AnonymousImpersonator() { must_revert_ = ::ImpersonateAnonymousToken(::GetCurrentThread()); } - + AnonymousImpersonator(const AnonymousImpersonator&) = delete; + AnonymousImpersonator& operator=(const AnonymousImpersonator&) = delete; ~AnonymousImpersonator() { if (must_revert_) ::RevertToSelf(); @@ -64,7 +63,6 @@ class AnonymousImpersonator { private: BOOL must_revert_; - DISALLOW_COPY_AND_ASSIGN(AnonymousImpersonator); }; // A scoper to manage acquiring and automatically releasing the clipboard. @@ -225,16 +223,12 @@ void TrimAfterNull(StringType* result) { } bool ReadFilenamesAvailable() { - // Only support filenames if chrome://flags#clipboard-filenames is enabled. - if (!base::FeatureList::IsEnabled(features::kClipboardFilenames)) - return false; - return ::IsClipboardFormatAvailable( - ClipboardFormatType::GetCFHDropType().ToFormatEtc().cfFormat) || + ClipboardFormatType::CFHDropType().ToFormatEtc().cfFormat) || ::IsClipboardFormatAvailable( - ClipboardFormatType::GetFilenameType().ToFormatEtc().cfFormat) || + ClipboardFormatType::FilenameType().ToFormatEtc().cfFormat) || ::IsClipboardFormatAvailable( - ClipboardFormatType::GetFilenameAType().ToFormatEtc().cfFormat); + ClipboardFormatType::FilenameAType().ToFormatEtc().cfFormat); } } // namespace @@ -262,9 +256,16 @@ DataTransferEndpoint* ClipboardWin::GetSource(ClipboardBuffer buffer) const { return nullptr; } -uint64_t ClipboardWin::GetSequenceNumber(ClipboardBuffer buffer) const { +const ClipboardSequenceNumberToken& ClipboardWin::GetSequenceNumber( + ClipboardBuffer buffer) const { DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste); - return ::GetClipboardSequenceNumber(); + + DWORD sequence_number = ::GetClipboardSequenceNumber(); + if (sequence_number != clipboard_sequence_.sequence_number) { + // Generate a unique token associated with the current sequence number. + clipboard_sequence_ = {sequence_number, ClipboardSequenceNumberToken()}; + } + return clipboard_sequence_.token; } // |data_dst| is not used. It's only passed to be consistent with other @@ -274,8 +275,19 @@ bool ClipboardWin::IsFormatAvailable( ClipboardBuffer buffer, const DataTransferEndpoint* data_dst) const { DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste); - if (format == ClipboardFormatType::GetFilenameType()) + if (format == ClipboardFormatType::FilenameType()) return ReadFilenamesAvailable(); + // Chrome can retrieve an image from the clipboard as either a bitmap or PNG. + if (format == ClipboardFormatType::PngType() || + format == ClipboardFormatType::BitmapType()) { + return ::IsClipboardFormatAvailable( + ClipboardFormatType::PngType().ToFormatEtc().cfFormat) != + FALSE || + ::IsClipboardFormatAvailable( + ClipboardFormatType::BitmapType().ToFormatEtc().cfFormat) != + FALSE; + } + return ::IsClipboardFormatAvailable(format.ToFormatEtc().cfFormat) != FALSE; } @@ -298,13 +310,13 @@ void ClipboardWin::ReadAvailableTypes( types->clear(); if (::IsClipboardFormatAvailable( - ClipboardFormatType::GetPlainTextAType().ToFormatEtc().cfFormat)) + ClipboardFormatType::PlainTextAType().ToFormatEtc().cfFormat)) types->push_back(base::UTF8ToUTF16(kMimeTypeText)); if (::IsClipboardFormatAvailable( - ClipboardFormatType::GetHtmlType().ToFormatEtc().cfFormat)) + ClipboardFormatType::HtmlType().ToFormatEtc().cfFormat)) types->push_back(base::UTF8ToUTF16(kMimeTypeHTML)); if (::IsClipboardFormatAvailable( - ClipboardFormatType::GetRtfType().ToFormatEtc().cfFormat)) + ClipboardFormatType::RtfType().ToFormatEtc().cfFormat)) types->push_back(base::UTF8ToUTF16(kMimeTypeRTF)); if (::IsClipboardFormatAvailable(CF_DIB)) types->push_back(base::UTF8ToUTF16(kMimeTypePNG)); @@ -317,7 +329,7 @@ void ClipboardWin::ReadAvailableTypes( return; HANDLE hdata = ::GetClipboardData( - ClipboardFormatType::GetWebCustomDataType().ToFormatEtc().cfFormat); + ClipboardFormatType::WebCustomDataType().ToFormatEtc().cfFormat); if (!hdata) return; @@ -342,12 +354,18 @@ ClipboardWin::ReadAvailablePlatformSpecificFormatNames( if (!clipboard.Acquire(GetClipboardWindow())) return {}; + // Check if we have any custom formats in the clipboard. + std::map<std::string, std::string> custom_format_names = + ExtractCustomPlatformNames(buffer, data_dst); + for (const auto& items : custom_format_names) { + types.push_back(base::ASCIIToUTF16(items.first)); + } UINT cf_format = 0; cf_format = ::EnumClipboardFormats(cf_format); while (cf_format) { std::string type_name = ClipboardFormatType(cf_format).GetName(); if (!type_name.empty()) - types.push_back(base::UTF8ToUTF16(type_name)); + types.push_back(base::ASCIIToUTF16(type_name)); cf_format = ::EnumClipboardFormats(cf_format); } return types; @@ -436,7 +454,7 @@ void ClipboardWin::ReadHTML(ClipboardBuffer buffer, return; HANDLE data = ::GetClipboardData( - ClipboardFormatType::GetHtmlType().ToFormatEtc().cfFormat); + ClipboardFormatType::HtmlType().ToFormatEtc().cfFormat); if (!data) return; @@ -461,9 +479,8 @@ void ClipboardWin::ReadHTML(ClipboardBuffer buffer, if (start_index < html_start || end_index < start_index) return; - std::vector<size_t> offsets; - offsets.push_back(start_index - html_start); - offsets.push_back(end_index - html_start); + std::vector<size_t> offsets = {start_index - html_start, + end_index - html_start}; markup->assign(base::UTF8ToUTF16AndAdjustOffsets(cf_html.data() + html_start, &offsets)); // Ensure the Fragment points within the string; see https://crbug.com/607181. @@ -481,7 +498,7 @@ void ClipboardWin::ReadSvg(ClipboardBuffer buffer, RecordRead(ClipboardFormatMetric::kSvg); std::string data; - ReadData(ClipboardFormatType::GetSvgType(), data_dst, &data); + ReadData(ClipboardFormatType::SvgType(), data_dst, &data); result->assign(reinterpret_cast<const char16_t*>(data.data()), data.size() / sizeof(char16_t)); @@ -496,7 +513,7 @@ void ClipboardWin::ReadRTF(ClipboardBuffer buffer, DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste); RecordRead(ClipboardFormatMetric::kRtf); - ReadData(ClipboardFormatType::GetRtfType(), data_dst, result); + ReadData(ClipboardFormatType::RtfType(), data_dst, result); TrimAfterNull(result); } @@ -505,8 +522,16 @@ void ClipboardWin::ReadRTF(ClipboardBuffer buffer, void ClipboardWin::ReadPng(ClipboardBuffer buffer, const DataTransferEndpoint* data_dst, ReadPngCallback callback) const { - // TODO(crbug.com/1201018): Implement this. - NOTIMPLEMENTED(); + RecordRead(ClipboardFormatMetric::kPng); + std::vector<uint8_t> data = ReadPngInternal(buffer); + // On Windows, PNG and bitmap are separate formats. Read PNG if possible, + // otherwise fall back to reading as a bitmap. + if (data.empty()) { + SkBitmap bitmap = ReadImageInternal(buffer); + gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, /*discard_transparency=*/false, + &data); + } + std::move(callback).Run(data); } // |data_dst| is not used. It's only passed to be consistent with other @@ -533,7 +558,7 @@ void ClipboardWin::ReadCustomData(ClipboardBuffer buffer, return; HANDLE hdata = ::GetClipboardData( - ClipboardFormatType::GetWebCustomDataType().ToFormatEtc().cfFormat); + ClipboardFormatType::WebCustomDataType().ToFormatEtc().cfFormat); if (!hdata) return; @@ -562,7 +587,7 @@ void ClipboardWin::ReadFilenames(ClipboardBuffer buffer, // TODO(crbug.com/1178671): Refactor similar code in clipboard_utils_win: // ClipboardUtil::GetFilenames() and reuse rather than duplicate. HANDLE data = ::GetClipboardData( - ClipboardFormatType::GetCFHDropType().ToFormatEtc().cfFormat); + ClipboardFormatType::CFHDropType().ToFormatEtc().cfFormat); if (data) { { base::win::ScopedHGlobal<HDROP> hdrop(data); @@ -583,7 +608,7 @@ void ClipboardWin::ReadFilenames(ClipboardBuffer buffer, } data = ::GetClipboardData( - ClipboardFormatType::GetFilenameType().ToFormatEtc().cfFormat); + ClipboardFormatType::FilenameType().ToFormatEtc().cfFormat); if (data) { { // filename using Unicode @@ -597,7 +622,7 @@ void ClipboardWin::ReadFilenames(ClipboardBuffer buffer, } data = ::GetClipboardData( - ClipboardFormatType::GetFilenameAType().ToFormatEtc().cfFormat); + ClipboardFormatType::FilenameAType().ToFormatEtc().cfFormat); if (data) { { // filename using ASCII @@ -627,8 +652,8 @@ void ClipboardWin::ReadBookmark(const DataTransferEndpoint* data_dst, if (!clipboard.Acquire(GetClipboardWindow())) return; - HANDLE data = ::GetClipboardData( - ClipboardFormatType::GetUrlType().ToFormatEtc().cfFormat); + HANDLE data = + ::GetClipboardData(ClipboardFormatType::UrlType().ToFormatEtc().cfFormat); if (!data) return; @@ -666,37 +691,19 @@ void ClipboardWin::ReadData(const ClipboardFormatType& format, // |data_src| is not used. It's only passed to be consistent with other // platforms. -void ClipboardWin::WritePortableRepresentations( +void ClipboardWin::WritePortableAndPlatformRepresentations( ClipboardBuffer buffer, const ObjectMap& objects, - std::unique_ptr<DataTransferEndpoint> data_src) { - DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste); - - ScopedClipboard clipboard; - if (!clipboard.Acquire(GetClipboardWindow())) - return; - - ::EmptyClipboard(); - - for (const auto& object : objects) - DispatchPortableRepresentation(object.first, object.second); -} - -// |data_src| is not used. It's only passed to be consistent with other -// platforms. -void ClipboardWin::WritePlatformRepresentations( - ClipboardBuffer buffer, std::vector<Clipboard::PlatformRepresentation> platform_representations, std::unique_ptr<DataTransferEndpoint> data_src) { - DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste); - ScopedClipboard clipboard; if (!clipboard.Acquire(GetClipboardWindow())) return; - ::EmptyClipboard(); DispatchPlatformRepresentations(std::move(platform_representations)); + for (const auto& object : objects) + DispatchPortableRepresentation(object.first, object.second); } void ClipboardWin::WriteText(const char* text_data, size_t text_len) { @@ -704,7 +711,7 @@ void ClipboardWin::WriteText(const char* text_data, size_t text_len) { base::UTF8ToUTF16(text_data, text_len, &text); HGLOBAL glob = CreateGlobalData(text); - WriteToClipboard(ClipboardFormatType::GetPlainTextType(), glob); + WriteToClipboard(ClipboardFormatType::PlainTextType(), glob); } void ClipboardWin::WriteHTML(const char* markup_data, @@ -720,7 +727,7 @@ void ClipboardWin::WriteHTML(const char* markup_data, std::string html_fragment = ClipboardUtil::HtmlToCFHtml(markup, url); HGLOBAL glob = CreateGlobalData(html_fragment); - WriteToClipboard(ClipboardFormatType::GetHtmlType(), glob); + WriteToClipboard(ClipboardFormatType::HtmlType(), glob); } void ClipboardWin::WriteSvg(const char* markup_data, size_t markup_len) { @@ -728,18 +735,18 @@ void ClipboardWin::WriteSvg(const char* markup_data, size_t markup_len) { base::UTF8ToUTF16(markup_data, markup_len, &markup); HGLOBAL glob = CreateGlobalData(markup); - WriteToClipboard(ClipboardFormatType::GetSvgType(), glob); + WriteToClipboard(ClipboardFormatType::SvgType(), glob); } void ClipboardWin::WriteRTF(const char* rtf_data, size_t data_len) { - WriteData(ClipboardFormatType::GetRtfType(), rtf_data, data_len); + WriteData(ClipboardFormatType::RtfType(), rtf_data, data_len); } void ClipboardWin::WriteFilenames(std::vector<ui::FileInfo> filenames) { STGMEDIUM storage = ClipboardUtil::CreateStorageForFileNames(filenames); if (storage.tymed == TYMED_NULL) return; - WriteToClipboard(ClipboardFormatType::GetCFHDropType(), storage.hGlobal); + WriteToClipboard(ClipboardFormatType::CFHDropType(), storage.hGlobal); } void ClipboardWin::WriteBookmark(const char* title_data, @@ -753,13 +760,13 @@ void ClipboardWin::WriteBookmark(const char* title_data, std::u16string wide_bookmark = base::UTF8ToUTF16(bookmark); HGLOBAL glob = CreateGlobalData(wide_bookmark); - WriteToClipboard(ClipboardFormatType::GetUrlType(), glob); + WriteToClipboard(ClipboardFormatType::UrlType(), glob); } void ClipboardWin::WriteWebSmartPaste() { DCHECK_NE(clipboard_owner_->hwnd(), nullptr); ::SetClipboardData( - ClipboardFormatType::GetWebKitSmartPasteType().ToFormatEtc().cfFormat, + ClipboardFormatType::WebKitSmartPasteType().ToFormatEtc().cfFormat, nullptr); } @@ -779,11 +786,11 @@ void ClipboardWin::WriteBitmap(const SkBitmap& bitmap) { &png_encoded_bitmap)) { HGLOBAL png_hglobal = skia::CreateHGlobalForByteArray(png_encoded_bitmap); if (png_hglobal) - WriteToClipboard(ClipboardFormatType::GetPngType(), png_hglobal); + WriteToClipboard(ClipboardFormatType::PngType(), png_hglobal); } HGLOBAL dibv5_hglobal = skia::CreateDIBV5ImageDataFromN32SkBitmap(bitmap); if (dibv5_hglobal) - WriteToClipboard(ClipboardFormatType::GetBitmapType(), dibv5_hglobal); + WriteToClipboard(ClipboardFormatType::BitmapType(), dibv5_hglobal); } void ClipboardWin::WriteData(const ClipboardFormatType& format, @@ -799,6 +806,26 @@ void ClipboardWin::WriteData(const ClipboardFormatType& format, WriteToClipboard(format, hdata); } +std::vector<uint8_t> ClipboardWin::ReadPngInternal( + ClipboardBuffer buffer) const { + DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste); + + // Acquire the clipboard. + ScopedClipboard clipboard; + if (!clipboard.Acquire(GetClipboardWindow())) + return std::vector<uint8_t>(); + + HANDLE data = + ::GetClipboardData(ClipboardFormatType::PngType().ToFormatEtc().cfFormat); + + if (!data) + return std::vector<uint8_t>(); + + std::string result(static_cast<const char*>(::GlobalLock(data)), + ::GlobalSize(data)); + ::GlobalUnlock(data); + return std::vector<uint8_t>(result.begin(), result.end()); +} SkBitmap ClipboardWin::ReadImageInternal(ClipboardBuffer buffer) const { DCHECK_EQ(buffer, ClipboardBuffer::kCopyPaste); @@ -816,6 +843,31 @@ SkBitmap ClipboardWin::ReadImageInternal(ClipboardBuffer buffer) const { return SkBitmap(); int color_table_length = 0; + // Image is too large, and may cause an allocation failure. + // See https://crbug.com/1164680. + constexpr size_t kMaxImageSizeBytes = 1 << 27; // 128 MiB + size_t image_size_bytes; + // Estimate the number of bytes per pixel. For images with fewer than one byte + // pixel we will over-estimate the size. For compressed images we will + // over-estimate the size, but some overestimating of the storage size is okay + // and the calculation will be a good estimate of the decompressed size. The + // reported bug was for uncompressed 32-bit images where this code will give + // correct results. + size_t bytes_per_pixel = bitmap->bmiHeader.biBitCount / 8; + if (bytes_per_pixel == 0) + bytes_per_pixel = 1; + // Calculate the size of the bitmap. This is not an exact calculation but that + // doesn't matter for this purpose. If the calculation overflows then the + // image is too big. Return an empty image. + if (!base::CheckMul( + bitmap->bmiHeader.biWidth, + base::CheckMul(bitmap->bmiHeader.biHeight, bytes_per_pixel)) + .AssignIfValid(&image_size_bytes)) + return SkBitmap(); + // If the image size is too big then return an empty image. + if (image_size_bytes > kMaxImageSizeBytes) + return SkBitmap(); + // For more information on BITMAPINFOHEADER and biBitCount definition, // see https://docs.microsoft.com/en-us/windows/win32/wmdm/-bitmapinfoheader switch (bitmap->bmiHeader.biBitCount) { diff --git a/chromium/ui/base/clipboard/clipboard_win.h b/chromium/ui/base/clipboard/clipboard_win.h index 33b6abca99d..355190e6c1f 100644 --- a/chromium/ui/base/clipboard/clipboard_win.h +++ b/chromium/ui/base/clipboard/clipboard_win.h @@ -10,7 +10,6 @@ #include <memory> -#include "base/macros.h" #include "ui/base/clipboard/clipboard.h" #include "ui/base/clipboard/clipboard_format_type.h" @@ -22,7 +21,14 @@ class MessageWindow; namespace ui { +// Documentation on the underlying Win32 API this ultimately abstracts is +// available at +// https://docs.microsoft.com/en-us/windows/win32/dataxchg/clipboard. class ClipboardWin : public Clipboard { + public: + ClipboardWin(const ClipboardWin&) = delete; + ClipboardWin& operator=(const ClipboardWin&) = delete; + private: friend class Clipboard; @@ -32,7 +38,8 @@ class ClipboardWin : public Clipboard { // Clipboard overrides: void OnPreShutdown() override; DataTransferEndpoint* GetSource(ClipboardBuffer buffer) const override; - uint64_t GetSequenceNumber(ClipboardBuffer buffer) const override; + const ClipboardSequenceNumberToken& GetSequenceNumber( + ClipboardBuffer buffer) const override; bool IsFormatAvailable(const ClipboardFormatType& format, ClipboardBuffer buffer, const DataTransferEndpoint* data_dst) const override; @@ -80,12 +87,9 @@ class ClipboardWin : public Clipboard { void ReadData(const ClipboardFormatType& format, const DataTransferEndpoint* data_dst, std::string* result) const override; - void WritePortableRepresentations( + void WritePortableAndPlatformRepresentations( ClipboardBuffer buffer, const ObjectMap& objects, - std::unique_ptr<DataTransferEndpoint> data_src) override; - void WritePlatformRepresentations( - ClipboardBuffer buffer, std::vector<Clipboard::PlatformRepresentation> platform_representations, std::unique_ptr<DataTransferEndpoint> data_src) override; void WriteText(const char* text_data, size_t text_len) override; @@ -105,6 +109,7 @@ class ClipboardWin : public Clipboard { void WriteData(const ClipboardFormatType& format, const char* data_data, size_t data_len) override; + std::vector<uint8_t> ReadPngInternal(ClipboardBuffer buffer) const; SkBitmap ReadImageInternal(ClipboardBuffer buffer) const; // Safely write to system clipboard. Free |handle| on failure. @@ -118,7 +123,11 @@ class ClipboardWin : public Clipboard { // Mark this as mutable so const methods can still do lazy initialization. mutable std::unique_ptr<base::win::MessageWindow> clipboard_owner_; - DISALLOW_COPY_AND_ASSIGN(ClipboardWin); + // Mapping of OS-provided sequence number to a unique token. + mutable struct { + DWORD sequence_number; + ClipboardSequenceNumberToken token; + } clipboard_sequence_; }; } // namespace ui diff --git a/chromium/ui/base/clipboard/clipboard_x11.cc b/chromium/ui/base/clipboard/clipboard_x11.cc index 2807d6e8ddc..304223c9d8e 100644 --- a/chromium/ui/base/clipboard/clipboard_x11.cc +++ b/chromium/ui/base/clipboard/clipboard_x11.cc @@ -8,6 +8,7 @@ #include <limits> #include <memory> #include <string> +#include <vector> #include "base/bind.h" #include "base/containers/contains.h" @@ -50,7 +51,8 @@ DataTransferEndpoint* ClipboardX11::GetSource(ClipboardBuffer buffer) const { return it == data_src_.end() ? nullptr : it->second.get(); } -uint64_t ClipboardX11::GetSequenceNumber(ClipboardBuffer buffer) const { +const ClipboardSequenceNumberToken& ClipboardX11::GetSequenceNumber( + ClipboardBuffer buffer) const { DCHECK(CalledOnValidThread()); return buffer == ClipboardBuffer::kCopyPaste ? clipboard_sequence_number_ : primary_sequence_number_; @@ -88,10 +90,10 @@ void ClipboardX11::ReadAvailableTypes( for (const auto& mime_type : available_types) { // Special handling for chromium/x-web-custom-data. We must read the data // and deserialize it to find the list of mime types to report. - if (mime_type == ClipboardFormatType::GetWebCustomDataType().GetName()) { + if (mime_type == ClipboardFormatType::WebCustomDataType().GetName()) { auto data(x_clipboard_helper_->Read( buffer, x_clipboard_helper_->GetAtomsForFormat( - ClipboardFormatType::GetWebCustomDataType()))); + ClipboardFormatType::WebCustomDataType()))); if (data.IsValid()) ReadCustomDataTypes(data.GetData(), data.GetSize(), types); } else { @@ -162,8 +164,8 @@ void ClipboardX11::ReadHTML(ClipboardBuffer buffer, *fragment_end = 0; SelectionData data(x_clipboard_helper_->Read( - buffer, x_clipboard_helper_->GetAtomsForFormat( - ClipboardFormatType::GetHtmlType()))); + buffer, + x_clipboard_helper_->GetAtomsForFormat(ClipboardFormatType::HtmlType()))); if (data.IsValid()) { *markup = data.GetHtml(); @@ -182,8 +184,8 @@ void ClipboardX11::ReadSvg(ClipboardBuffer buffer, RecordRead(ClipboardFormatMetric::kSvg); SelectionData data(x_clipboard_helper_->Read( - buffer, x_clipboard_helper_->GetAtomsForFormat( - ClipboardFormatType::GetSvgType()))); + buffer, + x_clipboard_helper_->GetAtomsForFormat(ClipboardFormatType::SvgType()))); if (data.IsValid()) { std::string markup; data.AssignTo(&markup); @@ -200,8 +202,8 @@ void ClipboardX11::ReadRTF(ClipboardBuffer buffer, RecordRead(ClipboardFormatMetric::kRtf); SelectionData data(x_clipboard_helper_->Read( - buffer, x_clipboard_helper_->GetAtomsForFormat( - ClipboardFormatType::GetRtfType()))); + buffer, + x_clipboard_helper_->GetAtomsForFormat(ClipboardFormatType::RtfType()))); if (data.IsValid()) data.AssignTo(result); } @@ -211,8 +213,9 @@ void ClipboardX11::ReadRTF(ClipboardBuffer buffer, void ClipboardX11::ReadPng(ClipboardBuffer buffer, const DataTransferEndpoint* data_dst, ReadPngCallback callback) const { - // TODO(crbug.com/1201018): Implement this. - NOTIMPLEMENTED(); + DCHECK(IsSupportedClipboardBuffer(buffer)); + RecordRead(ClipboardFormatMetric::kPng); + std::move(callback).Run(ReadPngInternal(buffer)); } // |data_dst| is not used. It's only passed to be consistent with other @@ -222,7 +225,10 @@ void ClipboardX11::ReadImage(ClipboardBuffer buffer, ReadImageCallback callback) const { DCHECK(IsSupportedClipboardBuffer(buffer)); RecordRead(ClipboardFormatMetric::kImage); - std::move(callback).Run(ReadImageInternal(buffer)); + auto png_data = ReadPngInternal(buffer); + SkBitmap bitmap; + gfx::PNGCodec::Decode(png_data.data(), png_data.size(), &bitmap); + std::move(callback).Run(bitmap); } // |data_dst| is not used. It's only passed to be consistent with other @@ -236,7 +242,7 @@ void ClipboardX11::ReadCustomData(ClipboardBuffer buffer, SelectionData data(x_clipboard_helper_->Read( buffer, x_clipboard_helper_->GetAtomsForFormat( - ClipboardFormatType::GetWebCustomDataType()))); + ClipboardFormatType::WebCustomDataType()))); if (data.IsValid()) ReadCustomDataForType(data.GetData(), data.GetSize(), type, result); } @@ -251,7 +257,7 @@ void ClipboardX11::ReadFilenames(ClipboardBuffer buffer, SelectionData data(x_clipboard_helper_->Read( buffer, x_clipboard_helper_->GetAtomsForFormat( - ClipboardFormatType::GetFilenamesType()))); + ClipboardFormatType::FilenamesType()))); std::string uri_list; if (data.IsValid()) data.AssignTo(&uri_list); @@ -289,20 +295,8 @@ bool ClipboardX11::IsSelectionBufferAvailable() const { } #endif // defined(USE_OZONE) -// |data_src| is not used. It's only passed to be consistent with other -// platforms. -void ClipboardX11::WritePortableRepresentations( - ClipboardBuffer buffer, - const ObjectMap& objects, - std::unique_ptr<DataTransferEndpoint> data_src) { - DCHECK(CalledOnValidThread()); - DCHECK(IsSupportedClipboardBuffer(buffer)); - - x_clipboard_helper_->CreateNewClipboardData(); - for (const auto& object : objects) - DispatchPortableRepresentation(object.first, object.second); - x_clipboard_helper_->TakeOwnershipOfSelection(buffer); - +void ClipboardX11::WritePortableTextRepresentation(ClipboardBuffer buffer, + const ObjectMap& objects) { if (buffer == ClipboardBuffer::kCopyPaste) { auto text_iter = objects.find(PortableFormat::kText); if (text_iter != objects.end()) { @@ -317,14 +311,13 @@ void ClipboardX11::WritePortableRepresentations( ClipboardBuffer::kSelection); } } - - data_src_[buffer] = std::move(data_src); } // |data_src| is not used. It's only passed to be consistent with other // platforms. -void ClipboardX11::WritePlatformRepresentations( +void ClipboardX11::WritePortableAndPlatformRepresentations( ClipboardBuffer buffer, + const ObjectMap& objects, std::vector<Clipboard::PlatformRepresentation> platform_representations, std::unique_ptr<DataTransferEndpoint> data_src) { DCHECK(CalledOnValidThread()); @@ -332,7 +325,12 @@ void ClipboardX11::WritePlatformRepresentations( x_clipboard_helper_->CreateNewClipboardData(); DispatchPlatformRepresentations(std::move(platform_representations)); + for (const auto& object : objects) + DispatchPortableRepresentation(object.first, object.second); x_clipboard_helper_->TakeOwnershipOfSelection(buffer); + + WritePortableTextRepresentation(buffer, objects); + data_src_[buffer] = std::move(data_src); } @@ -374,7 +372,7 @@ void ClipboardX11::WriteSvg(const char* markup_data, size_t markup_len) { } void ClipboardX11::WriteRTF(const char* rtf_data, size_t data_len) { - WriteData(ClipboardFormatType::GetRtfType(), rtf_data, data_len); + WriteData(ClipboardFormatType::RtfType(), rtf_data, data_len); } void ClipboardX11::WriteFilenames(std::vector<ui::FileInfo> filenames) { @@ -382,7 +380,7 @@ void ClipboardX11::WriteFilenames(std::vector<ui::FileInfo> filenames) { scoped_refptr<base::RefCountedMemory> mem( base::RefCountedString::TakeString(&uri_list)); x_clipboard_helper_->InsertMapping( - ClipboardFormatType::GetFilenamesType().GetName(), mem); + ClipboardFormatType::FilenamesType().GetName(), mem); } void ClipboardX11::WriteBookmark(const char* title_data, @@ -431,29 +429,30 @@ void ClipboardX11::WriteData(const ClipboardFormatType& format, x_clipboard_helper_->InsertMapping(format.GetName(), mem); } -SkBitmap ClipboardX11::ReadImageInternal(ClipboardBuffer buffer) const { +std::vector<uint8_t> ClipboardX11::ReadPngInternal( + ClipboardBuffer buffer) const { DCHECK(CalledOnValidThread()); - // TODO(https://crbug.com/443355): Since now that ReadImage() is async, - // refactor the code to keep a callback with the request, and invoke the - // callback when the request is satisfied. + // TODO(https://crbug.com/443355): Since ReadPng() is async, refactor the code + // to keep a callback with the request, and invoke the callback when the + // request is satisfied. SelectionData data(x_clipboard_helper_->Read( - buffer, x_clipboard_helper_->GetAtomsForFormat( - ClipboardFormatType::GetBitmapType()))); + buffer, + x_clipboard_helper_->GetAtomsForFormat(ClipboardFormatType::PngType()))); + if (data.IsValid()) { - SkBitmap bitmap; - if (gfx::PNGCodec::Decode(data.GetData(), data.GetSize(), &bitmap)) - return SkBitmap(bitmap); + return std::vector<uint8_t>(data.GetData(), + data.GetData() + data.GetSize()); } - return SkBitmap(); + return std::vector<uint8_t>(); } void ClipboardX11::OnSelectionChanged(ClipboardBuffer buffer) { if (buffer == ClipboardBuffer::kCopyPaste) - clipboard_sequence_number_++; + clipboard_sequence_number_ = ClipboardSequenceNumberToken(); else - primary_sequence_number_++; + primary_sequence_number_ = ClipboardSequenceNumberToken(); ClipboardMonitor::GetInstance()->NotifyClipboardDataChanged(); } diff --git a/chromium/ui/base/clipboard/clipboard_x11.h b/chromium/ui/base/clipboard/clipboard_x11.h index 88a5ed96273..be7eb96c88e 100644 --- a/chromium/ui/base/clipboard/clipboard_x11.h +++ b/chromium/ui/base/clipboard/clipboard_x11.h @@ -8,7 +8,6 @@ #include <cstdint> #include <memory> -#include "base/macros.h" #include "ui/base/clipboard/clipboard.h" #include "ui/base/clipboard/clipboard_buffer.h" @@ -17,6 +16,10 @@ namespace ui { class XClipboardHelper; class ClipboardX11 : public Clipboard { + public: + ClipboardX11(const ClipboardX11&) = delete; + ClipboardX11& operator=(const ClipboardX11&) = delete; + private: friend class Clipboard; @@ -26,7 +29,8 @@ class ClipboardX11 : public Clipboard { // Clipboard overrides: void OnPreShutdown() override; DataTransferEndpoint* GetSource(ClipboardBuffer buffer) const override; - uint64_t GetSequenceNumber(ClipboardBuffer buffer) const override; + const ClipboardSequenceNumberToken& GetSequenceNumber( + ClipboardBuffer buffer) const override; bool IsFormatAvailable(const ClipboardFormatType& format, ClipboardBuffer buffer, const DataTransferEndpoint* data_dst) const override; @@ -77,12 +81,11 @@ class ClipboardX11 : public Clipboard { #if defined(USE_OZONE) bool IsSelectionBufferAvailable() const override; #endif // defined(USE_OZONE) - void WritePortableRepresentations( + void WritePortableTextRepresentation(ClipboardBuffer buffer, + const ObjectMap& objects); + void WritePortableAndPlatformRepresentations( ClipboardBuffer buffer, const ObjectMap& objects, - std::unique_ptr<DataTransferEndpoint> data_src) override; - void WritePlatformRepresentations( - ClipboardBuffer buffer, std::vector<Clipboard::PlatformRepresentation> platform_representations, std::unique_ptr<DataTransferEndpoint> data_src) override; void WriteText(const char* text_data, size_t text_len) override; @@ -103,18 +106,16 @@ class ClipboardX11 : public Clipboard { const char* data_data, size_t data_len) override; - SkBitmap ReadImageInternal(ClipboardBuffer buffer) const; + std::vector<uint8_t> ReadPngInternal(ClipboardBuffer buffer) const; void OnSelectionChanged(ClipboardBuffer buffer); std::unique_ptr<XClipboardHelper> x_clipboard_helper_; - uint64_t clipboard_sequence_number_ = 0; - uint64_t primary_sequence_number_ = 0; + ClipboardSequenceNumberToken clipboard_sequence_number_; + ClipboardSequenceNumberToken primary_sequence_number_; base::flat_map<ClipboardBuffer, std::unique_ptr<DataTransferEndpoint>> data_src_; - - DISALLOW_COPY_AND_ASSIGN(ClipboardX11); }; } // namespace ui diff --git a/chromium/ui/base/clipboard/scoped_clipboard_writer.cc b/chromium/ui/base/clipboard/scoped_clipboard_writer.cc index 4e2394e4116..153f169d2cd 100644 --- a/chromium/ui/base/clipboard/scoped_clipboard_writer.cc +++ b/chromium/ui/base/clipboard/scoped_clipboard_writer.cc @@ -6,8 +6,10 @@ #include <memory> #include <utility> +#include "base/json/json_writer.h" #include "base/pickle.h" #include "base/strings/utf_string_conversions.h" +#include "base/values.h" #include "net/base/escape.h" #include "ui/base/clipboard/clipboard_format_type.h" #include "ui/base/clipboard/clipboard_metrics.h" @@ -24,16 +26,24 @@ ScopedClipboardWriter::ScopedClipboardWriter( ScopedClipboardWriter::~ScopedClipboardWriter() { static constexpr size_t kMaxRepresentations = 1 << 12; - DCHECK(objects_.empty() || platform_representations_.empty()) - << "Portable and Platform representations should not be written on the " - "same write."; DCHECK(platform_representations_.size() < kMaxRepresentations); - if (!objects_.empty()) { - Clipboard::GetForCurrentThread()->WritePortableRepresentations( - buffer_, objects_, std::move(data_src_)); - } else if (!platform_representations_.empty()) { - Clipboard::GetForCurrentThread()->WritePlatformRepresentations( - buffer_, std::move(platform_representations_), std::move(data_src_)); + // If the metadata format type is not empty then create a JSON payload and + // write to the clipboard. + if (!registered_formats_.empty()) { + std::string custom_format_json; + base::Value registered_formats_value(base::Value::Type::DICTIONARY); + for (const auto& item : registered_formats_) + registered_formats_value.SetStringKey(item.first, item.second); + base::JSONWriter::Write(registered_formats_value, &custom_format_json); + Clipboard::ObjectMapParams parameters; + parameters.push_back(Clipboard::ObjectMapParam(custom_format_json.begin(), + custom_format_json.end())); + objects_[Clipboard::PortableFormat::kWebCustomFormatMap] = parameters; + } + if (!objects_.empty() || !platform_representations_.empty()) { + Clipboard::GetForCurrentThread()->WritePortableAndPlatformRepresentations( + buffer_, objects_, std::move(platform_representations_), + std::move(data_src_)); } if (confidential_) @@ -176,15 +186,39 @@ void ScopedClipboardWriter::WritePickledData( void ScopedClipboardWriter::WriteData(const std::u16string& format, mojo_base::BigBuffer data) { RecordWrite(ClipboardFormatMetric::kData); - platform_representations_.push_back( - {base::UTF16ToUTF8(format), std::move(data)}); + // Windows / X11 clipboards enter an unrecoverable state after registering + // some amount of unique formats, and there's no way to un-register these + // formats. For these clipboards, use a conservative limit to avoid + // registering too many formats, as: + // (1) Other native applications may also register clipboard formats. + // (2) Malicious sites can write more than the hard limit defined on + // Windows(16k). (3) Chrome also registers other clipboard formats. + // + // There will be a custom format map which contains a JSON payload that will + // have a mapping of custom format MIME type to web custom format. + // There can only be 100 custom format per write and it will be + // registered when the web authors request for a custom format. + static constexpr int kMaxRegisteredFormats = 100; + if (counter_ >= kMaxRegisteredFormats) + return; + std::string format_in_ascii = base::UTF16ToASCII(format); + if (registered_formats_.find(format_in_ascii) == registered_formats_.end()) { + std::string web_custom_format_string = + ClipboardFormatType::WebCustomFormatName(counter_); + registered_formats_[format_in_ascii] = web_custom_format_string; + counter_++; + platform_representations_.push_back( + {web_custom_format_string, std::move(data)}); + } } void ScopedClipboardWriter::Reset() { objects_.clear(); platform_representations_.clear(); + registered_formats_.clear(); bitmap_.reset(); confidential_ = false; + counter_ = 0; } } // namespace ui diff --git a/chromium/ui/base/clipboard/scoped_clipboard_writer.h b/chromium/ui/base/clipboard/scoped_clipboard_writer.h index 4d5122026dd..879acd4f6f0 100644 --- a/chromium/ui/base/clipboard/scoped_clipboard_writer.h +++ b/chromium/ui/base/clipboard/scoped_clipboard_writer.h @@ -8,7 +8,7 @@ #include <string> #include "base/component_export.h" -#include "base/macros.h" +#include "base/containers/flat_map.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/base/clipboard/clipboard.h" #include "ui/base/data_transfer_policy/data_transfer_endpoint.h" @@ -36,7 +36,8 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) ScopedClipboardWriter { explicit ScopedClipboardWriter( ClipboardBuffer buffer, std::unique_ptr<DataTransferEndpoint> src = nullptr); - + ScopedClipboardWriter(const ScopedClipboardWriter&) = delete; + ScopedClipboardWriter& operator=(const ScopedClipboardWriter&) = delete; ~ScopedClipboardWriter(); // Converts |text| to UTF-8 and adds it to the clipboard. @@ -76,6 +77,7 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) ScopedClipboardWriter { // Data is written to the system clipboard in the same order as WriteData // calls are received. + // This is only used to write custom format data. void WriteData(const std::u16string& format, mojo_base::BigBuffer data); void WriteImage(const SkBitmap& bitmap); @@ -93,6 +95,9 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) ScopedClipboardWriter { Clipboard::ObjectMap objects_; std::vector<Clipboard::PlatformRepresentation> platform_representations_; + // Keeps track of the unique custom formats registered in the clipboard. + base::flat_map<std::string, std::string> registered_formats_; + int counter_ = 0; // The type is set at construction, and can be changed before committing. const ClipboardBuffer buffer_; @@ -105,8 +110,6 @@ class COMPONENT_EXPORT(UI_BASE_CLIPBOARD) ScopedClipboardWriter { // not set, or the source of the data can't be represented by // DataTransferEndpoint. std::unique_ptr<DataTransferEndpoint> data_src_; - - DISALLOW_COPY_AND_ASSIGN(ScopedClipboardWriter); }; } // namespace ui diff --git a/chromium/ui/base/cocoa/command_dispatcher.h b/chromium/ui/base/cocoa/command_dispatcher.h index ca79f3d2382..664d3b7dc0d 100644 --- a/chromium/ui/base/cocoa/command_dispatcher.h +++ b/chromium/ui/base/cocoa/command_dispatcher.h @@ -51,6 +51,9 @@ COMPONENT_EXPORT(UI_BASE) // reposted infinitely. Returns YES if the event is handled. - (BOOL)preSendEvent:(NSEvent*)event; +// The parent to bubble events to, or nil. +- (NSWindow<CommandDispatchingWindow>*)bubbleParent; + // Dispatch a -commandDispatch: action either to |handler| or a parent window's // handler. - (void)dispatch:(id)sender diff --git a/chromium/ui/base/cocoa/command_dispatcher.mm b/chromium/ui/base/cocoa/command_dispatcher.mm index dbc19a6c1f7..1cfe0e3518b 100644 --- a/chromium/ui/base/cocoa/command_dispatcher.mm +++ b/chromium/ui/base/cocoa/command_dispatcher.mm @@ -18,11 +18,6 @@ - (BOOL)hasKeyAppearance; @end -@interface CommandDispatcher () -// The parent to bubble events to, or nil. -- (NSWindow<CommandDispatchingWindow>*)bubbleParent; -@end - namespace { // Duplicate the given key event, but changing the associated window. diff --git a/chromium/ui/base/cocoa/constrained_window/constrained_window_animation.mm b/chromium/ui/base/cocoa/constrained_window/constrained_window_animation.mm index c8ce99f708b..c894456d938 100644 --- a/chromium/ui/base/cocoa/constrained_window/constrained_window_animation.mm +++ b/chromium/ui/base/cocoa/constrained_window/constrained_window_animation.mm @@ -7,12 +7,12 @@ #include <stdint.h> #include <stdlib.h> +#include "base/cxx17_backports.h" #include "base/files/file_path.h" #include "base/location.h" #import "base/mac/foundation_util.h" #include "base/native_library.h" #include "base/notreached.h" -#include "base/stl_util.h" #include "ui/gfx/animation/tween.h" // The window animations in this file use private APIs as described here: @@ -153,23 +153,33 @@ void SetWindowWarp(NSWindow* window, // coordinates. Note that the origin of the coordinate system is top, left. CGPointWarp mesh[2][2] = { {{ - // Top left. - {NSMinX(win_rect), NSMinY(win_rect)}, - {NSMinX(screen_rect) + perspective_offset, NSMinY(screen_rect)}, + // Top left. + {static_cast<float>(NSMinX(win_rect)), + static_cast<float>(NSMinY(win_rect))}, + {static_cast<float>(NSMinX(screen_rect) + perspective_offset), + static_cast<float>(NSMinY(screen_rect))}, }, { - // Top right. - {NSMaxX(win_rect), NSMinY(win_rect)}, - {NSMaxX(screen_rect) - perspective_offset, NSMinY(screen_rect)}, }}, + // Top right. + {static_cast<float>(NSMaxX(win_rect)), + static_cast<float>(NSMinY(win_rect))}, + {static_cast<float>(NSMaxX(screen_rect) - perspective_offset), + static_cast<float>(NSMinY(screen_rect))}, + }}, {{ - // Bottom left. - {NSMinX(win_rect), NSMaxY(win_rect)}, - {NSMinX(screen_rect), NSMaxY(screen_rect)}, + // Bottom left. + {static_cast<float>(NSMinX(win_rect)), + static_cast<float>(NSMaxY(win_rect))}, + {static_cast<float>(NSMinX(screen_rect)), + static_cast<float>(NSMaxY(screen_rect))}, }, { - // Bottom right. - {NSMaxX(win_rect), NSMaxY(win_rect)}, - {NSMaxX(screen_rect), NSMaxY(screen_rect)}, }}, + // Bottom right. + {static_cast<float>(NSMaxX(win_rect)), + static_cast<float>(NSMaxY(win_rect))}, + {static_cast<float>(NSMaxX(screen_rect)), + static_cast<float>(NSMaxY(screen_rect))}, + }}, }; CGSConnection cid = _CGSDefaultConnection(); diff --git a/chromium/ui/base/cocoa/focus_tracker.h b/chromium/ui/base/cocoa/focus_tracker.h index a676e08ef15..c89d08a3958 100644 --- a/chromium/ui/base/cocoa/focus_tracker.h +++ b/chromium/ui/base/cocoa/focus_tracker.h @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#ifndef UI_BASE_COCOA_FOCUS_TRACKER_H_ +#define UI_BASE_COCOA_FOCUS_TRACKER_H_ + #import <Cocoa/Cocoa.h> #include "base/component_export.h" @@ -27,3 +30,5 @@ COMPONENT_EXPORT(UI_BASE) // longer in the view hierarchy under |window|. - (BOOL)restoreFocusInWindow:(NSWindow*)window; @end + +#endif // UI_BASE_COCOA_FOCUS_TRACKER_H_
\ No newline at end of file diff --git a/chromium/ui/base/cursor/DIR_METADATA b/chromium/ui/base/cursor/DIR_METADATA index 198db497480..f3434449946 100644 --- a/chromium/ui/base/cursor/DIR_METADATA +++ b/chromium/ui/base/cursor/DIR_METADATA @@ -1,10 +1,10 @@ # Metadata information for this directory. # # For more information on DIR_METADATA files, see: -# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md +# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/README.md # # For the schema of this file, see Metadata message: -# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto +# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto monorail { component: "Internals>Input" diff --git a/chromium/ui/base/cursor/cursors_aura.cc b/chromium/ui/base/cursor/cursors_aura.cc index 3633161ec32..e013a811164 100644 --- a/chromium/ui/base/cursor/cursors_aura.cc +++ b/chromium/ui/base/cursor/cursors_aura.cc @@ -6,7 +6,7 @@ #include <stddef.h> -#include "base/stl_util.h" +#include "base/cxx17_backports.h" #include "build/build_config.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/base/cursor/cursor.h" @@ -292,7 +292,7 @@ bool SearchTable(const CursorData* table, DCHECK_NE(scale_factor, 0); bool resource_2x_available = - ResourceBundle::GetSharedInstance().GetMaxScaleFactor() == + ResourceBundle::GetSharedInstance().GetMaxResourceScaleFactor() == SCALE_FACTOR_200P; for (size_t i = 0; i < table_length; ++i) { if (table[i].id == id) { diff --git a/chromium/ui/base/cursor/ozone/DIR_METADATA b/chromium/ui/base/cursor/ozone/DIR_METADATA index 69e73155446..331533eb04f 100644 --- a/chromium/ui/base/cursor/ozone/DIR_METADATA +++ b/chromium/ui/base/cursor/ozone/DIR_METADATA @@ -1,10 +1,10 @@ # Metadata information for this directory. # # For more information on DIR_METADATA files, see: -# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md +# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/README.md # # For the schema of this file, see Metadata message: -# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto +# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto monorail { component: "UI>GFX" diff --git a/chromium/ui/base/cursor/ozone/bitmap_cursor_factory_ozone.cc b/chromium/ui/base/cursor/ozone/bitmap_cursor_factory_ozone.cc index 2f3d4bb0190..f8d200a1bba 100644 --- a/chromium/ui/base/cursor/ozone/bitmap_cursor_factory_ozone.cc +++ b/chromium/ui/base/cursor/ozone/bitmap_cursor_factory_ozone.cc @@ -94,12 +94,17 @@ scoped_refptr<BitmapCursorOzone> BitmapCursorOzone::FromPlatformCursor( static_cast<BitmapCursorOzone*>(platform_cursor.get())); } -BitmapCursorOzone::BitmapCursorOzone(mojom::CursorType type) : type_(type) {} +BitmapCursorOzone::BitmapCursorOzone(mojom::CursorType type, + float cursor_image_scale_factor) + : type_(type), cursor_image_scale_factor_(cursor_image_scale_factor) {} BitmapCursorOzone::BitmapCursorOzone(mojom::CursorType type, const SkBitmap& bitmap, - const gfx::Point& hotspot) - : type_(type), hotspot_(hotspot) { + const gfx::Point& hotspot, + float cursor_image_scale_factor) + : type_(type), + hotspot_(hotspot), + cursor_image_scale_factor_(cursor_image_scale_factor) { if (!bitmap.isNull()) bitmaps_.push_back(bitmap); } @@ -107,11 +112,13 @@ BitmapCursorOzone::BitmapCursorOzone(mojom::CursorType type, BitmapCursorOzone::BitmapCursorOzone(mojom::CursorType type, const std::vector<SkBitmap>& bitmaps, const gfx::Point& hotspot, - base::TimeDelta frame_delay) + base::TimeDelta frame_delay, + float cursor_image_scale_factor) : type_(type), bitmaps_(bitmaps), hotspot_(hotspot), - frame_delay_(frame_delay) { + frame_delay_(frame_delay), + cursor_image_scale_factor_(cursor_image_scale_factor) { DCHECK_LT(0U, bitmaps.size()); DCHECK_LE(base::TimeDelta(), frame_delay); // No null bitmap should be in the list. Blank cursors should just be an empty @@ -122,8 +129,11 @@ BitmapCursorOzone::BitmapCursorOzone(mojom::CursorType type, } BitmapCursorOzone::BitmapCursorOzone(mojom::CursorType type, - void* platform_data) - : type_(type), platform_data_(platform_data) {} + void* platform_data, + float cursor_image_scale_factor) + : type_(type), + platform_data_(platform_data), + cursor_image_scale_factor_(cursor_image_scale_factor) {} BitmapCursorOzone::~BitmapCursorOzone() = default; @@ -158,7 +168,8 @@ scoped_refptr<PlatformCursor> BitmapCursorFactoryOzone::GetDefaultCursor( // Lacros uses server-side cursors for most types. These cursors don't // need to load bitmap images on the client. // Similarly, the hidden cursor doesn't use any bitmap. - default_cursors_[type] = base::MakeRefCounted<BitmapCursorOzone>(type); + default_cursors_[type] = + base::MakeRefCounted<BitmapCursorOzone>(type, cursor_scale_factor_); } else { return nullptr; } @@ -171,7 +182,8 @@ scoped_refptr<PlatformCursor> BitmapCursorFactoryOzone::CreateImageCursor( mojom::CursorType type, const SkBitmap& bitmap, const gfx::Point& hotspot) { - return base::MakeRefCounted<BitmapCursorOzone>(type, bitmap, hotspot); + return base::MakeRefCounted<BitmapCursorOzone>(type, bitmap, hotspot, + cursor_scale_factor_); } scoped_refptr<PlatformCursor> BitmapCursorFactoryOzone::CreateAnimatedCursor( @@ -180,8 +192,13 @@ scoped_refptr<PlatformCursor> BitmapCursorFactoryOzone::CreateAnimatedCursor( const gfx::Point& hotspot, base::TimeDelta frame_delay) { DCHECK_LT(0U, bitmaps.size()); - return base::MakeRefCounted<BitmapCursorOzone>(type, bitmaps, hotspot, - frame_delay); + return base::MakeRefCounted<BitmapCursorOzone>( + type, bitmaps, hotspot, frame_delay, cursor_scale_factor_); +} + +void BitmapCursorFactoryOzone::SetDeviceScaleFactor(float scale) { + DCHECK_GT(scale, 0.f); + cursor_scale_factor_ = scale; } } // namespace ui diff --git a/chromium/ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h b/chromium/ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h index b217791adc0..3ba4755b163 100644 --- a/chromium/ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h +++ b/chromium/ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h @@ -30,21 +30,26 @@ class COMPONENT_EXPORT(UI_BASE_CURSOR) BitmapCursorOzone // Creates a cursor that doesn't need backing bitmaps (for example, a // server-side cursor for Lacros). - explicit BitmapCursorOzone(mojom::CursorType type); + explicit BitmapCursorOzone(mojom::CursorType type, + float cursor_image_scale_factor); // Creates a cursor with a single backing bitmap. BitmapCursorOzone(mojom::CursorType type, const SkBitmap& bitmap, - const gfx::Point& hotspot); + const gfx::Point& hotspot, + float cursor_image_scale_factor); // Creates a cursor with multiple bitmaps for animation. BitmapCursorOzone(mojom::CursorType type, const std::vector<SkBitmap>& bitmaps, const gfx::Point& hotspot, - base::TimeDelta frame_delay); + base::TimeDelta frame_delay, + float cursor_image_scale_factor); // Creates a cursor with external storage. - BitmapCursorOzone(mojom::CursorType type, void* platform_data); + BitmapCursorOzone(mojom::CursorType type, + void* platform_data, + float cursor_image_scale_factor); mojom::CursorType type() const { return type_; } const gfx::Point& hotspot(); @@ -57,6 +62,8 @@ class COMPONENT_EXPORT(UI_BASE_CURSOR) BitmapCursorOzone // For theme cursors. void* platform_data() { return platform_data_; } + float cursor_image_scale_factor() const { return cursor_image_scale_factor_; } + private: friend class base::RefCounted<PlatformCursor>; ~BitmapCursorOzone() override; @@ -70,6 +77,8 @@ class COMPONENT_EXPORT(UI_BASE_CURSOR) BitmapCursorOzone // is supplied by the platform. void* const platform_data_ = nullptr; + float cursor_image_scale_factor_ = 1.f; + DISALLOW_COPY_AND_ASSIGN(BitmapCursorOzone); }; @@ -95,10 +104,12 @@ class COMPONENT_EXPORT(UI_BASE_CURSOR) BitmapCursorFactoryOzone const std::vector<SkBitmap>& bitmaps, const gfx::Point& hotspot, base::TimeDelta frame_delay) override; + void SetDeviceScaleFactor(float scale) override; private: std::map<mojom::CursorType, scoped_refptr<BitmapCursorOzone>> default_cursors_; + float cursor_scale_factor_ = 1.f; DISALLOW_COPY_AND_ASSIGN(BitmapCursorFactoryOzone); }; diff --git a/chromium/ui/base/data_transfer_policy/DIR_METADATA b/chromium/ui/base/data_transfer_policy/DIR_METADATA index c4701852608..a600cb014f6 100644 --- a/chromium/ui/base/data_transfer_policy/DIR_METADATA +++ b/chromium/ui/base/data_transfer_policy/DIR_METADATA @@ -1,10 +1,10 @@ # Metadata information for this directory. # # For more information on DIR_METADATA files, see: -# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md +# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/README.md # # For the schema of this file, see Metadata message: -# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto +# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto monorail { component: "OS>Software>Enterprise>DLP" diff --git a/chromium/ui/base/data_transfer_policy/OWNERS b/chromium/ui/base/data_transfer_policy/OWNERS index ed5b6d0a1dd..c68ffbf5a48 100644 --- a/chromium/ui/base/data_transfer_policy/OWNERS +++ b/chromium/ui/base/data_transfer_policy/OWNERS @@ -2,4 +2,5 @@ ayaelattar@chromium.org # Secondary: -huangdarwin@chromium.org +mek@chromium.org +pwnall@chromium.org diff --git a/chromium/ui/base/data_transfer_policy/data_transfer_policy_controller.h b/chromium/ui/base/data_transfer_policy/data_transfer_policy_controller.h index edab3d050ad..47fc9d2ee07 100644 --- a/chromium/ui/base/data_transfer_policy/data_transfer_policy_controller.h +++ b/chromium/ui/base/data_transfer_policy/data_transfer_policy_controller.h @@ -7,10 +7,11 @@ #include "base/callback.h" #include "base/component_export.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "ui/base/data_transfer_policy/data_transfer_endpoint.h" namespace content { -class WebContents; +class RenderFrameHost; } namespace ui { @@ -34,26 +35,27 @@ class COMPONENT_EXPORT(UI_BASE_DATA_TRANSFER_POLICY) // nullptr can be passed instead of `data_src` or `data_dst`. If clipboard // read is not allowed, this function will show a notification to the user. - virtual bool IsClipboardReadAllowed( - const DataTransferEndpoint* const data_src, - const DataTransferEndpoint* const data_dst) = 0; + virtual bool IsClipboardReadAllowed(const DataTransferEndpoint* data_src, + const DataTransferEndpoint* data_dst, + absl::optional<size_t> size) = 0; // nullptr can be passed instead of `data_src` or `data_dst`. If clipboard // data is set to be in warning mode, this function will show a notification // to the user. If clipboard read is allowed, `callback` will be invoked with // true. Otherwise `callback` will be invoked with false. - // If `web_contents` got destroyed before `callback` is invoked, the + // If the WebContents of `rfh` got destroyed before `callback` is invoked, the // notification will get closed. - virtual void PasteIfAllowed(const DataTransferEndpoint* const data_src, - const DataTransferEndpoint* const data_dst, - content::WebContents* web_contents, + virtual void PasteIfAllowed(const DataTransferEndpoint* data_src, + const DataTransferEndpoint* data_dst, + absl::optional<size_t> size, + content::RenderFrameHost* rfh, base::OnceCallback<void(bool)> callback) = 0; // nullptr can be passed instead of `data_src` or `data_dst`. If dropping the // data is not allowed, this function will show a notification to the user. - virtual bool IsDragDropAllowed(const DataTransferEndpoint* const data_src, - const DataTransferEndpoint* const data_dst, - const bool is_drop) = 0; + virtual bool IsDragDropAllowed(const DataTransferEndpoint* data_src, + const DataTransferEndpoint* data_dst, + bool is_drop) = 0; protected: DataTransferPolicyController(); diff --git a/chromium/ui/base/dragdrop/DIR_METADATA b/chromium/ui/base/dragdrop/DIR_METADATA index 185817f957c..7d0a14efdc4 100644 --- a/chromium/ui/base/dragdrop/DIR_METADATA +++ b/chromium/ui/base/dragdrop/DIR_METADATA @@ -1,10 +1,10 @@ # Metadata information for this directory. # # For more information on DIR_METADATA files, see: -# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md +# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/README.md # # For the schema of this file, see Metadata message: -# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto +# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto monorail { component: "Blink>DataTransfer" diff --git a/chromium/ui/base/dragdrop/OWNERS b/chromium/ui/base/dragdrop/OWNERS index 7baa107fd51..859c2c608c3 100644 --- a/chromium/ui/base/dragdrop/OWNERS +++ b/chromium/ui/base/dragdrop/OWNERS @@ -1,5 +1 @@ -# Primary: -huangdarwin@chromium.org - -# Secondary: dcheng@chromium.org diff --git a/chromium/ui/base/dragdrop/os_exchange_data_provider_non_backed.cc b/chromium/ui/base/dragdrop/os_exchange_data_provider_non_backed.cc index ef3b1fe29ef..e803c765ef3 100644 --- a/chromium/ui/base/dragdrop/os_exchange_data_provider_non_backed.cc +++ b/chromium/ui/base/dragdrop/os_exchange_data_provider_non_backed.cc @@ -8,6 +8,7 @@ #include <string> #include "base/check.h" +#include "base/containers/contains.h" #include "base/files/file_path.h" #include "base/strings/utf_string_conversions.h" #include "build/chromeos_buildflags.h" diff --git a/chromium/ui/base/dragdrop/os_exchange_data_provider_non_backed_unittest.cc b/chromium/ui/base/dragdrop/os_exchange_data_provider_non_backed_unittest.cc index 8ff37d05d0d..9a9134cb14f 100644 --- a/chromium/ui/base/dragdrop/os_exchange_data_provider_non_backed_unittest.cc +++ b/chromium/ui/base/dragdrop/os_exchange_data_provider_non_backed_unittest.cc @@ -39,7 +39,7 @@ TEST(OSExchangeDataProviderNonBackedTest, CloneTest) { base::Pickle original_pickle; original_pickle.WriteString16(kTestString); - original.SetPickledData(ClipboardFormatType::GetPlainTextType(), + original.SetPickledData(ClipboardFormatType::PlainTextType(), original_pickle); original.SetFileContents(base::FilePath(kFileContentsFileName), std::string(kFileContents)); @@ -63,7 +63,7 @@ TEST(OSExchangeDataProviderNonBackedTest, CloneTest) { EXPECT_EQ(kUrlTitle, copy_title); base::Pickle copy_pickle; - copy->GetPickledData(ClipboardFormatType::GetPlainTextType(), ©_pickle); + copy->GetPickledData(ClipboardFormatType::PlainTextType(), ©_pickle); base::PickleIterator pickle_itr(copy_pickle); std::u16string copy_pickle_string; EXPECT_TRUE(pickle_itr.ReadString16(©_pickle_string)); diff --git a/chromium/ui/base/dragdrop/os_exchange_data_provider_win.cc b/chromium/ui/base/dragdrop/os_exchange_data_provider_win.cc index e6d06aa2b29..67ba22b203b 100644 --- a/chromium/ui/base/dragdrop/os_exchange_data_provider_win.cc +++ b/chromium/ui/base/dragdrop/os_exchange_data_provider_win.cc @@ -18,12 +18,12 @@ #include "base/callback.h" #include "base/check_op.h" #include "base/containers/span.h" +#include "base/cxx17_backports.h" #include "base/files/file_path.h" #include "base/i18n/file_util_icu.h" #include "base/no_destructor.h" #include "base/notreached.h" #include "base/pickle.h" -#include "base/stl_util.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "base/win/scoped_hdc.h" @@ -305,12 +305,12 @@ bool OSExchangeDataProviderWin::DidOriginateFromRenderer() const { void OSExchangeDataProviderWin::SetString(const std::u16string& data) { STGMEDIUM storage = CreateStorageForString(data); data_->contents_.push_back(DataObjectImpl::StoredDataInfo::TakeStorageMedium( - ClipboardFormatType::GetPlainTextType().ToFormatEtc(), storage)); + ClipboardFormatType::PlainTextType().ToFormatEtc(), storage)); // Also add the UTF8-encoded version. storage = CreateStorageForString(base::UTF16ToUTF8(data)); data_->contents_.push_back(DataObjectImpl::StoredDataInfo::TakeStorageMedium( - ClipboardFormatType::GetPlainTextAType().ToFormatEtc(), storage)); + ClipboardFormatType::PlainTextAType().ToFormatEtc(), storage)); } void OSExchangeDataProviderWin::SetURL(const GURL& url, @@ -327,7 +327,7 @@ void OSExchangeDataProviderWin::SetURL(const GURL& url, x_moz_url_str += title; STGMEDIUM storage = CreateStorageForString(x_moz_url_str); data_->contents_.push_back(DataObjectImpl::StoredDataInfo::TakeStorageMedium( - ClipboardFormatType::GetMozUrlType().ToFormatEtc(), storage)); + ClipboardFormatType::MozUrlType().ToFormatEtc(), storage)); // Add a .URL shortcut file for dragging to Explorer. std::wstring valid_file_name = @@ -338,10 +338,10 @@ void OSExchangeDataProviderWin::SetURL(const GURL& url, // Add a UniformResourceLocator link for apps like IE and Word. storage = CreateStorageForString(base::UTF8ToUTF16(url.spec())); data_->contents_.push_back(DataObjectImpl::StoredDataInfo::TakeStorageMedium( - ClipboardFormatType::GetUrlType().ToFormatEtc(), storage)); + ClipboardFormatType::UrlType().ToFormatEtc(), storage)); storage = CreateStorageForString(url.spec()); data_->contents_.push_back(DataObjectImpl::StoredDataInfo::TakeStorageMedium( - ClipboardFormatType::GetUrlAType().ToFormatEtc(), storage)); + ClipboardFormatType::UrlAType().ToFormatEtc(), storage)); // TODO(https://crbug.com/6767): add CF_HTML. @@ -357,7 +357,7 @@ void OSExchangeDataProviderWin::SetFilename(const base::FilePath& path) { if (storage.tymed == TYMED_NULL) return; data_->contents_.push_back(DataObjectImpl::StoredDataInfo::TakeStorageMedium( - ClipboardFormatType::GetIDListType().ToFormatEtc(), storage)); + ClipboardFormatType::IDListType().ToFormatEtc(), storage)); } void OSExchangeDataProviderWin::SetFilenames( @@ -367,7 +367,7 @@ void OSExchangeDataProviderWin::SetFilenames( return; data_->contents_.push_back(DataObjectImpl::StoredDataInfo::TakeStorageMedium( - ClipboardFormatType::GetCFHDropType().ToFormatEtc(), storage)); + ClipboardFormatType::CFHDropType().ToFormatEtc(), storage)); } void OSExchangeDataProviderWin::SetVirtualFileContentsForTesting( @@ -397,7 +397,7 @@ void OSExchangeDataProviderWin::SetVirtualFileContentsForTesting( STGMEDIUM storage = { .tymed = TYMED_HGLOBAL, .hGlobal = hdata, .pUnkForRelease = nullptr}; data_->contents_.push_back(DataObjectImpl::StoredDataInfo::TakeStorageMedium( - ClipboardFormatType::GetFileDescriptorType().ToFormatEtc(), storage)); + ClipboardFormatType::FileDescriptorType().ToFormatEtc(), storage)); for (size_t i = 0; i < num_files; i++) { // Fill in each FILEDESCRIPTORW with file name. @@ -473,8 +473,7 @@ void OSExchangeDataProviderWin::SetVirtualFileContentAtIndexForTesting( storage_for_contents = CreateStorageForBytes(data_buffer.data(), data_buffer.size_bytes()); } - ClipboardFormatType type = - ClipboardFormatType::GetFileContentAtIndexType(index); + ClipboardFormatType type = ClipboardFormatType::FileContentAtIndexType(index); // Pass ownership of |storage_for_contents| here. data_->contents_.push_back(DataObjectImpl::StoredDataInfo::TakeStorageMedium( type.ToFormatEtc(), storage_for_contents)); @@ -494,13 +493,13 @@ void OSExchangeDataProviderWin::SetFileContents( // Add CFSTR_FILEDESCRIPTORW. STGMEDIUM storage = CreateStorageForFileDescriptor(filename); data_->contents_.push_back(DataObjectImpl::StoredDataInfo::TakeStorageMedium( - ClipboardFormatType::GetFileDescriptorType().ToFormatEtc(), storage)); + ClipboardFormatType::FileDescriptorType().ToFormatEtc(), storage)); // Add CFSTR_FILECONTENTS. STGMEDIUM storage_contents = CreateStorageForBytes(file_contents.data(), file_contents.length()); data_->contents_.push_back(DataObjectImpl::StoredDataInfo::TakeStorageMedium( - ClipboardFormatType::GetFileContentZeroType().ToFormatEtc(), + ClipboardFormatType::FileContentZeroType().ToFormatEtc(), storage_contents)); } @@ -513,12 +512,12 @@ void OSExchangeDataProviderWin::SetHtml(const std::u16string& html, std::string cf_html = ClipboardUtil::HtmlToCFHtml(utf8_html, url); STGMEDIUM storage = CreateStorageForBytes(cf_html.c_str(), cf_html.size()); data_->contents_.push_back(DataObjectImpl::StoredDataInfo::TakeStorageMedium( - ClipboardFormatType::GetHtmlType().ToFormatEtc(), storage)); + ClipboardFormatType::HtmlType().ToFormatEtc(), storage)); STGMEDIUM storage_plain = CreateStorageForBytes(utf8_html.c_str(), utf8_html.size()); data_->contents_.push_back(DataObjectImpl::StoredDataInfo::TakeStorageMedium( - ClipboardFormatType::GetTextHtmlType().ToFormatEtc(), storage_plain)); + ClipboardFormatType::TextHtmlType().ToFormatEtc(), storage_plain)); } bool OSExchangeDataProviderWin::GetString(std::u16string* data) const { @@ -683,7 +682,7 @@ void OSExchangeDataProviderWin::SetDownloadFileInfo( // Add CF_HDROP. auto info = DataObjectImpl::StoredDataInfo::TakeStorageMedium( - ClipboardFormatType::GetCFHDropType().ToFormatEtc(), storage); + ClipboardFormatType::CFHDropType().ToFormatEtc(), storage); info->downloader = std::move(download->downloader); data_->contents_.push_back(std::move(info)); @@ -883,7 +882,7 @@ void DataObjectImpl::OnDownloadCompleted(const base::FilePath& file_path) { STGMEDIUM storage = ClipboardUtil::CreateStorageForFileNames( {FileInfo(file_path, base::FilePath())}); content = StoredDataInfo::TakeStorageMedium( - ClipboardFormatType::GetCFHDropType().ToFormatEtc(), storage); + ClipboardFormatType::CFHDropType().ToFormatEtc(), storage); content->downloader = std::move(downloader); break; } diff --git a/chromium/ui/base/dragdrop/os_exchange_data_win_unittest.cc b/chromium/ui/base/dragdrop/os_exchange_data_win_unittest.cc index 10ec539ae5b..09704bea911 100644 --- a/chromium/ui/base/dragdrop/os_exchange_data_win_unittest.cc +++ b/chromium/ui/base/dragdrop/os_exchange_data_win_unittest.cc @@ -9,6 +9,7 @@ #include "base/bind.h" #include "base/callback.h" +#include "base/containers/contains.h" #include "base/files/file_util.h" #include "base/memory/ref_counted.h" #include "base/sequence_checker.h" @@ -918,7 +919,7 @@ TEST_F(OSExchangeDataWinTest, CFHtml) { expected_cf_html += base::UTF16ToUTF8(html); expected_cf_html.append("<!--EndFragment-->\r\n</body>\r\n</html>"); - FORMATETC format = ClipboardFormatType::GetHtmlType().ToFormatEtc(); + FORMATETC format = ClipboardFormatType::HtmlType().ToFormatEtc(); STGMEDIUM medium; IDataObject* data_object = OSExchangeDataProviderWin::GetIDataObject(data); EXPECT_EQ(S_OK, data_object->GetData(&format, &medium)); diff --git a/chromium/ui/base/emoji/DIR_METADATA b/chromium/ui/base/emoji/DIR_METADATA index 198db497480..f3434449946 100644 --- a/chromium/ui/base/emoji/DIR_METADATA +++ b/chromium/ui/base/emoji/DIR_METADATA @@ -1,10 +1,10 @@ # Metadata information for this directory. # # For more information on DIR_METADATA files, see: -# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md +# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/README.md # # For the schema of this file, see Metadata message: -# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto +# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto monorail { component: "Internals>Input" diff --git a/chromium/ui/base/glib/DIR_METADATA b/chromium/ui/base/glib/DIR_METADATA index 63fc482f75d..d072fa6aeae 100644 --- a/chromium/ui/base/glib/DIR_METADATA +++ b/chromium/ui/base/glib/DIR_METADATA @@ -1,10 +1,10 @@ # Metadata information for this directory. # # For more information on DIR_METADATA files, see: -# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md +# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/README.md # # For the schema of this file, see Metadata message: -# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto +# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto monorail { component: "Internals" diff --git a/chromium/ui/base/idle/BUILD.gn b/chromium/ui/base/idle/BUILD.gn index 0e428cf697e..0fb5f6a745a 100644 --- a/chromium/ui/base/idle/BUILD.gn +++ b/chromium/ui/base/idle/BUILD.gn @@ -58,11 +58,10 @@ component("idle") { } if (is_linux) { + sources += [ "idle_linux.cc" ] if (use_dbus) { - sources += [ "idle_linux.cc" ] deps += [ "//dbus" ] - } else { - sources += [ "idle_stub.cc" ] + defines += [ "USE_DBUS" ] } } diff --git a/chromium/ui/base/idle/DIR_METADATA b/chromium/ui/base/idle/DIR_METADATA index 63fc482f75d..d072fa6aeae 100644 --- a/chromium/ui/base/idle/DIR_METADATA +++ b/chromium/ui/base/idle/DIR_METADATA @@ -1,10 +1,10 @@ # Metadata information for this directory. # # For more information on DIR_METADATA files, see: -# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md +# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/README.md # # For the schema of this file, see Metadata message: -# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto +# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto monorail { component: "Internals" diff --git a/chromium/ui/base/idle/idle_internal.cc b/chromium/ui/base/idle/idle_internal.cc index 968d800b74c..5a730a49811 100644 --- a/chromium/ui/base/idle/idle_internal.cc +++ b/chromium/ui/base/idle/idle_internal.cc @@ -4,13 +4,11 @@ #include "ui/base/idle/idle_internal.h" -#include "base/no_destructor.h" - namespace ui { absl::optional<IdleState>& IdleStateForTesting() { - static base::NoDestructor<absl::optional<IdleState>> idle_state; - return *idle_state; + static absl::optional<IdleState> idle_state; + return idle_state; } } // namespace ui diff --git a/chromium/ui/base/idle/idle_lacros.cc b/chromium/ui/base/idle/idle_lacros.cc index 61a50265c7e..6293d52a337 100644 --- a/chromium/ui/base/idle/idle_lacros.cc +++ b/chromium/ui/base/idle/idle_lacros.cc @@ -6,24 +6,21 @@ #include <algorithm> -#include "chromeos/lacros/lacros_chrome_service_impl.h" +#include "chromeos/lacros/lacros_service.h" #include "chromeos/lacros/system_idle_cache.h" namespace ui { int CalculateIdleTime() { base::TimeDelta idle_time = - base::TimeTicks::Now() - chromeos::LacrosChromeServiceImpl::Get() - ->system_idle_cache() - ->last_activity_time(); + base::TimeTicks::Now() - + chromeos::LacrosService::Get()->system_idle_cache()->last_activity_time(); // Clamp to positive in case of timing glitch. return std::max(0, static_cast<int>(idle_time.InSeconds())); } bool CheckIdleStateIsLocked() { - return chromeos::LacrosChromeServiceImpl::Get() - ->system_idle_cache() - ->is_locked(); + return chromeos::LacrosService::Get()->system_idle_cache()->is_locked(); } } // namespace ui diff --git a/chromium/ui/base/idle/idle_linux.cc b/chromium/ui/base/idle/idle_linux.cc index a9c1fafe9cf..869e9cc242f 100644 --- a/chromium/ui/base/idle/idle_linux.cc +++ b/chromium/ui/base/idle/idle_linux.cc @@ -2,25 +2,27 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/notreached.h" +#include "ui/base/idle/idle.h" +#include "ui/base/idle/idle_internal.h" + +#if defined(USE_DBUS) #include "base/bind.h" +#include "base/logging.h" #include "base/memory/scoped_refptr.h" #include "base/no_destructor.h" #include "base/task/task_traits.h" #include "base/task/thread_pool.h" #include "base/task_runner.h" -#include "ui/base/idle/idle.h" - #include "dbus/bus.h" #include "dbus/message.h" #include "dbus/object_path.h" #include "dbus/object_proxy.h" -#include "ui/base/idle/idle_internal.h" +#endif #if defined(USE_X11) #include "ui/base/x/x11_idle_query.h" #include "ui/base/x/x11_screensaver.h" -#else -#include "base/notreached.h" #endif #if defined(USE_OZONE) @@ -30,16 +32,20 @@ namespace ui { +#if defined(USE_DBUS) + namespace { const char kMethodName[] = "GetActive"; const char kSignalName[] = "ActiveChanged"; +// Various names under which the service may be found in different Linux desktop +// environments. struct { const char* service_name; const char* object_path; const char* interface; -} constexpr kInterfaces[] = { +} constexpr kServices[] = { // ksmserver, light-locker, etc. {"org.freedesktop.ScreenSaver", "/org/freedesktop/ScreenSaver", "org.freedesktop.ScreenSaver"}, @@ -54,23 +60,12 @@ struct { {"org.xfce.ScreenSaver", "/", "org.xfce.ScreenSaver"}, }; -bool ServiceNameHasOwner(dbus::Bus* bus, const char* service_name) { - dbus::ObjectProxy* dbus_proxy = - bus->GetObjectProxy(DBUS_SERVICE_DBUS, dbus::ObjectPath(DBUS_PATH_DBUS)); - dbus::MethodCall name_has_owner_call(DBUS_INTERFACE_DBUS, "NameHasOwner"); - dbus::MessageWriter writer(&name_has_owner_call); - writer.AppendString(service_name); - std::unique_ptr<dbus::Response> name_has_owner_response = - dbus_proxy->CallMethodAndBlock(&name_has_owner_call, - dbus::ObjectProxy::TIMEOUT_USE_DEFAULT); - dbus::MessageReader reader(name_has_owner_response.get()); - bool owned = false; - return name_has_owner_response && reader.PopBool(&owned) && owned; -} +constexpr size_t kServiceCount = sizeof(kServices) / sizeof(kServices[0]); -// This class checks for availability of various DBus screensaver interfaces -// and listens for screensaver events on the first one found. -class IdleLinuxImpl { +// This class tries to find the name under which the ScreenSaver D-Bus service +// is registered, and if found the one, connects to the service and subscribes +// to its notifications. +class DBusScreenSaverWatcher { public: enum class LockState { kUnknown, @@ -78,61 +73,119 @@ class IdleLinuxImpl { kUnlocked, }; - IdleLinuxImpl() + DBusScreenSaverWatcher() : task_runner_( base::ThreadPool::CreateSequencedTaskRunner(base::TaskTraits( base::MayBlock(), base::TaskPriority::USER_VISIBLE, - base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN))) {} + base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN))) { + dbus::Bus::Options options; + options.bus_type = dbus::Bus::SESSION; + options.connection_type = dbus::Bus::PRIVATE; + options.dbus_task_runner = task_runner_; + bus_ = base::MakeRefCounted<dbus::Bus>(options); - void Init() { - task_runner_->PostTask(FROM_HERE, - base::BindOnce(&IdleLinuxImpl::InitOnTaskRunner, - base::Unretained(this))); + TryCurrentService(); } LockState lock_state() const { return lock_state_; } private: - ~IdleLinuxImpl() = default; + ~DBusScreenSaverWatcher() = default; - void InitOnTaskRunner() { - dbus::Bus::Options options; - options.bus_type = dbus::Bus::SESSION; - options.connection_type = dbus::Bus::PRIVATE; - options.dbus_task_runner = task_runner_; - bus_ = base::MakeRefCounted<dbus::Bus>(options); + // Starts the initialisation sequence for the current service. Failure at any + // step will increment the service counter and re-start the process. + void TryCurrentService() { + // Detach the proxy, if we have one from the previous attempt. + if (proxy_) { + task_runner_->PostTask( + FROM_HERE, + base::BindOnce(&dbus::ObjectProxy::Detach, base::Unretained(proxy_))); + proxy_ = nullptr; + } - for (const auto& interface : kInterfaces) { - // Calling methods on a non-existent service will lead to a timeout rather - // than an immediate error, so check for service existence first. - if (!ServiceNameHasOwner(bus_.get(), interface.service_name)) - continue; - - // To avoid a race condition, start listenting for state changes before - // querying the state. - auto* proxy = bus_->GetObjectProxy( - interface.service_name, dbus::ObjectPath(interface.object_path)); - if (!proxy->ConnectToSignalAndBlock( - interface.interface, kSignalName, - base::BindRepeating(&IdleLinuxImpl::OnActiveChanged, - base::Unretained(this)))) { - continue; + if (current_service_ >= kServiceCount) { + if (current_service_ == kServiceCount) { + // Log the warning once. + LOG(WARNING) + << "None of the known D-Bus ScreenSaver services could be used."; + ++current_service_; } + return; + } + + // Calling methods on a non-existent service will lead to a timeout rather + // than an immediate error, so check for service existence first. + dbus::ObjectProxy* dbus_proxy = bus_->GetObjectProxy( + DBUS_SERVICE_DBUS, dbus::ObjectPath(DBUS_PATH_DBUS)); + dbus::MethodCall name_has_owner_call(DBUS_INTERFACE_DBUS, "NameHasOwner"); + dbus::MessageWriter writer(&name_has_owner_call); + writer.AppendString(kServices[current_service_].service_name); + dbus_proxy->CallMethod( + &name_has_owner_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + base::BindOnce(&DBusScreenSaverWatcher::OnServiceHasOwner, + weak_factory_.GetWeakPtr())); + } + + void OnServiceHasOwner(dbus::Response* response) { + DCHECK_LT(current_service_, kServiceCount); - // Some service owners (eg. gsd-screensaver-proxy) advertise the correct - // methods on org.freedesktop.ScreenSaver, but calling them will result in - // a NotImplemented DBus error. To ensure the service owner will send - // state change events, make an explicit method call and check that no - // error is returned. - dbus::MethodCall method_call(interface.interface, kMethodName); - auto response = proxy->CallMethodAndBlock( - &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT); - if (response && UpdateLockState(response.get())) - return; - - // Try the next interface. - proxy->Detach(); + dbus::MessageReader reader(response); + bool owned = false; + if (!response || !reader.PopBool(&owned) || !owned) { + VLOG(1) << kServices[current_service_].service_name + << " D-Bus service does not exist"; + ++current_service_; + return TryCurrentService(); + } + + // Now connect the ActiveChanged signal. + proxy_ = bus_->GetObjectProxy( + kServices[current_service_].service_name, + dbus::ObjectPath(kServices[current_service_].object_path)); + proxy_->ConnectToSignal( + kServices[current_service_].interface, kSignalName, + base::BindRepeating(&DBusScreenSaverWatcher::OnActiveChanged, + weak_factory_.GetWeakPtr()), + base::BindOnce(&DBusScreenSaverWatcher::OnActiveChangedSignalConnected, + weak_factory_.GetWeakPtr())); + } + + void OnActiveChangedSignalConnected(const std::string& interface, + const std::string& signal, + bool succeeded) { + DCHECK_LT(current_service_, kServiceCount); + DCHECK_EQ(interface, kServices[current_service_].interface); + DCHECK_EQ(signal, kSignalName); + + if (!succeeded) { + VLOG(1) << "Cannot connect to " << kSignalName << " signal of " + << interface << " D-Bus service"; + ++current_service_; + return TryCurrentService(); + } + + // Some service owners (e.g., gsd-screensaver-proxy) advertise the correct + // methods on org.freedesktop.ScreenSaver, but calling them will result in + // a NotImplemented DBus error. To ensure the service owner will send + // state change events, and to have the correct current state of the lock, + // make an explicit method call and check that no error is returned. + dbus::MethodCall method_call(kServices[current_service_].interface, + kMethodName); + proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + base::BindOnce(&DBusScreenSaverWatcher::OnGetActive, + weak_factory_.GetWeakPtr())); + } + + void OnGetActive(dbus::Response* response) { + DCHECK_LT(current_service_, kServiceCount); + + if (!response || !UpdateLockState(response)) { + VLOG(1) + << "Call to " << kMethodName << " method of " + << kServices[current_service_].interface << " D-Bus service failed"; + ++current_service_; + return TryCurrentService(); } } @@ -147,23 +200,29 @@ class IdleLinuxImpl { return true; } - // Only accessed on the task runner. - scoped_refptr<dbus::Bus> bus_; + LockState lock_state_ = LockState::kUnknown; + + // Index of the service (in the kServices array) that is currently being + // initialised or used. A value out of bounds means that no working service + // was found. + size_t current_service_ = 0; - // Only accessed on the main thread. + scoped_refptr<dbus::Bus> bus_; scoped_refptr<base::SequencedTaskRunner> task_runner_; + dbus::ObjectProxy* proxy_ = nullptr; - // Read on the main thread and written on the task runner. - std::atomic<LockState> lock_state_{LockState::kUnknown}; + base::WeakPtrFactory<DBusScreenSaverWatcher> weak_factory_{this}; }; -IdleLinuxImpl* GetIdleLinuxImpl() { - static base::NoDestructor<IdleLinuxImpl> impl; +DBusScreenSaverWatcher* GetDBusScreenSaverWatcher() { + static base::NoDestructor<DBusScreenSaverWatcher> impl; return impl.get(); } } // namespace +#endif // defined(USE_DBUS) + int CalculateIdleTime() { #if defined(USE_OZONE) if (features::IsUsingOzonePlatform()) { @@ -187,9 +246,11 @@ bool CheckIdleStateIsLocked() { if (IdleStateForTesting().has_value()) return IdleStateForTesting().value() == IDLE_STATE_LOCKED; - auto lock_state = GetIdleLinuxImpl()->lock_state(); - if (lock_state != IdleLinuxImpl::LockState::kUnknown) - return lock_state == IdleLinuxImpl::LockState::kLocked; +#if defined(USE_DBUS) + auto lock_state = GetDBusScreenSaverWatcher()->lock_state(); + if (lock_state != DBusScreenSaverWatcher::LockState::kUnknown) + return lock_state == DBusScreenSaverWatcher::LockState::kLocked; +#endif #if defined(USE_OZONE) if (features::IsUsingOzonePlatform()) { diff --git a/chromium/ui/base/idle/idle_stub.cc b/chromium/ui/base/idle/idle_stub.cc deleted file mode 100644 index 77ca1dd9638..00000000000 --- a/chromium/ui/base/idle/idle_stub.cc +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2021 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/base/idle/idle.h" - -#include "base/notreached.h" - -namespace ui { - -int CalculateIdleTime() { - NOTIMPLEMENTED(); - return 0; -} - -bool CheckIdleStateIsLocked() { - NOTIMPLEMENTED(); - return false; -} - -} // namespace ui diff --git a/chromium/ui/base/ime/DIR_METADATA b/chromium/ui/base/ime/DIR_METADATA index 9f8d08102a4..d50dc8612ab 100644 --- a/chromium/ui/base/ime/DIR_METADATA +++ b/chromium/ui/base/ime/DIR_METADATA @@ -1,10 +1,10 @@ # Metadata information for this directory. # # For more information on DIR_METADATA files, see: -# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md +# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/README.md # # For the schema of this file, see Metadata message: -# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto +# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto monorail { component: "OS>Inputs" # for Chrome/Chromium OS only. diff --git a/chromium/ui/base/ime/OWNERS b/chromium/ui/base/ime/OWNERS index 65b2c66cb9b..c4ce4012767 100644 --- a/chromium/ui/base/ime/OWNERS +++ b/chromium/ui/base/ime/OWNERS @@ -4,8 +4,5 @@ yhanada@chromium.org keithlee@chromium.org shend@chromium.org -# backup reviewers. -yukishiino@chromium.org - # For Windows. yukawa@chromium.org diff --git a/chromium/ui/base/ime/PRESUBMIT.py b/chromium/ui/base/ime/PRESUBMIT.py index e9f33bbe89b..9bfd2f3a5bd 100644 --- a/chromium/ui/base/ime/PRESUBMIT.py +++ b/chromium/ui/base/ime/PRESUBMIT.py @@ -11,6 +11,8 @@ for more details about the presubmit API built into depot_tools. import filecmp import os +USE_PYTHON3 = True + CHARACTER_COMPOSER_DATA_SOURCES=['character_composer_sequences.txt'] CHARACTER_COMPOSER_DATA_HEADER='character_composer_data.h' CHARACTER_COMPOSER_DATA_GENERATOR='generate_character_composer_data.py' @@ -18,10 +20,9 @@ CHARACTER_COMPOSER_DATA_GENERATOR='generate_character_composer_data.py' def CheckCharacterComposerData(input_api, output_api): results = [] whereami = input_api.PresubmitLocalPath() - files = map(lambda x: input_api.os_path.join(whereami, x), - (CHARACTER_COMPOSER_DATA_SOURCES + - [CHARACTER_COMPOSER_DATA_HEADER, - CHARACTER_COMPOSER_DATA_GENERATOR])) + files = [input_api.os_path.join(whereami, x) for x in + CHARACTER_COMPOSER_DATA_SOURCES + + [CHARACTER_COMPOSER_DATA_HEADER, CHARACTER_COMPOSER_DATA_GENERATOR]] if not input_api.AffectedFiles( file_filter=lambda x: x.AbsoluteLocalPath() in files): diff --git a/chromium/ui/base/ime/dummy_input_method.cc b/chromium/ui/base/ime/dummy_input_method.cc index 50f6be85e29..857a1f6cd86 100644 --- a/chromium/ui/base/ime/dummy_input_method.cc +++ b/chromium/ui/base/ime/dummy_input_method.cc @@ -64,28 +64,15 @@ TextInputType DummyInputMethod::GetTextInputType() const { return TEXT_INPUT_TYPE_NONE; } -TextInputMode DummyInputMethod::GetTextInputMode() const { - return TEXT_INPUT_MODE_DEFAULT; -} - -int DummyInputMethod::GetTextInputFlags() const { - return 0; -} - -bool DummyInputMethod::CanComposeInline() const { - return true; -} - bool DummyInputMethod::IsCandidatePopupOpen() const { return false; } -bool DummyInputMethod::GetClientShouldDoLearning() { - return false; -} - void DummyInputMethod::ShowVirtualKeyboardIfEnabled() {} +void DummyInputMethod::SetVirtualKeyboardVisibilityIfEnabled(bool should_show) { +} + void DummyInputMethod::AddObserver(InputMethodObserver* observer) { } diff --git a/chromium/ui/base/ime/dummy_input_method.h b/chromium/ui/base/ime/dummy_input_method.h index f403e5f0bb4..76384c83fe6 100644 --- a/chromium/ui/base/ime/dummy_input_method.h +++ b/chromium/ui/base/ime/dummy_input_method.h @@ -38,12 +38,9 @@ class DummyInputMethod : public InputMethod { void OnCaretBoundsChanged(const TextInputClient* client) override; void CancelComposition(const TextInputClient* client) override; TextInputType GetTextInputType() const override; - TextInputMode GetTextInputMode() const override; - int GetTextInputFlags() const override; - bool CanComposeInline() const override; bool IsCandidatePopupOpen() const override; - bool GetClientShouldDoLearning() override; void ShowVirtualKeyboardIfEnabled() override; + void SetVirtualKeyboardVisibilityIfEnabled(bool should_show) override; void AddObserver(InputMethodObserver* observer) override; void RemoveObserver(InputMethodObserver* observer) override; diff --git a/chromium/ui/base/ime/dummy_text_input_client.cc b/chromium/ui/base/ime/dummy_text_input_client.cc index 4507c8f52a6..f2527b8e4f1 100644 --- a/chromium/ui/base/ime/dummy_text_input_client.cc +++ b/chromium/ui/base/ime/dummy_text_input_client.cc @@ -177,6 +177,16 @@ bool DummyTextInputClient::SetAutocorrectRange( return true; } +absl::optional<GrammarFragment> DummyTextInputClient::GetGrammarFragment( + const gfx::Range& range) { + for (const auto& fragment : grammar_fragments_) { + if (fragment.range.Contains(range)) { + return fragment; + } + } + return absl::nullopt; +} + bool DummyTextInputClient::ClearGrammarFragments(const gfx::Range& range) { grammar_fragments_.clear(); return true; diff --git a/chromium/ui/base/ime/dummy_text_input_client.h b/chromium/ui/base/ime/dummy_text_input_client.h index 114f91da393..727b301f55c 100644 --- a/chromium/ui/base/ime/dummy_text_input_client.h +++ b/chromium/ui/base/ime/dummy_text_input_client.h @@ -70,6 +70,8 @@ class DummyTextInputClient : public TextInputClient { gfx::Range GetAutocorrectRange() const override; gfx::Rect GetAutocorrectCharacterBounds() const override; bool SetAutocorrectRange(const gfx::Range& range) override; + absl::optional<GrammarFragment> GetGrammarFragment( + const gfx::Range& range) override; bool ClearGrammarFragments(const gfx::Range& range) override; bool AddGrammarFragments( const std::vector<GrammarFragment>& fragments) override; diff --git a/chromium/ui/base/ime/fake_text_input_client.cc b/chromium/ui/base/ime/fake_text_input_client.cc index 1e3b7a4347b..2077fa4cc26 100644 --- a/chromium/ui/base/ime/fake_text_input_client.cc +++ b/chromium/ui/base/ime/fake_text_input_client.cc @@ -32,7 +32,12 @@ void FakeTextInputClient::SetTextAndSelection(const std::u16string& text, } void FakeTextInputClient::SetCompositionText( - const CompositionText& composition) {} + const CompositionText& composition) { + text_.insert(selection_.start(), composition.text); + const size_t new_cursor = selection_.start() + composition.text.length(); + composition_range_ = gfx::Range(selection_.start(), new_cursor); + selection_ = gfx::Range(new_cursor, new_cursor); +} uint32_t FakeTextInputClient::ConfirmCompositionText(bool keep_selection) { return UINT32_MAX; @@ -43,18 +48,18 @@ void FakeTextInputClient::ClearCompositionText() {} void FakeTextInputClient::InsertText( const std::u16string& text, TextInputClient::InsertTextCursorBehavior cursor_behavior) { - if (!composition_range_.is_empty()) { - text_.replace(composition_range_.start(), composition_range_.length(), - text); - } else { - text_.replace(selection_.start(), selection_.length(), text); - } + const gfx::Range replacement_range = + composition_range_.is_empty() ? selection_ : composition_range_; + + text_.replace(replacement_range.start(), replacement_range.length(), text); if (cursor_behavior == TextInputClient::InsertTextCursorBehavior::kMoveCursorAfterText) { - selection_ = gfx::Range(selection_.start() + text.length(), - selection_.end() + text.length()); + selection_ = gfx::Range(replacement_range.start() + text.length(), + replacement_range.start() + text.length()); } + + composition_range_ = gfx::Range(); } void FakeTextInputClient::InsertChar(const KeyEvent& event) {} diff --git a/chromium/ui/base/ime/fuchsia/input_method_fuchsia.cc b/chromium/ui/base/ime/fuchsia/input_method_fuchsia.cc index 4d88241eecd..7751b9f0cae 100644 --- a/chromium/ui/base/ime/fuchsia/input_method_fuchsia.cc +++ b/chromium/ui/base/ime/fuchsia/input_method_fuchsia.cc @@ -17,15 +17,19 @@ namespace ui { -InputMethodFuchsia::InputMethodFuchsia(internal::InputMethodDelegate* delegate, +InputMethodFuchsia::InputMethodFuchsia(bool enable_virtual_keyboard, + internal::InputMethodDelegate* delegate, fuchsia::ui::views::ViewRef view_ref) - : InputMethodBase(delegate), - virtual_keyboard_controller_(std::move(view_ref), this) {} + : InputMethodBase(delegate) { + if (enable_virtual_keyboard) + virtual_keyboard_controller_.emplace(std::move(view_ref), this); +} InputMethodFuchsia::~InputMethodFuchsia() {} VirtualKeyboardController* InputMethodFuchsia::GetVirtualKeyboardController() { - return &virtual_keyboard_controller_; + return virtual_keyboard_controller_ ? &virtual_keyboard_controller_.value() + : nullptr; } ui::EventDispatchDetails InputMethodFuchsia::DispatchKeyEvent( @@ -50,15 +54,24 @@ ui::EventDispatchDetails InputMethodFuchsia::DispatchKeyEvent( } void InputMethodFuchsia::CancelComposition(const TextInputClient* client) { - // FIDL asynchronicity makes it impossible to know whether a recent - // visibility update might be in flight, so always call Dismiss. - virtual_keyboard_controller_.DismissVirtualKeyboard(); + if (virtual_keyboard_controller_) { + // FIDL asynchronicity makes it impossible to know whether a recent + // visibility update might be in flight, so always call Dismiss. + virtual_keyboard_controller_->DismissVirtualKeyboard(); + } } void InputMethodFuchsia::OnTextInputTypeChanged(const TextInputClient* client) { InputMethodBase::OnTextInputTypeChanged(client); - virtual_keyboard_controller_.UpdateTextType(); + if (!virtual_keyboard_controller_) + return; + + if (IsTextInputTypeNone()) { + virtual_keyboard_controller_->DismissVirtualKeyboard(); + } else { + virtual_keyboard_controller_->UpdateTextType(); + } } void InputMethodFuchsia::OnCaretBoundsChanged(const TextInputClient* client) {} diff --git a/chromium/ui/base/ime/fuchsia/input_method_fuchsia.h b/chromium/ui/base/ime/fuchsia/input_method_fuchsia.h index 52b2b6b0eb2..eeab62cc6fd 100644 --- a/chromium/ui/base/ime/fuchsia/input_method_fuchsia.h +++ b/chromium/ui/base/ime/fuchsia/input_method_fuchsia.h @@ -11,6 +11,7 @@ #include "base/component_export.h" #include "base/macros.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "ui/base/ime/fuchsia/virtual_keyboard_controller_fuchsia.h" #include "ui/base/ime/input_method_base.h" #include "ui/base/ime/input_method_delegate.h" @@ -24,7 +25,8 @@ namespace ui { class COMPONENT_EXPORT(UI_BASE_IME_FUCHSIA) InputMethodFuchsia : public InputMethodBase { public: - InputMethodFuchsia(internal::InputMethodDelegate* delegate, + InputMethodFuchsia(bool enable_virtual_keyboard, + internal::InputMethodDelegate* delegate, fuchsia::ui::views::ViewRef view_ref); ~InputMethodFuchsia() override; @@ -40,7 +42,7 @@ class COMPONENT_EXPORT(UI_BASE_IME_FUCHSIA) InputMethodFuchsia bool IsCandidatePopupOpen() const final; private: - VirtualKeyboardControllerFuchsia virtual_keyboard_controller_; + absl::optional<VirtualKeyboardControllerFuchsia> virtual_keyboard_controller_; }; } // namespace ui diff --git a/chromium/ui/base/ime/fuchsia/keyboard_client.cc b/chromium/ui/base/ime/fuchsia/keyboard_client.cc index ab9fc10fbc2..4d3298e8331 100644 --- a/chromium/ui/base/ime/fuchsia/keyboard_client.cc +++ b/chromium/ui/base/ime/fuchsia/keyboard_client.cc @@ -8,8 +8,10 @@ #include "base/logging.h" #include "base/notreached.h" #include "ui/events/fuchsia/input_event_sink.h" +#include "ui/events/keycodes/dom/dom_code.h" #include "ui/events/keycodes/dom/keycode_converter.h" #include "ui/events/keycodes/keyboard_code_conversion.h" +#include "ui/events/keycodes/keyboard_code_conversion_fuchsia.h" namespace ui { @@ -34,6 +36,52 @@ int ModifiersToEventFlags(const fuchsia::ui::input3::Modifiers& modifiers) { return event_flags; } +absl::optional<EventType> ConvertKeyEventType( + fuchsia::ui::input3::KeyEventType type) { + switch (type) { + case fuchsia::ui::input3::KeyEventType::PRESSED: + return ET_KEY_PRESSED; + break; + case fuchsia::ui::input3::KeyEventType::RELEASED: + return ET_KEY_RELEASED; + break; + case fuchsia::ui::input3::KeyEventType::SYNC: + case fuchsia::ui::input3::KeyEventType::CANCEL: + // SYNC and CANCEL should not generate ui::Events. + return absl::nullopt; + default: + NOTREACHED() << "Unknown KeyEventType received: " + << static_cast<int>(type); + return absl::nullopt; + } +} + +// Creates an event for an event which has no |key|. +absl::optional<ui::KeyEvent> ConvertToCharacterEvent( + const fuchsia::ui::input3::KeyEvent& key_event) { + DCHECK(!key_event.has_key()); + + absl::optional<EventType> event_type = ConvertKeyEventType(key_event.type()); + if (!event_type) { + return absl::nullopt; + } + if (event_type != ET_KEY_PRESSED) { + // Keypress phase cannot be tracked on keypresses without hardware keys, + // so only handle the "pressed" edge transition. + return absl::nullopt; + } + + const uint32_t codepoint = key_event.key_meaning().codepoint(); + if (codepoint > std::numeric_limits<char16_t>::max()) { + // TODO(crbug.com/1220260): Handle codepoints outside the BMP. + return absl::nullopt; + } + + return ui::KeyEvent(*event_type, VKEY_UNKNOWN, DomCode::NONE, + EF_IS_SYNTHESIZED, DomKey::FromCharacter(codepoint), + base::TimeTicks::FromZxTime(key_event.timestamp()), true); +} + } // namespace KeyboardClient::KeyboardClient(fuchsia::ui::input3::Keyboard* keyboard_service, @@ -58,6 +106,11 @@ KeyboardClient::~KeyboardClient() = default; void KeyboardClient::OnKeyEvent( fuchsia::ui::input3::KeyEvent key_event, fuchsia::ui::input3::KeyboardListener::OnKeyEventCallback callback) { + if (!IsValid(key_event)) { + binding_.Close(ZX_ERR_INVALID_ARGS); + return; + } + if (ProcessKeyEvent(key_event)) { callback(fuchsia::ui::input3::KeyEventStatus::HANDLED); } else { @@ -65,45 +118,52 @@ void KeyboardClient::OnKeyEvent( } } +bool KeyboardClient::IsValid(const fuchsia::ui::input3::KeyEvent& key_event) { + if (!key_event.has_type() || !key_event.has_timestamp()) + return false; + + if (!key_event.has_key() && !key_event.has_key_meaning()) + return false; + + return true; +} + bool KeyboardClient::ProcessKeyEvent( const fuchsia::ui::input3::KeyEvent& key_event) { - if (!key_event.has_type() || !key_event.has_key() || - !key_event.has_timestamp()) { - LOG(ERROR) << "Could not process incomplete input3::KeyEvent."; + const bool generate_character_event = !key_event.has_key(); + absl::optional<ui::KeyEvent> converted_event; + if (generate_character_event) { + converted_event = ConvertToCharacterEvent(key_event); + } else { + UpdateCachedModifiers(key_event); + converted_event = ConvertKeystrokeEvent(key_event); + } + if (!converted_event) { return false; } - // Update activation flags of modifier keys (SHIFT, ALT, etc). This needs to - // be done for all key event types. - UpdatedCachedModifiers(key_event); + event_sink_->DispatchEvent(&converted_event.value()); + return converted_event->handled(); +} - EventType event_type; - switch (key_event.type()) { - case fuchsia::ui::input3::KeyEventType::PRESSED: - event_type = ET_KEY_PRESSED; - break; - case fuchsia::ui::input3::KeyEventType::RELEASED: - event_type = ET_KEY_RELEASED; - break; - case fuchsia::ui::input3::KeyEventType::SYNC: - case fuchsia::ui::input3::KeyEventType::CANCEL: - // SYNC and CANCEL should not generate ui::Events. - return true; - default: - NOTIMPLEMENTED() << "Unknown KeyEventType received: " - << static_cast<int>(event_type); - return false; +absl::optional<ui::KeyEvent> KeyboardClient::ConvertKeystrokeEvent( + const fuchsia::ui::input3::KeyEvent& key_event) { + DCHECK(key_event.has_key()); + + absl::optional<EventType> event_type = ConvertKeyEventType(key_event.type()); + if (!event_type) { + return absl::nullopt; } // Convert |key_event| to a ui::KeyEvent. - DomCode dom_code = - KeycodeConverter::UsbKeycodeToDomCode(static_cast<int>(key_event.key())); int event_flags = EventFlagsForCachedModifiers(); if (key_event.has_modifiers()) event_flags |= ModifiersToEventFlags(key_event.modifiers()); // TODO(https://crbug.com/1187257): Use input3.KeyMeaning instead of US layout // as the default. + DomCode dom_code = + KeycodeConverter::UsbKeycodeToDomCode(static_cast<int>(key_event.key())); DomKey dom_key; KeyboardCode key_code; if (!DomCodeToUsLayoutDomKey(dom_code, event_flags, &dom_key, &key_code)) { @@ -111,16 +171,13 @@ bool KeyboardClient::ProcessKeyEvent( << static_cast<uint32_t>(key_event.key()); } - ui::KeyEvent ui_key_event(event_type, key_code, dom_code, event_flags, - dom_key, - base::TimeTicks::FromZxTime(key_event.timestamp())); - event_sink_->DispatchEvent(&ui_key_event); - return ui_key_event.handled(); + return ui::KeyEvent(*event_type, key_code, dom_code, event_flags, dom_key, + base::TimeTicks::FromZxTime(key_event.timestamp())); } // TODO(https://crbug.com/850697): Add additional modifiers as they become // supported. -void KeyboardClient::UpdatedCachedModifiers( +void KeyboardClient::UpdateCachedModifiers( const fuchsia::ui::input3::KeyEvent& key_event) { // A SYNC event indicates that the key was pressed while the view gained input // focus. A CANCEL event indicates the key was held when the view lost input diff --git a/chromium/ui/base/ime/fuchsia/keyboard_client.h b/chromium/ui/base/ime/fuchsia/keyboard_client.h index 3c213077e83..e3cad59f6b0 100644 --- a/chromium/ui/base/ime/fuchsia/keyboard_client.h +++ b/chromium/ui/base/ime/fuchsia/keyboard_client.h @@ -9,6 +9,7 @@ #include <lib/fidl/cpp/binding.h> #include "base/component_export.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "ui/events/event.h" namespace ui { @@ -34,6 +35,12 @@ class COMPONENT_EXPORT(UI_BASE_IME_FUCHSIA) KeyboardClient fuchsia::ui::input3::KeyboardListener::OnKeyEventCallback callback) final; private: + bool IsValid(const fuchsia::ui::input3::KeyEvent& key_event); + + // Returns an unset value if the |key_event| type is unsupported. + absl::optional<ui::KeyEvent> ConvertKeystrokeEvent( + const fuchsia::ui::input3::KeyEvent& key_event); + // Handles converting and propagating |key_event|. Returns false if critical // information about |key_event| is missing, or if the key's event type is not // supported. @@ -42,7 +49,7 @@ class COMPONENT_EXPORT(UI_BASE_IME_FUCHSIA) KeyboardClient bool ProcessKeyEvent(const fuchsia::ui::input3::KeyEvent& key_event); // Update the value of modifiers such as shift. - void UpdatedCachedModifiers(const fuchsia::ui::input3::KeyEvent& key_event); + void UpdateCachedModifiers(const fuchsia::ui::input3::KeyEvent& key_event); // Translate state of locally tracked modifier keys (e.g. shift, alt) into // ui::Event flags. diff --git a/chromium/ui/base/ime/fuchsia/virtual_keyboard_controller_fuchsia.cc b/chromium/ui/base/ime/fuchsia/virtual_keyboard_controller_fuchsia.cc index 25de7d0c6a0..358cc48756f 100644 --- a/chromium/ui/base/ime/fuchsia/virtual_keyboard_controller_fuchsia.cc +++ b/chromium/ui/base/ime/fuchsia/virtual_keyboard_controller_fuchsia.cc @@ -11,11 +11,9 @@ #include "base/check.h" #include "base/fuchsia/fuchsia_logging.h" #include "base/fuchsia/process_context.h" +#include "ui/base/ime/text_input_client.h" namespace ui { -namespace { - -} // namespace VirtualKeyboardControllerFuchsia::VirtualKeyboardControllerFuchsia( fuchsia::ui::views::ViewRef view_ref, @@ -92,7 +90,12 @@ void VirtualKeyboardControllerFuchsia::OnVisibilityChange(bool is_visible) { // Returns the FIDL enum representation of the current InputMode. fuchsia::input::virtualkeyboard::TextType VirtualKeyboardControllerFuchsia::GetFocusedTextType() const { - switch (input_method_->GetTextInputMode()) { + TextInputClient* client = input_method_->GetTextInputClient(); + // This function should only be called when there's focus, so there should + // always be a TextInputClient. + DCHECK(client); + + switch (client->GetTextInputMode()) { case TEXT_INPUT_MODE_NUMERIC: case TEXT_INPUT_MODE_DECIMAL: return fuchsia::input::virtualkeyboard::TextType::NUMERIC; @@ -100,21 +103,40 @@ VirtualKeyboardControllerFuchsia::GetFocusedTextType() const { case TEXT_INPUT_MODE_TEL: return fuchsia::input::virtualkeyboard::TextType::PHONE; - case TEXT_INPUT_MODE_DEFAULT: - case TEXT_INPUT_MODE_NONE: case TEXT_INPUT_MODE_TEXT: case TEXT_INPUT_MODE_URL: case TEXT_INPUT_MODE_EMAIL: case TEXT_INPUT_MODE_SEARCH: return fuchsia::input::virtualkeyboard::TextType::ALPHANUMERIC; + + // Should be handled in InputMethodFuchsia. + case TEXT_INPUT_MODE_NONE: + NOTREACHED(); + return fuchsia::input::virtualkeyboard::TextType::ALPHANUMERIC; + + case TEXT_INPUT_MODE_DEFAULT: + // Fall-through to using TextInputType. + break; + } + + switch (client->GetTextInputType()) { + case TEXT_INPUT_TYPE_NUMBER: + return fuchsia::input::virtualkeyboard::TextType::NUMERIC; + + case TEXT_INPUT_TYPE_TELEPHONE: + return fuchsia::input::virtualkeyboard::TextType::PHONE; + + default: + return fuchsia::input::virtualkeyboard::TextType::ALPHANUMERIC; } } void VirtualKeyboardControllerFuchsia::UpdateTextType() { // Only send updates if the type has changed. auto new_type = GetFocusedTextType(); + DVLOG(1) << "UpdateTextType() called (current: " << requested_type_ + << ", new: " << new_type << ")"; if (new_type != requested_type_) { - DVLOG(1) << "SetTextType " << static_cast<int>(new_type); controller_service_->SetTextType(new_type); requested_type_ = new_type; } diff --git a/chromium/ui/base/ime/generate_character_composer_data.py b/chromium/ui/base/ime/generate_character_composer_data.py index a0becd22479..44a58fad9b2 100755 --- a/chromium/ui/base/ime/generate_character_composer_data.py +++ b/chromium/ui/base/ime/generate_character_composer_data.py @@ -430,6 +430,7 @@ def main(argv): out.write('// FROM {}\n\n'.format(' '.join(args.inputs))) guard = args.guard if args.guard else args.output guard = ''.join([c.upper() if c.isalpha() else '_' for c in guard]) + guard = 'UI_BASE_IME_' + guard out.write('#ifndef {0}_\n#define {0}_\n'.format(guard)) Assembler(args, parse_tree).Write(out) out.write('#endif // {}_\n'.format(guard)) diff --git a/chromium/ui/base/ime/input_method.h b/chromium/ui/base/ime/input_method.h index 6e411aa2e5e..99c3f085902 100644 --- a/chromium/ui/base/ime/input_method.h +++ b/chromium/ui/base/ime/input_method.h @@ -133,29 +133,16 @@ class InputMethod { // ui::TEXT_INPUT_TYPE_NONE if there is no focused client. virtual TextInputType GetTextInputType() const = 0; - // Gets the text input mode of the focused text input client. Returns - // ui::TEXT_INPUT_TYPE_DEFAULT if there is no focused client. - virtual TextInputMode GetTextInputMode() const = 0; - - // Gets the text input flags of the focused text input client. Returns - // 0 if there is no focused client. - virtual int GetTextInputFlags() const = 0; - - // Checks if the focused text input client supports inline composition. - virtual bool CanComposeInline() const = 0; - // Returns true if we know for sure that a candidate window (or IME suggest, // etc.) is open. Returns false if no popup window is open or the detection // of IME popups is not supported. virtual bool IsCandidatePopupOpen() const = 0; - // Check whether text entered into the focused text input client should be - // used to improve typing suggestions for the user. - virtual bool GetClientShouldDoLearning() = 0; - // Displays an on screen keyboard if enabled. virtual void ShowVirtualKeyboardIfEnabled() = 0; + virtual void SetVirtualKeyboardVisibilityIfEnabled(bool should_show) = 0; + // Management of the observer list. virtual void AddObserver(InputMethodObserver* observer) = 0; virtual void RemoveObserver(InputMethodObserver* observer) = 0; diff --git a/chromium/ui/base/ime/input_method_base.cc b/chromium/ui/base/ime/input_method_base.cc index 73bfbfb06c6..55592e980a6 100644 --- a/chromium/ui/base/ime/input_method_base.cc +++ b/chromium/ui/base/ime/input_method_base.cc @@ -86,26 +86,6 @@ TextInputType InputMethodBase::GetTextInputType() const { return client ? client->GetTextInputType() : TEXT_INPUT_TYPE_NONE; } -TextInputMode InputMethodBase::GetTextInputMode() const { - TextInputClient* client = GetTextInputClient(); - return client ? client->GetTextInputMode() : TEXT_INPUT_MODE_DEFAULT; -} - -int InputMethodBase::GetTextInputFlags() const { - TextInputClient* client = GetTextInputClient(); - return client ? client->GetTextInputFlags() : 0; -} - -bool InputMethodBase::CanComposeInline() const { - TextInputClient* client = GetTextInputClient(); - return client ? client->CanComposeInline() : true; -} - -bool InputMethodBase::GetClientShouldDoLearning() { - TextInputClient* client = GetTextInputClient(); - return client && client->ShouldDoLearning(); -} - void InputMethodBase::ShowVirtualKeyboardIfEnabled() { for (InputMethodObserver& observer : observer_list_) observer.OnShowVirtualKeyboardIfEnabled(); @@ -113,6 +93,19 @@ void InputMethodBase::ShowVirtualKeyboardIfEnabled() { keyboard->DisplayVirtualKeyboard(); } +void InputMethodBase::SetVirtualKeyboardVisibilityIfEnabled(bool should_show) { + for (InputMethodObserver& observer : observer_list_) + observer.OnVirtualKeyboardVisibilityChangedIfEnabled(should_show); + auto* keyboard = GetVirtualKeyboardController(); + if (keyboard) { + if (should_show) { + keyboard->DisplayVirtualKeyboard(); + } else { + keyboard->DismissVirtualKeyboard(); + } + } +} + void InputMethodBase::AddObserver(InputMethodObserver* observer) { observer_list_.AddObserver(observer); } diff --git a/chromium/ui/base/ime/input_method_base.h b/chromium/ui/base/ime/input_method_base.h index b2886fd8dc4..8210a5dddef 100644 --- a/chromium/ui/base/ime/input_method_base.h +++ b/chromium/ui/base/ime/input_method_base.h @@ -58,11 +58,8 @@ class COMPONENT_EXPORT(UI_BASE_IME) InputMethodBase // implementation. void OnTextInputTypeChanged(const TextInputClient* client) override; TextInputType GetTextInputType() const override; - TextInputMode GetTextInputMode() const override; - int GetTextInputFlags() const override; - bool CanComposeInline() const override; - bool GetClientShouldDoLearning() override; void ShowVirtualKeyboardIfEnabled() override; + void SetVirtualKeyboardVisibilityIfEnabled(bool should_show) override; void AddObserver(InputMethodObserver* observer) override; void RemoveObserver(InputMethodObserver* observer) override; diff --git a/chromium/ui/base/ime/input_method_observer.h b/chromium/ui/base/ime/input_method_observer.h index 1581c4a21ca..f35d69229a8 100644 --- a/chromium/ui/base/ime/input_method_observer.h +++ b/chromium/ui/base/ime/input_method_observer.h @@ -38,6 +38,10 @@ class COMPONENT_EXPORT(UI_BASE_IME) InputMethodObserver { // Called when a user gesture should trigger showing the virtual keyboard // or alternate input view (e.g. handwriting palette). Used in ChromeOS. virtual void OnShowVirtualKeyboardIfEnabled() = 0; + + // Called when an API call is used to explicitly show / hide the virtual + // keyboard. + virtual void OnVirtualKeyboardVisibilityChangedIfEnabled(bool should_show) {} }; } // namespace ui diff --git a/chromium/ui/base/ime/linux/composition_text_util_pango.cc b/chromium/ui/base/ime/linux/composition_text_util_pango.cc index e985cc7b227..5a202c7f4d5 100644 --- a/chromium/ui/base/ime/linux/composition_text_util_pango.cc +++ b/chromium/ui/base/ime/linux/composition_text_util_pango.cc @@ -9,8 +9,8 @@ #include <string> +#include "base/cxx17_backports.h" #include "base/i18n/char_iterator.h" -#include "base/numerics/ranges.h" #include "base/strings/utf_string_conversions.h" #include "ui/base/ime/composition_text.h" @@ -42,7 +42,7 @@ void ExtractCompositionTextFromGtkPreedit(const char* utf8_text, char16_offsets.push_back(length); size_t cursor_offset = - char16_offsets[base::ClampToRange(cursor_position, 0, char_length)]; + char16_offsets[base::clamp(cursor_position, 0, char_length)]; composition->selection = gfx::Range(cursor_offset); diff --git a/chromium/ui/base/ime/linux/composition_text_util_pango_unittest.cc b/chromium/ui/base/ime/linux/composition_text_util_pango_unittest.cc index 3d37d710feb..a4ecf865898 100644 --- a/chromium/ui/base/ime/linux/composition_text_util_pango_unittest.cc +++ b/chromium/ui/base/ime/linux/composition_text_util_pango_unittest.cc @@ -11,8 +11,8 @@ #include <string> #include <utility> +#include "base/cxx17_backports.h" #include "base/notreached.h" -#include "base/stl_util.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/ime/composition_text.h" diff --git a/chromium/ui/base/ime/linux/fake_input_method_context.cc b/chromium/ui/base/ime/linux/fake_input_method_context.cc index 36897c43744..a864a6e79ff 100644 --- a/chromium/ui/base/ime/linux/fake_input_method_context.cc +++ b/chromium/ui/base/ime/linux/fake_input_method_context.cc @@ -15,6 +15,10 @@ bool FakeInputMethodContext::DispatchKeyEvent( return false; } +bool FakeInputMethodContext::IsPeekKeyEvent(const ui::KeyEvent& key_event) { + return false; +} + void FakeInputMethodContext::Reset() { } diff --git a/chromium/ui/base/ime/linux/fake_input_method_context.h b/chromium/ui/base/ime/linux/fake_input_method_context.h index 4b9518a3275..b4ef26e1e13 100644 --- a/chromium/ui/base/ime/linux/fake_input_method_context.h +++ b/chromium/ui/base/ime/linux/fake_input_method_context.h @@ -19,6 +19,7 @@ class COMPONENT_EXPORT(UI_BASE_IME_LINUX) FakeInputMethodContext // Overriden from ui::LinuxInputMethodContext bool DispatchKeyEvent(const ui::KeyEvent& key_event) override; + bool IsPeekKeyEvent(const ui::KeyEvent& key_event) override; void Reset() override; void Focus() override; void Blur() override; diff --git a/chromium/ui/base/ime/linux/input_method_auralinux.cc b/chromium/ui/base/ime/linux/input_method_auralinux.cc index 3bff237d309..316216eb5e6 100644 --- a/chromium/ui/base/ime/linux/input_method_auralinux.cc +++ b/chromium/ui/base/ime/linux/input_method_auralinux.cc @@ -109,9 +109,23 @@ ui::EventDispatchDetails InputMethodAuraLinux::DispatchKeyEvent( } ime_filtered_key_event_.reset(); - // If no text input client, do nothing. - if (!GetTextInputClient()) + LinuxInputMethodContext* context = + text_input_type_ != TEXT_INPUT_TYPE_NONE && + text_input_type_ != TEXT_INPUT_TYPE_PASSWORD + ? context_.get() + : context_simple_.get(); + + // If no text input client, dispatch immediately. + if (!GetTextInputClient()) { + // For Wayland, wl_keyboard::key will be sent following the peek key event + // if the event is not consumed by IME, so peek key events should not be + // dispatched. crbug.com/1225747 + if (context->IsPeekKeyEvent(*event)) { + ime_filtered_key_event_ = std::move(*event); + return ui::EventDispatchDetails(); + } return DispatchKeyEventPostIME(event); + } if (IsEventFromVK(*event)) { // Faked key events that are sent from input.ime.sendKeyEvents. @@ -133,11 +147,6 @@ ui::EventDispatchDetails InputMethodAuraLinux::DispatchKeyEvent( suppress_non_key_input_until_ = base::TimeTicks::UnixEpoch(); composition_changed_ = false; result_text_.clear(); - LinuxInputMethodContext* context = - text_input_type_ != TEXT_INPUT_TYPE_NONE && - text_input_type_ != TEXT_INPUT_TYPE_PASSWORD - ? context_.get() - : context_simple_.get(); base::AutoReset<bool> flipper(&is_sync_mode_, true); filtered = context->DispatchKeyEvent(*event); } diff --git a/chromium/ui/base/ime/linux/input_method_auralinux_unittest.cc b/chromium/ui/base/ime/linux/input_method_auralinux_unittest.cc index 52d57c5d0b5..52e11d4bf2f 100644 --- a/chromium/ui/base/ime/linux/input_method_auralinux_unittest.cc +++ b/chromium/ui/base/ime/linux/input_method_auralinux_unittest.cc @@ -17,6 +17,7 @@ #include "ui/base/ime/linux/fake_input_method_context.h" #include "ui/base/ime/linux/linux_input_method_context_factory.h" #include "ui/events/event.h" +#include "ui/events/event_utils.h" #include "ui/events/keycodes/dom/dom_code.h" namespace ui { @@ -112,6 +113,19 @@ class LinuxInputMethodContextForTesting : public LinuxInputMethodContext { return eat_key_; } + bool IsPeekKeyEvent(const ui::KeyEvent& key_event) override { + const auto* properties = key_event.properties(); + // For the purposes of tests if kPropertyKeyboardImeFlag is not + // explicitly set assume the event is not a key event. + if (!properties) + return false; + auto it = properties->find(kPropertyKeyboardImeFlag); + if (it == properties->end()) { + return false; + } + return !(it->second[0] & kPropertyKeyboardImeIgnoredFlag); + } + void Reset() override {} void Focus() override { focused_ = true; } @@ -529,6 +543,24 @@ TEST_F(InputMethodAuraLinuxTest, DeadKeySimpleContextTest) { test_result_); } +// Wayland may send both a peek key event and a key event for key events not +// consumed by IME. In that case, the peek key should not be dispatched. +TEST_F(InputMethodAuraLinuxTest, MockWaylandEventsTest) { + KeyEvent peek_key(ET_KEY_PRESSED, VKEY_TAB, 0); + ui::Event::Properties properties; + properties[ui::kPropertyKeyboardImeFlag] = + std::vector<uint8_t>(ui::kPropertyKeyboardImeIgnoredFlag); + peek_key.SetProperties(properties); + input_method_auralinux_->DispatchKeyEvent(&peek_key); + // No expected action for peek key events. + test_result_->Verify(); + + KeyEvent key(ET_KEY_PRESSED, VKEY_TAB, 0); + input_method_auralinux_->DispatchKeyEvent(&key); + test_result_->ExpectAction("keydown:9"); + test_result_->Verify(); +} + TEST_F(InputMethodAuraLinuxTest, MultiCommitsTest) { context_->SetSyncMode(true); context_->SetEatKey(true); diff --git a/chromium/ui/base/ime/linux/linux_input_method_context.h b/chromium/ui/base/ime/linux/linux_input_method_context.h index 4fe9ac63b4d..77cefc38071 100644 --- a/chromium/ui/base/ime/linux/linux_input_method_context.h +++ b/chromium/ui/base/ime/linux/linux_input_method_context.h @@ -31,6 +31,9 @@ class COMPONENT_EXPORT(UI_BASE_IME_LINUX) LinuxInputMethodContext { // before dispatching a key event. virtual bool DispatchKeyEvent(const ui::KeyEvent& key_event) = 0; + // Returns whether the event is a peek key event. + virtual bool IsPeekKeyEvent(const ui::KeyEvent& key_event) = 0; + // Tells the system IME for the cursor rect which is relative to the // client window rect. virtual void SetCursorLocation(const gfx::Rect& rect) = 0; diff --git a/chromium/ui/base/ime/mock_input_method.cc b/chromium/ui/base/ime/mock_input_method.cc index fa2dec5bb7a..01c0c11f8fc 100644 --- a/chromium/ui/base/ime/mock_input_method.cc +++ b/chromium/ui/base/ime/mock_input_method.cc @@ -95,31 +95,20 @@ TextInputType MockInputMethod::GetTextInputType() const { return TEXT_INPUT_TYPE_NONE; } -TextInputMode MockInputMethod::GetTextInputMode() const { - return TEXT_INPUT_MODE_DEFAULT; -} - -int MockInputMethod::GetTextInputFlags() const { - return 0; -} - -bool MockInputMethod::CanComposeInline() const { - return true; -} - bool MockInputMethod::IsCandidatePopupOpen() const { return false; } -bool MockInputMethod::GetClientShouldDoLearning() { - return false; -} - void MockInputMethod::ShowVirtualKeyboardIfEnabled() { for (InputMethodObserver& observer : observer_list_) observer.OnShowVirtualKeyboardIfEnabled(); } +void MockInputMethod::SetVirtualKeyboardVisibilityIfEnabled(bool should_show) { + for (InputMethodObserver& observer : observer_list_) + observer.OnVirtualKeyboardVisibilityChangedIfEnabled(should_show); +} + void MockInputMethod::AddObserver(InputMethodObserver* observer) { observer_list_.AddObserver(observer); } diff --git a/chromium/ui/base/ime/mock_input_method.h b/chromium/ui/base/ime/mock_input_method.h index 0ed0143661e..b6e62a6eb2a 100644 --- a/chromium/ui/base/ime/mock_input_method.h +++ b/chromium/ui/base/ime/mock_input_method.h @@ -49,12 +49,9 @@ class COMPONENT_EXPORT(UI_BASE_IME) MockInputMethod : public InputMethod { void OnCaretBoundsChanged(const TextInputClient* client) override; void CancelComposition(const TextInputClient* client) override; TextInputType GetTextInputType() const override; - TextInputMode GetTextInputMode() const override; - int GetTextInputFlags() const override; - bool CanComposeInline() const override; bool IsCandidatePopupOpen() const override; - bool GetClientShouldDoLearning() override; void ShowVirtualKeyboardIfEnabled() override; + void SetVirtualKeyboardVisibilityIfEnabled(bool should_show) override; void AddObserver(InputMethodObserver* observer) override; void RemoveObserver(InputMethodObserver* observer) override; VirtualKeyboardController* GetVirtualKeyboardController() override; diff --git a/chromium/ui/base/ime/mojom/ime_mojom_traits_unittest.cc b/chromium/ui/base/ime/mojom/ime_mojom_traits_unittest.cc index d20b916614d..3b1f587c199 100644 --- a/chromium/ui/base/ime/mojom/ime_mojom_traits_unittest.cc +++ b/chromium/ui/base/ime/mojom/ime_mojom_traits_unittest.cc @@ -6,7 +6,7 @@ #include <utility> -#include "base/stl_util.h" +#include "base/cxx17_backports.h" #include "base/test/task_environment.h" #include "mojo/public/cpp/bindings/receiver_set.h" #include "mojo/public/cpp/bindings/remote.h" diff --git a/chromium/ui/base/ime/win/input_method_win_base.cc b/chromium/ui/base/ime/win/input_method_win_base.cc index bc80f95a689..de4e2f8fa17 100644 --- a/chromium/ui/base/ime/win/input_method_win_base.cc +++ b/chromium/ui/base/ime/win/input_method_win_base.cc @@ -229,7 +229,7 @@ ui::EventDispatchDetails InputMethodWinBase::DispatchKeyEvent( // If only 1 WM_CHAR per the key event, set it as the character of it. if (char_msgs.size() == 1 && !std::iswcntrl(static_cast<wint_t>(char_msgs[0].wParam))) - event->set_character(char16_t{char_msgs[0].wParam}); + event->set_character(static_cast<char16_t>(char_msgs[0].wParam)); return ProcessUnhandledKeyEvent(event, &char_msgs); } @@ -277,8 +277,8 @@ LRESULT InputMethodWinBase::OnChar(HWND window_handle, // We need to send character events to the focused text input client event if // its text input type is ui::TEXT_INPUT_TYPE_NONE. if (GetTextInputClient()) { - const char16_t kCarriageReturn = L'\r'; - const char16_t ch{wparam}; + const char16_t kCarriageReturn = u'\r'; + const char16_t ch = static_cast<char16_t>(wparam); // A mask to determine the previous key state from |lparam|. The value is 1 // if the key is down before the message is sent, or it is 0 if the key is // up. diff --git a/chromium/ui/base/ime/win/input_method_win_base.h b/chromium/ui/base/ime/win/input_method_win_base.h index 60306824f62..3fcdad110cb 100644 --- a/chromium/ui/base/ime/win/input_method_win_base.h +++ b/chromium/ui/base/ime/win/input_method_win_base.h @@ -7,8 +7,6 @@ #include <windows.h> -#include <string> - #include "base/compiler_specific.h" #include "base/component_export.h" #include "base/macros.h" diff --git a/chromium/ui/base/ime/win/input_method_win_imm32.cc b/chromium/ui/base/ime/win/input_method_win_imm32.cc index 07341babad1..2b489c33193 100644 --- a/chromium/ui/base/ime/win/input_method_win_imm32.cc +++ b/chromium/ui/base/ime/win/input_method_win_imm32.cc @@ -323,6 +323,11 @@ void InputMethodWinImm32::ConfirmCompositionText() { imm32_manager_.CleanupComposition(composing_window_handle_); } +TextInputMode InputMethodWinImm32::GetTextInputMode() const { + TextInputClient* client = GetTextInputClient(); + return client ? client->GetTextInputMode() : TEXT_INPUT_MODE_DEFAULT; +} + void InputMethodWinImm32::UpdateIMEState() { // Use switch here in case we are going to add more text input types. // We disable input method in password field. diff --git a/chromium/ui/base/ime/win/input_method_win_imm32.h b/chromium/ui/base/ime/win/input_method_win_imm32.h index cd2334758bd..f96b6f678a6 100644 --- a/chromium/ui/base/ime/win/input_method_win_imm32.h +++ b/chromium/ui/base/ime/win/input_method_win_imm32.h @@ -79,6 +79,10 @@ class COMPONENT_EXPORT(UI_BASE_IME_WIN) InputMethodWinImm32 void ConfirmCompositionText(); + // Gets the text input mode of the focused text input client. Returns + // ui::TEXT_INPUT_MODE_DEFAULT if there is no focused client. + TextInputMode GetTextInputMode() const; + // Windows IMM32 wrapper. // (See "ui/base/ime/win/ime_input.h" for its details.) ui::IMM32Manager imm32_manager_; diff --git a/chromium/ui/base/ime/win/input_method_win_tsf.h b/chromium/ui/base/ime/win/input_method_win_tsf.h index 34d9616ab08..e98575c9a45 100644 --- a/chromium/ui/base/ime/win/input_method_win_tsf.h +++ b/chromium/ui/base/ime/win/input_method_win_tsf.h @@ -7,8 +7,6 @@ #include <windows.h> -#include <string> - #include "base/component_export.h" #include "ui/base/ime/win/input_method_win_base.h" diff --git a/chromium/ui/base/ime/win/tsf_bridge.cc b/chromium/ui/base/ime/win/tsf_bridge.cc index a4d9ab04d1a..a4fa3df277f 100644 --- a/chromium/ui/base/ime/win/tsf_bridge.cc +++ b/chromium/ui/base/ime/win/tsf_bridge.cc @@ -6,10 +6,10 @@ #include <map> +#include "base/cxx17_backports.h" #include "base/logging.h" #include "base/memory/ref_counted.h" #include "base/no_destructor.h" -#include "base/stl_util.h" #include "base/task/current_thread.h" #include "base/threading/thread_local_storage.h" #include "base/trace_event/trace_event.h" diff --git a/chromium/ui/base/ime/win/tsf_text_store.cc b/chromium/ui/base/ime/win/tsf_text_store.cc index 5e152181451..3789cb0ac17 100644 --- a/chromium/ui/base/ime/win/tsf_text_store.cc +++ b/chromium/ui/base/ime/win/tsf_text_store.cc @@ -11,8 +11,8 @@ #include <algorithm> +#include "base/cxx17_backports.h" #include "base/logging.h" -#include "base/numerics/ranges.h" #include "base/trace_event/trace_event.h" #include "base/win/scoped_variant.h" #include "ui/base/ime/text_input_client.h" @@ -528,9 +528,8 @@ HRESULT TSFTextStore::QueryInsert(LONG acp_test_start, const LONG composition_start = static_cast<LONG>(composition_start_); const LONG buffer_size = static_cast<LONG>(string_buffer_document_.size()); *acp_result_start = - base::ClampToRange(acp_test_start, composition_start, buffer_size); - *acp_result_end = - base::ClampToRange(acp_test_end, composition_start, buffer_size); + base::clamp(acp_test_start, composition_start, buffer_size); + *acp_result_end = base::clamp(acp_test_end, composition_start, buffer_size); return S_OK; } @@ -1415,6 +1414,10 @@ void TSFTextStore::CommitTextAndEndCompositionIfAny(size_t old_size, // the new text if replacement text has already been inserted into Blink. if (new_text_inserted_ && (old_size > replace_text_range_.start()) && !replace_text_range_.is_empty()) { + // Delete text that has already been inserted into blink. + text_input_client_->ExtendSelectionAndDelete( + replace_text_range_.end() - replace_text_range_.start(), 0); + new_committed_string_offset = replace_text_range_.start(); new_committed_string_size = replace_text_size_; } diff --git a/chromium/ui/base/ime/win/tsf_text_store_unittest.cc b/chromium/ui/base/ime/win/tsf_text_store_unittest.cc index 77132609652..033f1fe5ce6 100644 --- a/chromium/ui/base/ime/win/tsf_text_store_unittest.cc +++ b/chromium/ui/base/ime/win/tsf_text_store_unittest.cc @@ -12,8 +12,8 @@ #include <vector> +#include "base/cxx17_backports.h" #include "base/memory/ref_counted.h" -#include "base/stl_util.h" #include "base/win/scoped_com_initializer.h" #include "base/win/scoped_variant.h" #include "build/build_config.h" @@ -4020,6 +4020,107 @@ TEST_F(TSFTextStoreTest, RegressionTest12) { EXPECT_EQ(S_OK, result); } +// regression tests for crbug.com/1225896. +// Some IMEs (e.g. voice typing panel) may remove text before active +// composition. We should delete text before inserting new text. +class RegressionTest13Callback : public TSFTextStoreTestCallback { + public: + explicit RegressionTest13Callback(TSFTextStore* text_store) + : TSFTextStoreTestCallback(text_store) {} + + RegressionTest13Callback(const RegressionTest13Callback&) = delete; + RegressionTest13Callback& operator=(const RegressionTest13Callback&) = delete; + + HRESULT LockGranted1(DWORD flags) { + SetTextTest(0, 0, L"text ", S_OK); + SetTextTest(4, 5, L"", S_OK); + SetTextTest(4, 4, L" delete that ", S_OK); + SetSelectionTest(17, 17, S_OK); + + text_spans()->clear(); + ImeTextSpan text_span; + text_span.start_offset = 4; + text_span.end_offset = 17; + text_span.underline_color = SK_ColorBLACK; + text_span.thickness = ImeTextSpan::Thickness::kThin; + text_span.background_color = SK_ColorTRANSPARENT; + text_spans()->push_back(text_span); + *edit_flag() = true; + *composition_start() = 4; + composition_range()->set_start(4); + composition_range()->set_end(17); + // text_store_->OnKeyTraceDown(65u, 1966081u); + *has_composition_range() = true; + + return S_OK; + } + + void SetCompositionText1(const ui::CompositionText& composition) { + EXPECT_EQ(u" delete that ", composition.text); + EXPECT_EQ(13u, composition.selection.start()); + EXPECT_EQ(13u, composition.selection.end()); + ASSERT_EQ(1u, composition.ime_text_spans.size()); + EXPECT_EQ(0u, composition.ime_text_spans[0].start_offset); + EXPECT_EQ(13u, composition.ime_text_spans[0].end_offset); + SetHasCompositionText(true); + SetTextRange(0, 17); + SetTextBuffer(u"text delete that "); + SetSelectionRange(17, 17); + } + + HRESULT LockGranted2(DWORD flags) { + GetTextTest(0, -1, L"text delete that ", 17); + SetTextTest(4, 17, L"", S_OK); + GetTextTest(0, -1, L"text", 4); + SetTextTest(0, 4, L"", S_OK); + GetTextTest(0, -1, L"", 0); + SetSelectionTest(0, 0, S_OK); + + text_spans()->clear(); + *edit_flag() = true; + *composition_start() = 3; + composition_range()->set_start(0); + composition_range()->set_end(0); + *has_composition_range() = false; + + return S_OK; + } + + HRESULT LockGranted3(DWORD flags) { + GetTextTest(0, -1, L"", 0); + + return S_OK; + } +}; + +TEST_F(TSFTextStoreTest, RegressionTest13) { + RegressionTest13Callback callback(text_store_.get()); + EXPECT_CALL(text_input_client_, ExtendSelectionAndDelete(_, _)).Times(1); + EXPECT_CALL(text_input_client_, InsertText(_, _)).Times(0); + EXPECT_CALL(text_input_client_, SetCompositionText(_)) + .WillOnce( + Invoke(&callback, &RegressionTest13Callback::SetCompositionText1)); + + EXPECT_CALL(*sink_, OnLockGranted(_)) + .WillOnce(Invoke(&callback, &RegressionTest13Callback::LockGranted1)) + .WillOnce(Invoke(&callback, &RegressionTest13Callback::LockGranted2)) + .WillOnce(Invoke(&callback, &RegressionTest13Callback::LockGranted3)); + + ON_CALL(text_input_client_, HasCompositionText()) + .WillByDefault( + Invoke(&callback, &TSFTextStoreTestCallback::HasCompositionText)); + + HRESULT result = kInvalidResult; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(S_OK, result); + result = kInvalidResult; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(S_OK, result); + result = kInvalidResult; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(S_OK, result); +} + // Test multiple |SetText| call in one edit session. class MultipleSetTextCallback : public TSFTextStoreTestCallback { public: diff --git a/chromium/ui/base/interaction/README.md b/chromium/ui/base/interaction/README.md index 26d96148063..797fafaab58 100644 --- a/chromium/ui/base/interaction/README.md +++ b/chromium/ui/base/interaction/README.md @@ -75,7 +75,7 @@ To create a class member that is a unique identifier, use: // also make the identifier protected or private if you wanted. class MyClass { public: - DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kClassMemberIdentifier); + DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(MyClass, kClassMemberIdentifier); }; // This goes in the .cc file: @@ -148,6 +148,21 @@ if (element->IsA<views::TrackedElementViews>()) { // Do something with the view that was shown here. } ``` + +Then, in your production code, assign an element identifier to the element you +want to track: +``` cpp +// Note: matching DECLARE macro must go in header file. +DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(MyView, kMyElementIdentifier); + +MyView::MyView() { + auto* const child_view = AddChildView(std::make_unique<ChildViewType>()); + + // This child view will now generate events with the given identifier. + // The context will be derived from the widget the parent view is added to. + child_view->SetProperty(views::kElementIdentifierKey, kMyElementIdentifier); +} +``` ## Defining and following user interaction sequences The `InteractionSequence` class provides a way to describe a sequence of @@ -172,9 +187,9 @@ dismissed (such as if a dialog or menu is closed). To create an interaction sequence, use a `InteractionSequence::Builder`. To add steps to the builder, use an `InteractionSequence::StepBuilder`, or call a -convenience method like `InitialElement()`. Here is an example that expects the -user to interact with a feature entry point and then displays a help bubble on -the resulting dialog: +convenience method like `WithInitialElement()`. Here is an example that expects +the user to interact with a feature entry point and then displays a help bubble +on the resulting dialog: ``` cpp initial_element = @@ -183,7 +198,7 @@ sequence_ = InteractionSequence::Builder() .SetCompletedCallback(base::BindOnce( &MyClass::OnSequenceComplete, base::Unretained(this))) - .AddStep(InteractionSequence::InitialElement(initial_element)) + .AddStep(InteractionSequence::WithInitialElement(initial_element)) .AddStep(InteractionSequence::StepBuilder() .SetElementID(initial_element->identifier()) .SetType(StepType::kActivated) @@ -215,16 +230,17 @@ whether the element must be visible at start or remain visible, default values will be assigned according to the type of step. All callbacks are optional. Instead of using `StepBuilder`, for the initial step you can call -`InteractionSequence::InitialElement()`. This creates a default **shown** step -for an element that is already visible; it expects the element to be visible -when `Start()` is called or the sequence will abort. You may pass optional -step start and end callbacks to `InitialElement()`; these are useful for -displaying an initial prompt to the user (in the case of a tutorial). +`InteractionSequence::WithInitialElement()`. This creates a default **shown** +step for an element that is already visible; it expects the element to be +visible when `Start()` is called or the sequence will abort. You may pass +optional step start and end callbacks to `WithInitialElement()`; these are +useful for displaying an initial prompt to the user (in the case of a +tutorial). There is an additional method on `StepBuilder`, `SetContext()`, but it is only used by helper methods and for testing. You should instead use -`Builder::SetContext()` or `InteractionSequence::InitialElement()`. There is -currently no support for cross-context sequences and setting conflicting +`Builder::SetContext()` or `InteractionSequence::WithInitialElement()`. There +is currently no support for cross-context sequences and setting conflicting contexts in a sequence is an error and will crash if DCHECK is enabled. ### Step callbacks @@ -250,8 +266,8 @@ input to the UI. In general, it will be pretty obvious how to construct your sequence, because you know the steps you need to perform in the UI to get where you want to go. However, keep the following in mind: -* Try to start the sequence with a step generated by `InitialElement()`, keyed - to a UI element you know will be visible when the sequence starts. +* Try to start the sequence with a step generated by `WithInitialElement()`, + keyed to a UI element you know will be visible when the sequence starts. * Do not assume the order in which elements will become visible when a surface is shown. * Do not assume that interacting with a button or menu item will bring up a diff --git a/chromium/ui/base/interaction/element_identifier.h b/chromium/ui/base/interaction/element_identifier.h index 3ff3d581266..b9f8b55ccf2 100644 --- a/chromium/ui/base/interaction/element_identifier.h +++ b/chromium/ui/base/interaction/element_identifier.h @@ -10,6 +10,7 @@ #include <ostream> #include "base/component_export.h" +#include "base/containers/contains.h" #include "ui/base/class_property.h" // Overview: diff --git a/chromium/ui/base/interaction/element_tracker.h b/chromium/ui/base/interaction/element_tracker.h index 9be36263248..08ce7a80f63 100644 --- a/chromium/ui/base/interaction/element_tracker.h +++ b/chromium/ui/base/interaction/element_tracker.h @@ -14,7 +14,6 @@ #include "base/gtest_prod_util.h" #include "base/no_destructor.h" #include "base/notreached.h" -#include "base/observer_list.h" #include "base/observer_list_types.h" #include "ui/base/interaction/element_identifier.h" diff --git a/chromium/ui/base/interaction/interaction_sequence.cc b/chromium/ui/base/interaction/interaction_sequence.cc index 1980a82c5f4..6b108b149ee 100644 --- a/chromium/ui/base/interaction/interaction_sequence.cc +++ b/chromium/ui/base/interaction/interaction_sequence.cc @@ -8,6 +8,7 @@ #include "base/logging.h" #include "base/memory/ptr_util.h" #include "base/memory/weak_ptr.h" +#include "base/run_loop.h" #include "base/scoped_observation.h" #include "ui/base/interaction/element_tracker.h" @@ -230,7 +231,7 @@ InteractionSequence::WithInitialElement(TrackedElement* element, InteractionSequence::~InteractionSequence() { // We can abort during a step callback, but we cannot destroy this object. if (started_) - Abort(); + Abort(AbortedReason::kSequenceDestroyed); } void InteractionSequence::Start() { @@ -238,12 +239,19 @@ void InteractionSequence::Start() { DCHECK(!started_); started_ = true; if (missing_first_element_) { - Abort(); + Abort(AbortedReason::kElementHiddenBeforeSequenceStart); return; } StageNextStep(); } +void InteractionSequence::RunSynchronouslyForTesting() { + base::RunLoop run_loop; + quit_run_loop_closure_for_testing_ = run_loop.QuitClosure(); + Start(); + run_loop.Run(); +} + void InteractionSequence::OnElementShown(TrackedElement* element) { DCHECK_EQ(StepType::kShown, next_step()->type); DCHECK(element->identifier() == next_step()->id); @@ -270,7 +278,7 @@ void InteractionSequence::OnElementHidden(TrackedElement* element) { // seen the triggering event for the next step, abort. if (current_step_->must_remain_visible.value() && !activated_during_callback_) { - Abort(); + Abort(AbortedReason::kElementHiddenDuringStep); return; } @@ -388,12 +396,15 @@ void InteractionSequence::DoStepTransition(TrackedElement* element) { // Last step end callback needs to be run before sequence completed. // Because the InteractionSequence could conceivably be destroyed during // one of these callbacks, make local copies of the callbacks and data. + base::OnceClosure quit_closure = + std::move(quit_run_loop_closure_for_testing_); CompletedCallback completed_callback = std::move(configuration_->completed_callback); std::unique_ptr<Step> last_step = std::move(current_step_); RunIfValid(std::move(last_step->end_callback), last_step->element, last_step->id, last_step->type); RunIfValid(std::move(completed_callback)); + RunIfValid(std::move(quit_closure)); return; } @@ -423,7 +434,7 @@ void InteractionSequence::StageNextStep() { // We don't want to call the step-end callback during Abort() since we // didn't technically start the step. current_step_->end_callback = StepCallback(); - Abort(); + Abort(AbortedReason::kElementNotVisibleAtStartOfStep); return; } @@ -462,9 +473,13 @@ void InteractionSequence::StageNextStep() { } } -void InteractionSequence::Abort() { +void InteractionSequence::Abort(AbortedReason reason) { DCHECK(started_); configuration_->steps.clear(); + // The current object could be destroyed during callbacks, so ensure we save + // a handle to the testing run loop (if there is one). + base::OnceClosure quit_closure = + std::move(quit_run_loop_closure_for_testing_); if (current_step_) { // Stop listening for events; we don't want additional callbacks during // teardown. @@ -481,14 +496,15 @@ void InteractionSequence::Abort() { RunIfValid(std::move(last_step->end_callback), element.get(), last_step->id, last_step->type); RunIfValid(std::move(aborted_callback), element.get(), last_step->id, - last_step->type); + last_step->type, reason); } else { // Aborted before any steps were run. Pass default values. // Note that if the sequence has already been aborted, this is a no-op, the // callback will already be null. RunIfValid(std::move(configuration_->aborted_callback), nullptr, - ElementIdentifier(), StepType::kShown); + ElementIdentifier(), StepType::kShown, reason); } + RunIfValid(std::move(quit_closure)); } bool InteractionSequence::AbortedDuringCallback() const { diff --git a/chromium/ui/base/interaction/interaction_sequence.h b/chromium/ui/base/interaction/interaction_sequence.h index 90d2a14b12c..83013abcff2 100644 --- a/chromium/ui/base/interaction/interaction_sequence.h +++ b/chromium/ui/base/interaction/interaction_sequence.h @@ -69,6 +69,18 @@ class COMPONENT_EXPORT(UI_BASE) InteractionSequence { kHidden }; + // Details why a sequence was aborted. + enum class AbortedReason { + // External code destructed this object before the sequence could complete. + kSequenceDestroyed, + // The starting element was hidden before the sequence started. + kElementHiddenBeforeSequenceStart, + // An element should have been visible at the start of a step but was not. + kElementNotVisibleAtStartOfStep, + // An element should have remained visible during a step but did not. + kElementHiddenDuringStep + }; + // Callback when a step happens in the sequence, or when a step ends. If // |element| is no longer available, it will be null. using StepCallback = base::OnceCallback<void(TrackedElement* element, @@ -79,9 +91,11 @@ class COMPONENT_EXPORT(UI_BASE) InteractionSequence { // sequence of steps, or if this object is deleted after the sequence starts. // The most recent event is described by the parameters; if the target element // is no longer available it will be null. - using AbortedCallback = base::OnceCallback<void(TrackedElement* last_element, - ElementIdentifier last_id, - StepType last_step_type)>; + using AbortedCallback = + base::OnceCallback<void(TrackedElement* last_element, + ElementIdentifier last_id, + StepType last_step_type, + AbortedReason aborted_reason)>; using CompletedCallback = base::OnceClosure; @@ -219,6 +233,15 @@ class COMPONENT_EXPORT(UI_BASE) InteractionSequence { // associated with that window). void Start(); + // Starts the sequence and does not return until the sequence either + // completes or aborts. Events on the current thread continue to be processed + // while the method is waiting, so this will not e.g. block the browser UI + // thread from handling inputs. + // + // This is a test-only method since production code applications should + // always run asynchronously. + void RunSynchronouslyForTesting(); + private: explicit InteractionSequence(std::unique_ptr<Configuration> configuration); @@ -246,7 +269,7 @@ class COMPONENT_EXPORT(UI_BASE) InteractionSequence { void StageNextStep(); // Cancels the sequence and cleans up. - void Abort(); + void Abort(AbortedReason reason); // Returns true (and does some sanity checking) if the sequence was aborted // during the most recent callback. @@ -267,6 +290,7 @@ class COMPONENT_EXPORT(UI_BASE) InteractionSequence { bool processing_step_ = false; std::unique_ptr<Step> current_step_; std::unique_ptr<Configuration> configuration_; + base::OnceClosure quit_run_loop_closure_for_testing_; // This is necessary because this object could be deleted during any callback, // and we don't want to risk a UAF if that happens. diff --git a/chromium/ui/base/interaction/interaction_sequence_unittest.cc b/chromium/ui/base/interaction/interaction_sequence_unittest.cc index 0d4feedf16c..4fb34ca840d 100644 --- a/chromium/ui/base/interaction/interaction_sequence_unittest.cc +++ b/chromium/ui/base/interaction/interaction_sequence_unittest.cc @@ -5,8 +5,12 @@ #include "ui/base/interaction/interaction_sequence.h" #include "base/callback_forward.h" +#include "base/location.h" +#include "base/task/post_task.h" +#include "base/task/thread_pool.h" #include "base/test/bind.h" #include "base/test/mock_callback.h" +#include "base/test/task_environment.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/interaction/element_identifier.h" @@ -124,7 +128,12 @@ TEST(InteractionSequenceTest, AbortIfWithInitialElementHiddenBeforeStart) { .Build()) .Build(); element.reset(); - EXPECT_CALL_IN_SCOPE(aborted, Run, tracker->Start()); + EXPECT_CALL_IN_SCOPE( + aborted, + Run(nullptr, ElementIdentifier(), InteractionSequence::StepType::kShown, + InteractionSequence::AbortedReason:: + kElementHiddenBeforeSequenceStart), + tracker->Start()); } TEST(InteractionSequenceTest, @@ -270,8 +279,10 @@ TEST(InteractionSequenceTest, TransitionFailsOnElementShownIfMustBeVisible) { .SetMustBeVisibleAtStart(true) .Build()) .Build(); - EXPECT_CALL(aborted, Run(nullptr, element2.identifier(), - InteractionSequence::StepType::kShown)) + EXPECT_CALL( + aborted, + Run(nullptr, element2.identifier(), InteractionSequence::StepType::kShown, + InteractionSequence::AbortedReason::kElementNotVisibleAtStartOfStep)) .Times(1); tracker->Start(); } @@ -379,8 +390,11 @@ TEST(InteractionSequenceTest, FailOnOtherElementAlreadyHiddenIfMustBeVisible) { .SetStartCallback(step.Get()) .Build()) .Build(); - EXPECT_CALL(aborted, Run(nullptr, element2.identifier(), - InteractionSequence::StepType::kHidden)) + EXPECT_CALL( + aborted, + Run(nullptr, element2.identifier(), + InteractionSequence::StepType::kHidden, + InteractionSequence::AbortedReason::kElementNotVisibleAtStartOfStep)) .Times(1); tracker->Start(); } @@ -631,10 +645,12 @@ TEST(InteractionSequenceTest, CancelMidSequenceWhenViewHidden) { EXPECT_CALLS_IN_SCOPE_2(step1_end, Run, step2_start, Run, element2.Activate()); - EXPECT_CALLS_IN_SCOPE_2(step2_end, Run, aborted, - Run(testing::_, element2.identifier(), - InteractionSequence::StepType::kActivated), - element2.Hide()); + EXPECT_CALLS_IN_SCOPE_2( + step2_end, Run, aborted, + Run(testing::_, element2.identifier(), + InteractionSequence::StepType::kActivated, + InteractionSequence::AbortedReason::kElementHiddenDuringStep), + element2.Hide()); } TEST(InteractionSequenceTest, DontCancelIfViewDoesNotNeedToRemainVisible) { @@ -1121,10 +1137,11 @@ TEST(InteractionSequenceTest, ElementHiddenDuringStepEndDuringAbort) { // First parameter will be null because during the delete the step end // callback will hide the element, which happens before the abort callback is // called. - EXPECT_CALL_IN_SCOPE(aborted, - Run(nullptr, element2.identifier(), - InteractionSequence::StepType::kShown), - tracker.reset()); + EXPECT_CALL_IN_SCOPE( + aborted, + Run(nullptr, element2.identifier(), InteractionSequence::StepType::kShown, + InteractionSequence::AbortedReason::kSequenceDestroyed), + tracker.reset()); } TEST(InteractionSequenceTest, SequenceDestroyedDuringInitialStepStartCallback) { @@ -1192,7 +1209,8 @@ TEST(InteractionSequenceTest, SequenceDestroyedDuringInitialStepAbort) { std::unique_ptr<InteractionSequence> tracker; auto callback = [&](TrackedElement*, ElementIdentifier, - InteractionSequence::StepType) { tracker.reset(); }; + InteractionSequence::StepType, + InteractionSequence::AbortedReason) { tracker.reset(); }; tracker = InteractionSequence::Builder() .SetAbortedCallback(base::BindLambdaForTesting(callback)) .SetCompletedCallback(completed.Get()) @@ -1298,7 +1316,8 @@ TEST(InteractionSequenceTest, SequenceDestroyedDuringMidSequenceAbort) { std::unique_ptr<InteractionSequence> tracker; auto callback = [&](TrackedElement*, ElementIdentifier, - InteractionSequence::StepType) { tracker.reset(); }; + InteractionSequence::StepType, + InteractionSequence::AbortedReason) { tracker.reset(); }; tracker = InteractionSequence::Builder() .SetAbortedCallback(base::BindLambdaForTesting(callback)) .SetCompletedCallback(completed.Get()) @@ -1399,4 +1418,121 @@ TEST(InteractionSequenceTest, SequenceDestroyedDuringCompleted) { EXPECT_FALSE(tracker); } +TEST(InteractionSequenceTest, + RunSynchronouslyForTesting_SequenceAbortsDuringStart) { + base::test::TaskEnvironment task_environment; + auto task_runner = base::ThreadPool::CreateSequencedTaskRunner({}); + UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted); + UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed); + TestElement element1(kTestIdentifier1, kTestContext1); + + std::unique_ptr<InteractionSequence> tracker; + tracker = InteractionSequence::Builder() + .SetAbortedCallback(aborted.Get()) + .SetCompletedCallback(completed.Get()) + .SetContext(kTestContext1) + .AddStep(InteractionSequence::StepBuilder() + .SetElementID(element1.identifier()) + .SetType(InteractionSequence::StepType::kShown) + .SetMustBeVisibleAtStart(true) + .Build()) + .Build(); + EXPECT_CALL_IN_SCOPE(aborted, Run, tracker->RunSynchronouslyForTesting()); +} + +TEST(InteractionSequenceTest, + RunSynchronouslyForTesting_SequenceCompletesDuringStart) { + base::test::TaskEnvironment task_environment; + auto task_runner = base::ThreadPool::CreateSequencedTaskRunner({}); + UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted); + UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed); + TestElement element1(kTestIdentifier1, kTestContext1); + element1.Show(); + + std::unique_ptr<InteractionSequence> tracker; + tracker = InteractionSequence::Builder() + .SetAbortedCallback(aborted.Get()) + .SetCompletedCallback(completed.Get()) + .SetContext(kTestContext1) + .AddStep(InteractionSequence::StepBuilder() + .SetElementID(element1.identifier()) + .SetType(InteractionSequence::StepType::kShown) + .SetMustBeVisibleAtStart(true) + .Build()) + .Build(); + EXPECT_CALL_IN_SCOPE(completed, Run, tracker->RunSynchronouslyForTesting()); +} + +TEST(InteractionSequenceTest, + RunSynchronouslyForTesting_SequenceAbortsDuringStep) { + base::test::SingleThreadTaskEnvironment task_environment; + UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted); + UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed); + TestElement element1(kTestIdentifier1, kTestContext1); + TestElement element2(kTestIdentifier2, kTestContext1); + element1.Show(); + + std::unique_ptr<InteractionSequence> tracker; + tracker = + InteractionSequence::Builder() + .SetAbortedCallback(aborted.Get()) + .SetCompletedCallback(completed.Get()) + .SetContext(kTestContext1) + .AddStep( + InteractionSequence::StepBuilder() + .SetElementID(element1.identifier()) + .SetType(InteractionSequence::StepType::kShown) + .SetMustRemainVisible(true) + .SetStartCallback(base::BindLambdaForTesting( + [&](TrackedElement* element, ElementIdentifier element_id, + InteractionSequence::StepType step_type) { + task_environment.GetMainThreadTaskRunner()->PostTask( + FROM_HERE, base::BindLambdaForTesting( + [&]() { element1.Hide(); })); + })) + .Build()) + .AddStep(InteractionSequence::StepBuilder() + .SetElementID(element2.identifier()) + .SetType(InteractionSequence::StepType::kShown) + .Build()) + .Build(); + EXPECT_CALL_IN_SCOPE(aborted, Run, tracker->RunSynchronouslyForTesting()); +} + +TEST(InteractionSequenceTest, + RunSynchronouslyForTesting_SequenceCompletesDuringStep) { + base::test::SingleThreadTaskEnvironment task_environment; + + UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted); + UNCALLED_MOCK_CALLBACK(InteractionSequence::CompletedCallback, completed); + TestElement element1(kTestIdentifier1, kTestContext1); + TestElement element2(kTestIdentifier2, kTestContext1); + element1.Show(); + + std::unique_ptr<InteractionSequence> tracker; + tracker = + InteractionSequence::Builder() + .SetAbortedCallback(aborted.Get()) + .SetCompletedCallback(completed.Get()) + .SetContext(kTestContext1) + .AddStep( + InteractionSequence::StepBuilder() + .SetElementID(element1.identifier()) + .SetType(InteractionSequence::StepType::kShown) + .SetStartCallback(base::BindLambdaForTesting( + [&](TrackedElement* element, ElementIdentifier element_id, + InteractionSequence::StepType step_type) { + task_environment.GetMainThreadTaskRunner()->PostTask( + FROM_HERE, base::BindLambdaForTesting( + [&]() { element2.Show(); })); + })) + .Build()) + .AddStep(InteractionSequence::StepBuilder() + .SetElementID(element2.identifier()) + .SetType(InteractionSequence::StepType::kShown) + .Build()) + .Build(); + EXPECT_CALL_IN_SCOPE(completed, Run, tracker->RunSynchronouslyForTesting()); +} + } // namespace ui diff --git a/chromium/ui/base/l10n/DIR_METADATA b/chromium/ui/base/l10n/DIR_METADATA index 34512e7607a..0d5c5e7c0c7 100644 --- a/chromium/ui/base/l10n/DIR_METADATA +++ b/chromium/ui/base/l10n/DIR_METADATA @@ -1,10 +1,10 @@ # Metadata information for this directory. # # For more information on DIR_METADATA files, see: -# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md +# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/README.md # # For the schema of this file, see Metadata message: -# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto +# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto monorail { component: "UI>Internationalization" diff --git a/chromium/ui/base/l10n/l10n_util.cc b/chromium/ui/base/l10n/l10n_util.cc index 897c5b80b3d..db2e205a297 100644 --- a/chromium/ui/base/l10n/l10n_util.cc +++ b/chromium/ui/base/l10n/l10n_util.cc @@ -13,6 +13,7 @@ #include "base/check_op.h" #include "base/command_line.h" #include "base/compiler_specific.h" +#include "base/cxx17_backports.h" #include "base/files/file_util.h" #include "base/i18n/file_util_icu.h" #include "base/i18n/message_formatter.h" @@ -23,7 +24,6 @@ #include "base/logging.h" #include "base/no_destructor.h" #include "base/notreached.h" -#include "base/stl_util.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" @@ -238,10 +238,11 @@ static const char* const kAcceptLanguageList[] = { // to have no duplicates. // // Note that this could have false positives at runtime on Android and iOS: -// - On Android, some locales aren't shipped (|android_apk_omitted_locales| in -// GN), and some locales files are dynamically shipped in app bundles -// (|android_bundle_only_locales|). Both of these lists are included in -// this variable. +// - On Android, locale files are dynamically shipped in app bundles which are +// only downloaded when needed - so the |locales| variable does not accurately +// reflect the UI strings that are currently available on disk. +// See the comment at the top of |LoadLocaleResources| in +// ui/base/resource/resource_bundle_android.cc for more information. // - On iOS, some locales aren't shipped (|ios_unsupported_locales|) as they are // not supported by the operating system. These locales are included in this // variable. diff --git a/chromium/ui/base/l10n/l10n_util_mac_unittest.mm b/chromium/ui/base/l10n/l10n_util_mac_unittest.mm index 9b2802415f5..738cd92b81c 100644 --- a/chromium/ui/base/l10n/l10n_util_mac_unittest.mm +++ b/chromium/ui/base/l10n/l10n_util_mac_unittest.mm @@ -5,7 +5,7 @@ #import <Foundation/Foundation.h> #include <stddef.h> -#include "base/stl_util.h" +#include "base/cxx17_backports.h" #include "base/strings/sys_string_conversions.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/platform_test.h" diff --git a/chromium/ui/base/l10n/l10n_util_unittest.cc b/chromium/ui/base/l10n/l10n_util_unittest.cc index 100e40fe785..172ac073ec8 100644 --- a/chromium/ui/base/l10n/l10n_util_unittest.cc +++ b/chromium/ui/base/l10n/l10n_util_unittest.cc @@ -8,13 +8,13 @@ #include <memory> #include "base/containers/flat_set.h" +#include "base/cxx17_backports.h" #include "base/environment.h" #include "base/files/file_util.h" #include "base/i18n/case_conversion.h" #include "base/i18n/rtl.h" #include "base/i18n/time_formatting.h" #include "base/path_service.h" -#include "base/stl_util.h" #include "base/strings/pattern.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" diff --git a/chromium/ui/base/layout.cc b/chromium/ui/base/layout.cc index d984586ceee..7cb0cb4a24b 100644 --- a/chromium/ui/base/layout.cc +++ b/chromium/ui/base/layout.cc @@ -22,44 +22,45 @@ namespace ui { namespace { -std::vector<ScaleFactor>* g_supported_scale_factors = nullptr; +std::vector<ResourceScaleFactor>* g_supported_resource_scale_factors = nullptr; } // namespace -void SetSupportedScaleFactors( - const std::vector<ui::ScaleFactor>& scale_factors) { - if (g_supported_scale_factors != nullptr) - delete g_supported_scale_factors; +void SetSupportedResourceScaleFactors( + const std::vector<ResourceScaleFactor>& scale_factors) { + if (g_supported_resource_scale_factors != nullptr) + delete g_supported_resource_scale_factors; - g_supported_scale_factors = new std::vector<ScaleFactor>(scale_factors); - std::sort(g_supported_scale_factors->begin(), - g_supported_scale_factors->end(), - [](ScaleFactor lhs, ScaleFactor rhs) { - return GetScaleForScaleFactor(lhs) < GetScaleForScaleFactor(rhs); - }); + g_supported_resource_scale_factors = + new std::vector<ResourceScaleFactor>(scale_factors); + std::sort(g_supported_resource_scale_factors->begin(), + g_supported_resource_scale_factors->end(), + [](ResourceScaleFactor lhs, ResourceScaleFactor rhs) { + return GetScaleForResourceScaleFactor(lhs) < + GetScaleForResourceScaleFactor(rhs); + }); // Set ImageSkia's supported scales. std::vector<float> scales; - for (std::vector<ScaleFactor>::const_iterator it = - g_supported_scale_factors->begin(); - it != g_supported_scale_factors->end(); ++it) { - scales.push_back(GetScaleForScaleFactor(*it)); + for (std::vector<ResourceScaleFactor>::const_iterator it = + g_supported_resource_scale_factors->begin(); + it != g_supported_resource_scale_factors->end(); ++it) { + scales.push_back(GetScaleForResourceScaleFactor(*it)); } gfx::ImageSkia::SetSupportedScales(scales); } -const std::vector<ScaleFactor>& GetSupportedScaleFactors() { - DCHECK(g_supported_scale_factors != nullptr); - return *g_supported_scale_factors; +const std::vector<ResourceScaleFactor>& GetSupportedResourceScaleFactors() { + DCHECK(g_supported_resource_scale_factors != nullptr); + return *g_supported_resource_scale_factors; } -ScaleFactor GetSupportedScaleFactor(float scale) { - DCHECK(g_supported_scale_factors != nullptr); - ScaleFactor closest_match = SCALE_FACTOR_100P; +ResourceScaleFactor GetSupportedResourceScaleFactor(float scale) { + DCHECK(g_supported_resource_scale_factors != nullptr); + ResourceScaleFactor closest_match = SCALE_FACTOR_100P; float smallest_diff = std::numeric_limits<float>::max(); - for (size_t i = 0; i < g_supported_scale_factors->size(); ++i) { - ScaleFactor scale_factor = (*g_supported_scale_factors)[i]; - float diff = std::abs(GetScaleForScaleFactor(scale_factor) - scale); + for (auto scale_factor : *g_supported_resource_scale_factors) { + float diff = std::abs(GetScaleForResourceScaleFactor(scale_factor) - scale); if (diff < smallest_diff) { closest_match = scale_factor; smallest_diff = diff; @@ -70,8 +71,8 @@ ScaleFactor GetSupportedScaleFactor(float scale) { } bool IsSupportedScale(float scale) { - for (auto scale_factor_idx : *g_supported_scale_factors) { - if (GetScaleForScaleFactor(scale_factor_idx) == scale) + for (auto scale_factor_idx : *g_supported_resource_scale_factors) { + if (GetScaleForResourceScaleFactor(scale_factor_idx) == scale) return true; } return false; @@ -79,24 +80,25 @@ bool IsSupportedScale(float scale) { namespace test { -ScopedSetSupportedScaleFactors::ScopedSetSupportedScaleFactors( - const std::vector<ui::ScaleFactor>& new_scale_factors) { - if (g_supported_scale_factors) { - original_scale_factors_ = - new std::vector<ScaleFactor>(*g_supported_scale_factors); +ScopedSetSupportedResourceScaleFactors::ScopedSetSupportedResourceScaleFactors( + const std::vector<ResourceScaleFactor>& new_scale_factors) { + if (g_supported_resource_scale_factors) { + original_scale_factors_ = new std::vector<ResourceScaleFactor>( + *g_supported_resource_scale_factors); } else { original_scale_factors_ = nullptr; } - SetSupportedScaleFactors(new_scale_factors); + SetSupportedResourceScaleFactors(new_scale_factors); } -ScopedSetSupportedScaleFactors::~ScopedSetSupportedScaleFactors() { +ScopedSetSupportedResourceScaleFactors:: + ~ScopedSetSupportedResourceScaleFactors() { if (original_scale_factors_) { - SetSupportedScaleFactors(*original_scale_factors_); + SetSupportedResourceScaleFactors(*original_scale_factors_); delete original_scale_factors_; } else { - delete g_supported_scale_factors; - g_supported_scale_factors = nullptr; + delete g_supported_resource_scale_factors; + g_supported_resource_scale_factors = nullptr; } } diff --git a/chromium/ui/base/layout.h b/chromium/ui/base/layout.h index e25289b3a48..f5c04cf6a5b 100644 --- a/chromium/ui/base/layout.h +++ b/chromium/ui/base/layout.h @@ -8,7 +8,6 @@ #include <vector> #include "base/component_export.h" -#include "base/macros.h" #include "build/build_config.h" #include "ui/base/resource/scale_factor.h" #include "ui/gfx/native_widget_types.h" @@ -19,40 +18,44 @@ namespace ui { // Use ScopedSetSupportedScaleFactors for unit tests as not to affect the // state of other tests. COMPONENT_EXPORT(UI_BASE) -void SetSupportedScaleFactors(const std::vector<ScaleFactor>& scale_factors); +void SetSupportedResourceScaleFactors( + const std::vector<ResourceScaleFactor>& scale_factors); // Returns a vector with the scale factors which are supported by this // platform, in ascending order. COMPONENT_EXPORT(UI_BASE) -const std::vector<ScaleFactor>& GetSupportedScaleFactors(); +const std::vector<ResourceScaleFactor>& GetSupportedResourceScaleFactors(); -// Returns the supported ScaleFactor which most closely matches |scale|. -// Converting from float to ScaleFactor is inefficient and should be done as -// little as possible. +// Returns the supported ResourceScaleFactor which most closely matches |scale|. +// Converting from float to ResourceScaleFactor is inefficient and should be +// done as little as possible. COMPONENT_EXPORT(UI_BASE) -ScaleFactor GetSupportedScaleFactor(float image_scale); +ResourceScaleFactor GetSupportedResourceScaleFactor(float image_scale); -// Returns the ScaleFactor used by |view|. +// Returns the ResourceScaleFactor used by |view|. COMPONENT_EXPORT(UI_BASE) float GetScaleFactorForNativeView(gfx::NativeView view); // Returns true if the scale passed in is the list of supported scales for // the platform. +// TODO(oshima): Deprecate this. COMPONENT_EXPORT(UI_BASE) bool IsSupportedScale(float scale); namespace test { -// Class which changes the value of GetSupportedScaleFactors() to +// Class which changes the value of GetSupportedResourceScaleFactors() to // |new_scale_factors| for the duration of its lifetime. -class COMPONENT_EXPORT(UI_BASE) ScopedSetSupportedScaleFactors { +class COMPONENT_EXPORT(UI_BASE) ScopedSetSupportedResourceScaleFactors { public: - explicit ScopedSetSupportedScaleFactors( - const std::vector<ui::ScaleFactor>& new_scale_factors); - ~ScopedSetSupportedScaleFactors(); + explicit ScopedSetSupportedResourceScaleFactors( + const std::vector<ResourceScaleFactor>& new_scale_factors); + ScopedSetSupportedResourceScaleFactors( + const ScopedSetSupportedResourceScaleFactors&) = delete; + ScopedSetSupportedResourceScaleFactors& operator=( + const ScopedSetSupportedResourceScaleFactors&) = delete; + ~ScopedSetSupportedResourceScaleFactors(); private: - std::vector<ui::ScaleFactor>* original_scale_factors_; - - DISALLOW_COPY_AND_ASSIGN(ScopedSetSupportedScaleFactors); + std::vector<ResourceScaleFactor>* original_scale_factors_; }; } // namespace test diff --git a/chromium/ui/base/layout_unittest.cc b/chromium/ui/base/layout_unittest.cc index 0b4cdf53e54..d38c9ea1a24 100644 --- a/chromium/ui/base/layout_unittest.cc +++ b/chromium/ui/base/layout_unittest.cc @@ -16,49 +16,39 @@ namespace ui { TEST(LayoutTest, GetScaleFactorFromScalePartlySupported) { - std::vector<ScaleFactor> supported_factors; + std::vector<ResourceScaleFactor> supported_factors; supported_factors.push_back(SCALE_FACTOR_100P); - supported_factors.push_back(SCALE_FACTOR_180P); - test::ScopedSetSupportedScaleFactors scoped_supported(supported_factors); - EXPECT_EQ(SCALE_FACTOR_100P, GetSupportedScaleFactor(0.1f)); - EXPECT_EQ(SCALE_FACTOR_100P, GetSupportedScaleFactor(0.9f)); - EXPECT_EQ(SCALE_FACTOR_100P, GetSupportedScaleFactor(1.0f)); - EXPECT_EQ(SCALE_FACTOR_100P, GetSupportedScaleFactor(1.39f)); - EXPECT_EQ(SCALE_FACTOR_180P, GetSupportedScaleFactor(1.41f)); - EXPECT_EQ(SCALE_FACTOR_180P, GetSupportedScaleFactor(1.8f)); - EXPECT_EQ(SCALE_FACTOR_180P, GetSupportedScaleFactor(2.0f)); - EXPECT_EQ(SCALE_FACTOR_180P, GetSupportedScaleFactor(999.0f)); + supported_factors.push_back(SCALE_FACTOR_200P); + test::ScopedSetSupportedResourceScaleFactors scoped_supported( + supported_factors); + EXPECT_EQ(SCALE_FACTOR_100P, GetSupportedResourceScaleFactor(0.1f)); + EXPECT_EQ(SCALE_FACTOR_100P, GetSupportedResourceScaleFactor(0.9f)); + EXPECT_EQ(SCALE_FACTOR_100P, GetSupportedResourceScaleFactor(1.0f)); + EXPECT_EQ(SCALE_FACTOR_100P, GetSupportedResourceScaleFactor(1.41f)); + EXPECT_EQ(SCALE_FACTOR_200P, GetSupportedResourceScaleFactor(1.6f)); + EXPECT_EQ(SCALE_FACTOR_200P, GetSupportedResourceScaleFactor(2.0f)); + EXPECT_EQ(SCALE_FACTOR_200P, GetSupportedResourceScaleFactor(999.0f)); } TEST(LayoutTest, GetScaleFactorFromScaleAllSupported) { - std::vector<ScaleFactor> supported_factors; + std::vector<ResourceScaleFactor> supported_factors; for (int factor = SCALE_FACTOR_100P; factor < NUM_SCALE_FACTORS; ++factor) { - supported_factors.push_back(static_cast<ScaleFactor>(factor)); + supported_factors.push_back(static_cast<ResourceScaleFactor>(factor)); } - test::ScopedSetSupportedScaleFactors scoped_supported(supported_factors); - - EXPECT_EQ(SCALE_FACTOR_100P, GetSupportedScaleFactor(0.1f)); - EXPECT_EQ(SCALE_FACTOR_100P, GetSupportedScaleFactor(0.9f)); - EXPECT_EQ(SCALE_FACTOR_100P, GetSupportedScaleFactor(1.0f)); - EXPECT_EQ(SCALE_FACTOR_125P, GetSupportedScaleFactor(1.19f)); - EXPECT_EQ(SCALE_FACTOR_125P, GetSupportedScaleFactor(1.21f)); - EXPECT_EQ(SCALE_FACTOR_133P, GetSupportedScaleFactor(1.291f)); - EXPECT_EQ(SCALE_FACTOR_133P, GetSupportedScaleFactor(1.3f)); - EXPECT_EQ(SCALE_FACTOR_140P, GetSupportedScaleFactor(1.4f)); - EXPECT_EQ(SCALE_FACTOR_150P, GetSupportedScaleFactor(1.59f)); - EXPECT_EQ(SCALE_FACTOR_150P, GetSupportedScaleFactor(1.61f)); - EXPECT_EQ(SCALE_FACTOR_180P, GetSupportedScaleFactor(1.7f)); - EXPECT_EQ(SCALE_FACTOR_180P, GetSupportedScaleFactor(1.89f)); - EXPECT_EQ(SCALE_FACTOR_200P, GetSupportedScaleFactor(1.91f)); - EXPECT_EQ(SCALE_FACTOR_200P, GetSupportedScaleFactor(2.0f)); - EXPECT_EQ(SCALE_FACTOR_200P, GetSupportedScaleFactor(2.1f)); - EXPECT_EQ(SCALE_FACTOR_250P, GetSupportedScaleFactor(2.3f)); - EXPECT_EQ(SCALE_FACTOR_250P, GetSupportedScaleFactor(2.5f)); - EXPECT_EQ(SCALE_FACTOR_250P, GetSupportedScaleFactor(2.6f)); - EXPECT_EQ(SCALE_FACTOR_300P, GetSupportedScaleFactor(2.9f)); - EXPECT_EQ(SCALE_FACTOR_300P, GetSupportedScaleFactor(3.0f)); - EXPECT_EQ(SCALE_FACTOR_300P, GetSupportedScaleFactor(3.1f)); - EXPECT_EQ(SCALE_FACTOR_300P, GetSupportedScaleFactor(999.0f)); + test::ScopedSetSupportedResourceScaleFactors scoped_supported( + supported_factors); + + EXPECT_EQ(SCALE_FACTOR_100P, GetSupportedResourceScaleFactor(0.1f)); + EXPECT_EQ(SCALE_FACTOR_100P, GetSupportedResourceScaleFactor(0.9f)); + EXPECT_EQ(SCALE_FACTOR_100P, GetSupportedResourceScaleFactor(1.0f)); + EXPECT_EQ(SCALE_FACTOR_100P, GetSupportedResourceScaleFactor(1.49f)); + EXPECT_EQ(SCALE_FACTOR_200P, GetSupportedResourceScaleFactor(1.51f)); + EXPECT_EQ(SCALE_FACTOR_200P, GetSupportedResourceScaleFactor(2.0f)); + EXPECT_EQ(SCALE_FACTOR_200P, GetSupportedResourceScaleFactor(2.49f)); + EXPECT_EQ(SCALE_FACTOR_300P, GetSupportedResourceScaleFactor(2.51f)); + EXPECT_EQ(SCALE_FACTOR_300P, GetSupportedResourceScaleFactor(3.0f)); + EXPECT_EQ(SCALE_FACTOR_300P, GetSupportedResourceScaleFactor(3.1f)); + EXPECT_EQ(SCALE_FACTOR_300P, GetSupportedResourceScaleFactor(999.0f)); } } // namespace ui diff --git a/chromium/ui/base/linux/linux_desktop.cc b/chromium/ui/base/linux/linux_desktop.cc index b5e3c80e0fe..6721512f9f1 100644 --- a/chromium/ui/base/linux/linux_desktop.cc +++ b/chromium/ui/base/linux/linux_desktop.cc @@ -4,41 +4,31 @@ #include "ui/base/linux/linux_desktop.h" +#include <vector> + #include "base/environment.h" #include "base/nix/xdg_util.h" #include "base/strings/string_piece.h" #include "base/values.h" +#include "ui/display/util/gpu_info_util.h" namespace ui { -namespace { - -// Must be in sync with the copy in //content/browser/gpu/gpu_internals_ui.cc. -base::Value NewDescriptionValuePair(base::StringPiece desc, - base::StringPiece value) { - base::Value dict(base::Value::Type::DICTIONARY); - dict.SetKey("description", base::Value(desc)); - dict.SetKey("value", base::Value(value)); - return dict; -} - -} // namespace - -base::Value GetDesktopEnvironmentInfoAsListValue() { - base::Value result(base::Value::Type::LIST); +std::vector<base::Value> GetDesktopEnvironmentInfo() { + std::vector<base::Value> result; auto env(base::Environment::Create()); std::string value; if (env->GetVar(base::nix::kXdgCurrentDesktopEnvVar, &value)) { - result.Append( - NewDescriptionValuePair(base::nix::kXdgCurrentDesktopEnvVar, value)); + result.push_back( + display::BuildGpuInfoEntry(base::nix::kXdgCurrentDesktopEnvVar, value)); } if (env->GetVar(base::nix::kXdgSessionTypeEnvVar, &value)) { - result.Append( - NewDescriptionValuePair(base::nix::kXdgSessionTypeEnvVar, value)); + result.push_back( + display::BuildGpuInfoEntry(base::nix::kXdgSessionTypeEnvVar, value)); } constexpr char kGDMSession[] = "GDMSESSION"; if (env->GetVar(kGDMSession, &value)) - result.Append(NewDescriptionValuePair(kGDMSession, value)); + result.push_back(display::BuildGpuInfoEntry(kGDMSession, value)); return result; } diff --git a/chromium/ui/base/linux/linux_desktop.h b/chromium/ui/base/linux/linux_desktop.h index 15688cac26f..c4dd042c2d6 100644 --- a/chromium/ui/base/linux/linux_desktop.h +++ b/chromium/ui/base/linux/linux_desktop.h @@ -5,16 +5,15 @@ #ifndef UI_BASE_LINUX_LINUX_DESKTOP_H_ #define UI_BASE_LINUX_LINUX_DESKTOP_H_ -#include "base/component_export.h" +#include <vector> -namespace base { -class Value; -} // namespace base +#include "base/component_export.h" +#include "base/values.h" namespace ui { -// Returns desktop environment info as list value. -COMPONENT_EXPORT(UI_BASE) base::Value GetDesktopEnvironmentInfoAsListValue(); +// Returns desktop environment info as list of values. +COMPONENT_EXPORT(UI_BASE) std::vector<base::Value> GetDesktopEnvironmentInfo(); } // namespace ui diff --git a/chromium/ui/base/linux/linux_ui_delegate.cc b/chromium/ui/base/linux/linux_ui_delegate.cc index 7b0aefd4311..795da1cb03c 100644 --- a/chromium/ui/base/linux/linux_ui_delegate.cc +++ b/chromium/ui/base/linux/linux_ui_delegate.cc @@ -27,7 +27,7 @@ LinuxUiDelegate::~LinuxUiDelegate() { instance_ = nullptr; } -bool LinuxUiDelegate::SetWidgetTransientFor( +bool LinuxUiDelegate::ExportWindowHandle( uint32_t parent_widget, base::OnceCallback<void(const std::string&)> callback) { // This function should not be called when using a platform that doesn't diff --git a/chromium/ui/base/linux/linux_ui_delegate.h b/chromium/ui/base/linux/linux_ui_delegate.h index 1ed7f90c366..c0a9df8022c 100644 --- a/chromium/ui/base/linux/linux_ui_delegate.h +++ b/chromium/ui/base/linux/linux_ui_delegate.h @@ -28,7 +28,7 @@ class COMPONENT_EXPORT(UI_BASE) LinuxUiDelegate { virtual LinuxUiBackend GetBackend() const = 0; // Only implemented on Wayland. - virtual bool SetWidgetTransientFor( + virtual bool ExportWindowHandle( uint32_t parent_widget, base::OnceCallback<void(const std::string&)> callback); diff --git a/chromium/ui/base/metadata/BUILD.gn b/chromium/ui/base/metadata/BUILD.gn new file mode 100644 index 00000000000..642cf00d8f7 --- /dev/null +++ b/chromium/ui/base/metadata/BUILD.gn @@ -0,0 +1,61 @@ +# Copyright 2021 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. + +import("//build/buildflag_header.gni") +import("//testing/test.gni") + +source_set("metadata_headers") { + sources = [ + "base_type_conversion.h", + "metadata_cache.h", + "metadata_header_macros.h", + "metadata_impl_macros.h", + "metadata_macros_internal.h", + "metadata_types.h", + "property_metadata.h", + ] + + public_deps = [ + "//base:base", + "//skia:skia", + "//ui/base", + "//ui/gfx", + "//url", + ] +} + +component("metadata") { + sources = [ + "base_type_conversion.cc", + "metadata_cache.cc", + "metadata_types.cc", + ] + + defines = [ "IS_UI_BASE_METADATA_IMPL" ] + + deps = [ + "//skia:skia", + "//ui/gfx:color_utils", + ] + + public_deps = [ ":metadata_headers" ] +} + +source_set("metadata_tests") { + testonly = true + + sources = [ + "base_type_conversion_unittest.cc", + "metadata_unittest.cc", + ] + + deps = [ + ":metadata", + "//base/test:test_support", + "//skia:skia", + "//testing/gtest", + "//ui/gfx:color_utils", + "//ui/gfx:test_support", + ] +} diff --git a/chromium/ui/base/metadata/base_type_conversion.cc b/chromium/ui/base/metadata/base_type_conversion.cc index 431cd6cc06f..ae3e4d24159 100644 --- a/chromium/ui/base/metadata/base_type_conversion.cc +++ b/chromium/ui/base/metadata/base_type_conversion.cc @@ -8,8 +8,8 @@ #include <string> #include "base/containers/fixed_flat_set.h" +#include "base/cxx17_backports.h" #include "base/no_destructor.h" -#include "base/numerics/ranges.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" #include "base/strings/string_tokenizer.h" @@ -539,11 +539,11 @@ TypeConverter<UNIQUE_TYPE_NAME(SkColor)>::ParseHslString( (values.size() == 3 || (base::StringToDouble(values[3], &a) && a >= 0.0 && a <= 1.0))) { SkScalar hsv[3]; - hsv[0] = base::ClampToRange(std::fmod(h, 360.0), 0.0, 360.0); - hsv[1] = s > 1.0 ? base::ClampToRange(s, 0.0, 100.0) / 100.0 - : base::ClampToRange(s, 0.0, 1.0); - hsv[2] = v > 1.0 ? base::ClampToRange(v, 0.0, 100.0) / 100.0 - : base::ClampToRange(v, 0.0, 1.0); + hsv[0] = base::clamp(std::fmod(h, 360.0), 0.0, 360.0); + hsv[1] = + s > 1.0 ? base::clamp(s, 0.0, 100.0) / 100.0 : base::clamp(s, 0.0, 1.0); + hsv[2] = + v > 1.0 ? base::clamp(v, 0.0, 100.0) / 100.0 : base::clamp(v, 0.0, 1.0); return absl::make_optional( SkHSVToColor(base::ClampRound<SkAlpha>(a * SK_AlphaOPAQUE), hsv)); } diff --git a/chromium/ui/base/metadata/base_type_conversion.h b/chromium/ui/base/metadata/base_type_conversion.h index 3262802f347..6fc68e11e23 100644 --- a/chromium/ui/base/metadata/base_type_conversion.h +++ b/chromium/ui/base/metadata/base_type_conversion.h @@ -62,8 +62,8 @@ using ArgType = T, const T&>::type; -COMPONENT_EXPORT(UI_BASE) extern const char kNoPrefix[]; -COMPONENT_EXPORT(UI_BASE) extern const char kSkColorPrefix[]; +COMPONENT_EXPORT(UI_BASE_METADATA) extern const char kNoPrefix[]; +COMPONENT_EXPORT(UI_BASE_METADATA) extern const char kSkColorPrefix[]; // General Type Conversion Template Functions --------------------------------- template <bool serializable, @@ -184,12 +184,12 @@ struct EnumStringsMap; // String Conversions --------------------------------------------------------- -COMPONENT_EXPORT(UI_BASE) +COMPONENT_EXPORT(UI_BASE_METADATA) std::u16string PointerToString(const void* pointer_val); #define DECLARE_CONVERSIONS(T) \ template <> \ - struct COMPONENT_EXPORT(UI_BASE) \ + struct COMPONENT_EXPORT(UI_BASE_METADATA) \ TypeConverter<T> : BaseTypeConverter<true> { \ static std::u16string ToString(ArgType<T> source_value); \ static absl::optional<T> FromString(const std::u16string& source_value); \ @@ -225,7 +225,8 @@ DECLARE_CONVERSIONS(url::Component) #undef DECLARE_CONVERSIONS template <> -struct COMPONENT_EXPORT(UI_BASE) TypeConverter<bool> : BaseTypeConverter<true> { +struct COMPONENT_EXPORT(UI_BASE_METADATA) TypeConverter<bool> + : BaseTypeConverter<true> { static std::u16string ToString(bool source_value); static absl::optional<bool> FromString(const std::u16string& source_value); static ValidStrings GetValidStrings(); @@ -233,7 +234,7 @@ struct COMPONENT_EXPORT(UI_BASE) TypeConverter<bool> : BaseTypeConverter<true> { // Special conversions for wrapper types -------------------------------------- -COMPONENT_EXPORT(UI_BASE) const std::u16string& GetNullOptStr(); +COMPONENT_EXPORT(UI_BASE_METADATA) const std::u16string& GetNullOptStr(); template <typename T> struct TypeConverter<absl::optional<T>> @@ -316,7 +317,8 @@ struct TypeConverter<std::vector<T>> MAKE_TYPE_UNIQUE(SkColor); template <> -struct COMPONENT_EXPORT(UI_BASE) TypeConverter<UNIQUE_TYPE_NAME(SkColor)> +struct COMPONENT_EXPORT(UI_BASE_METADATA) + TypeConverter<UNIQUE_TYPE_NAME(SkColor)> : BaseTypeConverter<true, false, kSkColorPrefix> { static std::u16string ToString(SkColor source_value); static absl::optional<SkColor> FromString(const std::u16string& source_value); @@ -370,9 +372,12 @@ using SkColorConverter = TypeConverter<UNIQUE_TYPE_NAME(SkColor)>; } // namespace metadata } // namespace ui -EXPORT_ENUM_CONVERTERS(gfx::HorizontalAlignment, COMPONENT_EXPORT(UI_BASE)) -EXPORT_ENUM_CONVERTERS(gfx::VerticalAlignment, COMPONENT_EXPORT(UI_BASE)) -EXPORT_ENUM_CONVERTERS(gfx::ElideBehavior, COMPONENT_EXPORT(UI_BASE)) -EXPORT_ENUM_CONVERTERS(ui::MenuSeparatorType, COMPONENT_EXPORT(UI_BASE)) +EXPORT_ENUM_CONVERTERS(gfx::HorizontalAlignment, + COMPONENT_EXPORT(UI_BASE_METADATA)) +EXPORT_ENUM_CONVERTERS(gfx::VerticalAlignment, + COMPONENT_EXPORT(UI_BASE_METADATA)) +EXPORT_ENUM_CONVERTERS(gfx::ElideBehavior, COMPONENT_EXPORT(UI_BASE_METADATA)) +EXPORT_ENUM_CONVERTERS(ui::MenuSeparatorType, + COMPONENT_EXPORT(UI_BASE_METADATA)) -#endif // UI_BASE_METDATA_BASE_TYPE_CONVERSION_H_
\ No newline at end of file +#endif // UI_BASE_METADATA_BASE_TYPE_CONVERSION_H_
\ No newline at end of file diff --git a/chromium/ui/base/metadata/base_type_conversion_unittest.cc b/chromium/ui/base/metadata/base_type_conversion_unittest.cc index d4d12888e49..287d0853a8b 100644 --- a/chromium/ui/base/metadata/base_type_conversion_unittest.cc +++ b/chromium/ui/base/metadata/base_type_conversion_unittest.cc @@ -11,7 +11,6 @@ #include "testing/platform_test.h" #include "ui/gfx/geometry/insets.h" #include "ui/gfx/geometry/rect.h" -//#include "ui/views/controls/button/button.h" using TypeConversionTest = PlatformTest; diff --git a/chromium/ui/base/metadata/metadata_cache.h b/chromium/ui/base/metadata/metadata_cache.h index 70ceaa6e606..24fe0c79cfc 100644 --- a/chromium/ui/base/metadata/metadata_cache.h +++ b/chromium/ui/base/metadata/metadata_cache.h @@ -23,7 +23,7 @@ class ClassMetaData; // MetaDataCache is implemented as a singleton. This also implies that each // instance of ClassMetaData registered into the cache represents one and only // one class type. -class COMPONENT_EXPORT(UI_BASE) MetaDataCache { +class COMPONENT_EXPORT(UI_BASE_METADATA) MetaDataCache { public: MetaDataCache(); @@ -46,7 +46,7 @@ class COMPONENT_EXPORT(UI_BASE) MetaDataCache { // // Registers the class metadata into the global cache. Will DCHECK if the // metadata for a class is already registered. -COMPONENT_EXPORT(UI_BASE) +COMPONENT_EXPORT(UI_BASE_METADATA) void RegisterClassInfo(std::unique_ptr<ClassMetaData> meta_data); // Help function for creating and registering the metadata container into the diff --git a/chromium/ui/base/metadata/metadata_impl_macros.h b/chromium/ui/base/metadata/metadata_impl_macros.h index 162b8940038..cc18806873f 100644 --- a/chromium/ui/base/metadata/metadata_impl_macros.h +++ b/chromium/ui/base/metadata/metadata_impl_macros.h @@ -6,7 +6,6 @@ #define UI_BASE_METADATA_METADATA_IMPL_MACROS_H_ #include <memory> -#include <string> #include <utility> #include "ui/base/metadata/metadata_cache.h" @@ -16,8 +15,10 @@ // Generate the implementation of the metadata accessors and internal class with // additional macros for defining the class' properties. -#define BEGIN_METADATA_BASE(class_name) \ - BEGIN_METADATA_INTERNAL( \ +#define BEGIN_METADATA_BASE(class_name) \ + METADATA_REINTERPRET_BASE_CLASS_INTERNAL( \ + class_name, METADATA_CLASS_NAME_INTERNAL(class_name)) \ + BEGIN_METADATA_INTERNAL( \ class_name, METADATA_CLASS_NAME_INTERNAL(class_name), class_name) #define _BEGIN_NESTED_METADATA(outer_class, class_name, parent_class_name) \ diff --git a/chromium/ui/base/metadata/metadata_macros_internal.h b/chromium/ui/base/metadata/metadata_macros_internal.h index a87c0879756..f642adb26bc 100644 --- a/chromium/ui/base/metadata/metadata_macros_internal.h +++ b/chromium/ui/base/metadata/metadata_macros_internal.h @@ -25,10 +25,22 @@ // A version of METADATA_ACCESSORS_INTERNAL for View, the root of the metadata // hierarchy; here GetClassName() is not declared as an override. +// +// This also introduces the ReinterpretToBaseClass(), which should exist at the +// root of the hierarchy on which the metadata will be attached. This will take +// the given void*, which should be some instance of a subclass of this base +// class, and return a properly typed class_name*. The body of this function +// does a reinterpret_cast<> of the void* to obtain a class_name*. Doing a +// direct static_cast<> from the void* may result in an incorrect +// pointer-to-instance, which may cause a crash. Using this intermediate step of +// reinterpret_cast<> provides more information to the compiler in order to +// obtain the proper result from the static_cast<>. See |AsClass(void* obj)| +// in property_metadata.h for additional info. #define METADATA_ACCESSORS_INTERNAL_BASE(class_name) \ static const char kViewClassName[]; \ virtual const char* GetClassName() const; \ static ui::metadata::ClassMetaData* MetaData(); \ + class_name* ReinterpretToBaseClass(void* obj); \ ui::metadata::ClassMetaData* GetClassMetaData() override; // Metadata Class ------------------------------------------------------------- @@ -48,7 +60,7 @@ \ private: \ friend class class_name; \ - virtual void BuildMetaData(); \ + void BuildMetaData(); \ static ui::metadata::ClassMetaData* meta_data_ ALLOW_UNUSED_TYPE; \ } @@ -100,6 +112,17 @@ void qualified_class_name::metadata_class_name::BuildMetaData() { \ SetTypeName(std::string(#qualified_class_name)); +// See the comment above on the METADATA_ACCESSORS_INTERNAL_BASE macro for more +// information. NOTE: This function should not be modified to access |this|, +// directly or indirectly. It should only do what is necessary to convert |obj| +// to a properly typed pointer. +#define METADATA_REINTERPRET_BASE_CLASS_INTERNAL(qualified_class_name, \ + metadata_class_name) \ + qualified_class_name* qualified_class_name::ReinterpretToBaseClass( \ + void* obj) { \ + return reinterpret_cast<qualified_class_name*>(obj); \ + } + #define METADATA_PARENT_CLASS_INTERNAL(parent_class_name) \ SetParentClassMetaData(parent_class_name::MetaData()); diff --git a/chromium/ui/base/metadata/metadata_types.h b/chromium/ui/base/metadata/metadata_types.h index db631ee031d..850993c1ee8 100644 --- a/chromium/ui/base/metadata/metadata_types.h +++ b/chromium/ui/base/metadata/metadata_types.h @@ -35,13 +35,13 @@ enum class PropertyFlags : uint32_t { kSerializable = 0x100, }; -COMPONENT_EXPORT(UI_BASE) +COMPONENT_EXPORT(UI_BASE_METADATA) extern PropertyFlags operator|(PropertyFlags op1, PropertyFlags op2); -COMPONENT_EXPORT(UI_BASE) +COMPONENT_EXPORT(UI_BASE_METADATA) extern PropertyFlags operator&(PropertyFlags op1, PropertyFlags op2); -COMPONENT_EXPORT(UI_BASE) +COMPONENT_EXPORT(UI_BASE_METADATA) extern PropertyFlags operator^(PropertyFlags op1, PropertyFlags op2); -COMPONENT_EXPORT(UI_BASE) extern bool operator!(PropertyFlags op); +COMPONENT_EXPORT(UI_BASE_METADATA) extern bool operator!(PropertyFlags op); // Used to identify the CallbackList<> within the PropertyChangedVectors map. using PropertyKey = const void*; @@ -53,7 +53,7 @@ using PropertyChangedCallback = PropertyChangedCallbacks::CallbackType; // metadata_header_macros.h). GetClassMetaData() is automatically overridden and // implemented in the relevant macros, so a class must merely have // MetaDataProvider somewhere in its ancestry. -class COMPONENT_EXPORT(UI_BASE) MetaDataProvider { +class COMPONENT_EXPORT(UI_BASE_METADATA) MetaDataProvider { public: MetaDataProvider(); virtual ~MetaDataProvider(); @@ -79,7 +79,7 @@ class MemberMetaDataBase; // macros in ui/base/metadata/metadata_impl_macros.h, a descendant of this // class is declared within the scope of the containing class. See information // about using the macros in the comment for the views::View class. -class COMPONENT_EXPORT(UI_BASE) ClassMetaData { +class COMPONENT_EXPORT(UI_BASE_METADATA) ClassMetaData { public: ClassMetaData(); ClassMetaData(std::string file, int line); @@ -111,7 +111,7 @@ class COMPONENT_EXPORT(UI_BASE) ClassMetaData { // for(views::MemberMetaDataBase* member : class_meta_data) { // OperateOn(member); // } - class COMPONENT_EXPORT(UI_BASE) ClassMemberIterator + class COMPONENT_EXPORT(UI_BASE_METADATA) ClassMemberIterator : public std::iterator<std::forward_iterator_tag, MemberMetaDataBase*> { public: ClassMemberIterator(const ClassMemberIterator& other); @@ -165,7 +165,7 @@ class COMPONENT_EXPORT(UI_BASE) ClassMetaData { // Abstract base class to represent meta data about class members. // Provides basic information (such as the name of the member), and templated // accessors to get/set the value of the member on an object. -class COMPONENT_EXPORT(UI_BASE) MemberMetaDataBase { +class COMPONENT_EXPORT(UI_BASE_METADATA) MemberMetaDataBase { public: using ValueStrings = std::vector<std::u16string>; MemberMetaDataBase(const std::string& member_name, diff --git a/chromium/ui/base/metadata/property_metadata.h b/chromium/ui/base/metadata/property_metadata.h index bca6d0f2999..b8158fe3f8e 100644 --- a/chromium/ui/base/metadata/property_metadata.h +++ b/chromium/ui/base/metadata/property_metadata.h @@ -51,6 +51,17 @@ struct ClassPropertyMetaDataTypeHelper<const ui::ClassProperty<TKValue_>* const, } }; +// Works around static casting issues related to the void*. See the comment on +// the METADATA_ACCESSORS_INTERNAL_BASE macro for more information about why +// this is necessary. NOTE: The reinterpret_cast<> here is merely to bring +// ReinterpretToBaseClass() into scope and make it callable. The body of that +// function does not access |this|, so this call is safe. +template <typename TClass> +TClass* AsClass(void* obj) { + return static_cast<TClass*>( + reinterpret_cast<TClass*>(obj)->ReinterpretToBaseClass(obj)); +} + } // namespace internal // Represents meta data for a specific read-only property member of class @@ -74,7 +85,7 @@ class ObjectPropertyReadOnlyMetaData : public ui::metadata::MemberMetaDataBase { std::u16string GetValueAsString(void* obj) const override { if (!kTypeIsSerializable && !kTypeIsReadOnly) return std::u16string(); - return TConverter::ToString((static_cast<TClass*>(obj)->*Get)()); + return TConverter::ToString((internal::AsClass<TClass>(obj)->*Get)()); } ui::metadata::PropertyFlags GetPropertyFlags() const override { @@ -121,7 +132,7 @@ class ObjectPropertyMetaData if (!kTypeIsSerializable || kTypeIsReadOnly) return; if (absl::optional<TValue> result = TConverter::FromString(new_value)) { - (static_cast<TClass*>(obj)->*Set)(std::move(result.value())); + (internal::AsClass<TClass>(obj)->*Set)(std::move(result.value())); } } @@ -167,7 +178,7 @@ class ClassPropertyMetaData : public ui::metadata::MemberMetaDataBase { // |TKValue| == |TValue|, dereferences the pointer. std::u16string GetValueAsString(void* obj) const override { typename TypeHelper::TKValue value = - static_cast<TClass*>(obj)->GetProperty(key_); + internal::AsClass<TClass>(obj)->GetProperty(key_); if (std::is_pointer<typename TypeHelper::TKValue>::value && !value) { return u"(not assigned)"; } else { @@ -181,7 +192,7 @@ class ClassPropertyMetaData : public ui::metadata::MemberMetaDataBase { void SetValueAsString(void* obj, const std::u16string& new_value) override { absl::optional<TValue> value = TConverter::FromString(new_value); if (value) - static_cast<TClass*>(obj)->SetProperty(key_, *value); + internal::AsClass<TClass>(obj)->SetProperty(key_, *value); } ui::metadata::PropertyFlags GetPropertyFlags() const override { diff --git a/chromium/ui/base/models/dialog_model.h b/chromium/ui/base/models/dialog_model.h index 4d870a45dfe..c2a6c90e811 100644 --- a/chromium/ui/base/models/dialog_model.h +++ b/chromium/ui/base/models/dialog_model.h @@ -10,7 +10,6 @@ #include "base/callback.h" #include "base/component_export.h" -#include "base/containers/flat_map.h" #include "base/types/pass_key.h" #include "ui/base/models/dialog_model_field.h" #include "ui/base/models/dialog_model_host.h" @@ -78,7 +77,7 @@ class COMPONENT_EXPORT(UI_BASE) DialogModelDelegate { // std::make_unique<views::BubbleDialogModelHost>(std::move(dialog_model)); // bubble->SetAnchorView(anchor_view); // views::Widget* const widget = -// views::BubbleDialogDelegateView::CreateBubble(bubble.release()); +// views::BubbleDialogDelegate::CreateBubble(std::move(bubble)); // widget->Show(); class COMPONENT_EXPORT(UI_BASE) DialogModel final { public: @@ -122,6 +121,11 @@ class COMPONENT_EXPORT(UI_BASE) DialogModel final { return *this; } + Builder& SetInternalName(std::string internal_name) { + model_->internal_name_ = std::move(internal_name); + return *this; + } + Builder& SetTitle(std::u16string title) { model_->title_ = std::move(title); return *this; @@ -289,6 +293,10 @@ class COMPONENT_EXPORT(UI_BASE) DialogModel final { return override_show_close_button_; } + const std::string& internal_name(base::PassKey<DialogModelHost>) const { + return internal_name_; + } + const std::u16string& title(base::PassKey<DialogModelHost>) const { return title_; } @@ -340,6 +348,7 @@ class COMPONENT_EXPORT(UI_BASE) DialogModel final { absl::optional<bool> override_show_close_button_; bool close_on_deactivate_ = true; + std::string internal_name_; std::u16string title_; ImageModel icon_; @@ -360,4 +369,4 @@ class COMPONENT_EXPORT(UI_BASE) DialogModel final { } // namespace ui -#endif // UI_BASE_MODELS_DIALOG_MODEL_H_
\ No newline at end of file +#endif // UI_BASE_MODELS_DIALOG_MODEL_H_ diff --git a/chromium/ui/base/models/image_model.cc b/chromium/ui/base/models/image_model.cc index 1ccf83f1011..392c305e2fb 100644 --- a/chromium/ui/base/models/image_model.cc +++ b/chromium/ui/base/models/image_model.cc @@ -4,6 +4,7 @@ #include <tuple> +#include "base/callback.h" #include "ui/base/models/image_model.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/vector_icon_utils.h" @@ -52,14 +53,6 @@ bool VectorIconModel::operator!=(const VectorIconModel& other) const { ImageModel::ImageModel() = default; -ImageModel::ImageModel(const VectorIconModel& vector_icon_model) - : icon_(vector_icon_model) {} - -ImageModel::ImageModel(const gfx::Image& image) : icon_(image) {} - -ImageModel::ImageModel(const gfx::ImageSkia& image_skia) - : ImageModel(gfx::Image(image_skia)) {} - ImageModel::~ImageModel() = default; ImageModel::ImageModel(const ImageModel&) = default; @@ -107,8 +100,14 @@ ImageModel ImageModel::FromResourceId(int resource_id) { ResourceBundle::GetSharedInstance().GetImageNamed(resource_id)); } +// static +ImageModel ImageModel::FromImageGenerator(ImageGenerator generator, + gfx::Size size) { + return ImageModel(ImageGeneratorAndSize(generator, size)); +} + bool ImageModel::IsEmpty() const { - return !IsVectorIcon() && !IsImage(); + return !IsVectorIcon() && !IsImage() && !IsImageGenerator(); } bool ImageModel::IsVectorIcon() const { @@ -121,12 +120,20 @@ bool ImageModel::IsImage() const { !absl::get<gfx::Image>(icon_).IsEmpty(); } +bool ImageModel::IsImageGenerator() const { + return absl::holds_alternative<ImageGeneratorAndSize>(icon_) && + !absl::get<ImageGeneratorAndSize>(icon_).size.IsEmpty(); +} + gfx::Size ImageModel::Size() const { if (IsVectorIcon()) { const int icon_size = GetVectorIcon().icon_size_; return gfx::Size(icon_size, icon_size); } - return IsImage() ? GetImage().Size() : gfx::Size(); + if (IsImage()) + return GetImage().Size(); + return IsImageGenerator() ? absl::get<ImageGeneratorAndSize>(icon_).size + : gfx::Size(); } VectorIconModel ImageModel::GetVectorIcon() const { @@ -139,6 +146,11 @@ gfx::Image ImageModel::GetImage() const { return absl::get<gfx::Image>(icon_); } +ImageModel::ImageGenerator ImageModel::GetImageGenerator() const { + DCHECK(IsImageGenerator()); + return absl::get<ImageGeneratorAndSize>(icon_).generator; +} + bool ImageModel::operator==(const ImageModel& other) const { return icon_ == other.icon_; } @@ -147,4 +159,33 @@ bool ImageModel::operator!=(const ImageModel& other) const { return !(*this == other); } +ImageModel::ImageGeneratorAndSize::ImageGeneratorAndSize( + ImageGenerator generator, + gfx::Size size) + : generator(std::move(generator)), size(std::move(size)) {} + +ImageModel::ImageGeneratorAndSize::ImageGeneratorAndSize( + const ImageGeneratorAndSize&) = default; + +ImageModel::ImageGeneratorAndSize& ImageModel::ImageGeneratorAndSize::operator=( + const ImageGeneratorAndSize&) = default; + +ImageModel::ImageGeneratorAndSize::~ImageGeneratorAndSize() = default; + +bool ImageModel::ImageGeneratorAndSize::operator==( + const ImageGeneratorAndSize& other) const { + return std::tie(generator, size) == std::tie(other.generator, other.size); +} + +ImageModel::ImageModel(const VectorIconModel& vector_icon_model) + : icon_(vector_icon_model) {} + +ImageModel::ImageModel(const gfx::Image& image) : icon_(image) {} + +ImageModel::ImageModel(const gfx::ImageSkia& image_skia) + : ImageModel(gfx::Image(image_skia)) {} + +ImageModel::ImageModel(ImageGeneratorAndSize image_generator) + : icon_(std::move(image_generator)) {} + } // namespace ui diff --git a/chromium/ui/base/models/image_model.h b/chromium/ui/base/models/image_model.h index af0297a2187..9df2f9246a7 100644 --- a/chromium/ui/base/models/image_model.h +++ b/chromium/ui/base/models/image_model.h @@ -19,6 +19,8 @@ struct VectorIcon; namespace ui { +class NativeTheme; + // The following classes encapsulate the various ways that a model may provide // or otherwise specify an icon or image. Most notably, these are used by the // MenuModel and SimpleMenuModel for building actual menus. @@ -76,6 +78,9 @@ class COMPONENT_EXPORT(UI_BASE) VectorIconModel { class COMPONENT_EXPORT(UI_BASE) ImageModel { public: + using ImageGenerator = + base::RepeatingCallback<gfx::ImageSkia(const ui::NativeTheme*)>; + ImageModel(); ImageModel(const ImageModel&); ImageModel& operator=(const ImageModel&); @@ -94,25 +99,45 @@ class COMPONENT_EXPORT(UI_BASE) ImageModel { static ImageModel FromImage(const gfx::Image& image); static ImageModel FromImageSkia(const gfx::ImageSkia& image_skia); static ImageModel FromResourceId(int resource_id); + // `size` must be the size of the image the `generator` returns. + // NOTE: If this proves onerous, we could allow autodetection, at the cost of + // requiring `generator` to be runnable with a null NativeTheme*. + static ImageModel FromImageGenerator(ImageGenerator generator, + gfx::Size size); bool IsEmpty() const; bool IsVectorIcon() const; bool IsImage() const; + bool IsImageGenerator() const; gfx::Size Size() const; // Only valid if IsVectorIcon() or IsImage() return true, respectively. VectorIconModel GetVectorIcon() const; gfx::Image GetImage() const; + ImageGenerator GetImageGenerator() const; - // Checks if both model yield equal images. + // Checks if both models yield equal images. bool operator==(const ImageModel& other) const; bool operator!=(const ImageModel& other) const; private: - ImageModel(const gfx::Image& image); - ImageModel(const gfx::ImageSkia& image_skia); - ImageModel(const VectorIconModel& vector_icon_model); + struct ImageGeneratorAndSize { + ImageGeneratorAndSize(ImageGenerator generator, gfx::Size size); + ImageGeneratorAndSize(const ImageGeneratorAndSize&); + ImageGeneratorAndSize& operator=(const ImageGeneratorAndSize&); + ~ImageGeneratorAndSize(); + + bool operator==(const ImageGeneratorAndSize& other) const; + + ImageGenerator generator; + gfx::Size size; + }; + + explicit ImageModel(const VectorIconModel& vector_icon_model); + explicit ImageModel(const gfx::Image& image); + explicit ImageModel(const gfx::ImageSkia& image_skia); + explicit ImageModel(ImageGeneratorAndSize image_generator); - absl::variant<VectorIconModel, gfx::Image> icon_; + absl::variant<VectorIconModel, gfx::Image, ImageGeneratorAndSize> icon_; }; } // namespace ui diff --git a/chromium/ui/base/models/image_model_unittest.cc b/chromium/ui/base/models/image_model_unittest.cc index ea116c55609..7b5da6d15b1 100644 --- a/chromium/ui/base/models/image_model_unittest.cc +++ b/chromium/ui/base/models/image_model_unittest.cc @@ -65,12 +65,30 @@ TEST(ImageModelTest, CheckForImage) { EXPECT_TRUE(image_model.IsImage()); } +TEST(ImageModelTest, CheckForImageGenerator) { + ImageModel image_model = ImageModel::FromImageGenerator( + base::BindRepeating([](const ui::NativeTheme*) { + return gfx::test::CreateImage(16, 16).AsImageSkia(); + }), + gfx::Size(16, 16)); + + EXPECT_FALSE(image_model.IsEmpty()); + EXPECT_TRUE(image_model.IsImageGenerator()); +} + TEST(ImageModelTest, Size) { EXPECT_EQ(gfx::Size(), ImageModel().Size()); EXPECT_EQ(gfx::Size(16, 16), ImageModel::FromVectorIcon(GetCircleVectorIcon(), -1, 16).Size()); EXPECT_EQ(gfx::Size(16, 16), ImageModel::FromImage(gfx::test::CreateImage(16, 16)).Size()); + EXPECT_EQ(gfx::Size(16, 16), + ImageModel::FromImageGenerator( + base::BindRepeating([](const ui::NativeTheme*) { + return gfx::test::CreateImage(16, 16).AsImageSkia(); + }), + gfx::Size(16, 16)) + .Size()); } TEST(ImageModelTest, CheckAssignVectorIcon) { @@ -93,13 +111,11 @@ TEST(ImageModelTest, CheckAssignImage) { EXPECT_TRUE(image_model_dest.IsEmpty()); EXPECT_FALSE(image_model_src.IsEmpty()); EXPECT_TRUE(image_model_src.IsImage()); - EXPECT_FALSE(image_model_src.IsVectorIcon()); image_model_dest = image_model_src; EXPECT_FALSE(image_model_dest.IsEmpty()); EXPECT_TRUE(image_model_dest.IsImage()); - EXPECT_FALSE(image_model_dest.IsVectorIcon()); image_model_src = ImageModel::FromVectorIcon(GetCircleVectorIcon(), -1, 16); @@ -108,7 +124,18 @@ TEST(ImageModelTest, CheckAssignImage) { image_model_dest = image_model_src; EXPECT_TRUE(image_model_dest.IsVectorIcon()); - EXPECT_FALSE(image_model_dest.IsImage()); + + image_model_src = ImageModel::FromImageGenerator( + base::BindRepeating([](const ui::NativeTheme*) { + return gfx::test::CreateImage(16, 16).AsImageSkia(); + }), + gfx::Size(16, 16)); + + EXPECT_TRUE(image_model_src.IsImageGenerator()); + + image_model_dest = image_model_src; + + EXPECT_TRUE(image_model_dest.IsImageGenerator()); } TEST(ImageModelTest, CheckEqual) { @@ -155,6 +182,26 @@ TEST(ImageModelTest, CheckEqual) { image_model_dest = ImageModel::FromVectorIcon(GetCircleVectorIcon(), SK_ColorMAGENTA, 2); EXPECT_NE(image_model_src, image_model_dest); + + auto generator = base::BindRepeating([](const ui::NativeTheme*) { + return gfx::test::CreateImage(16, 16).AsImageSkia(); + }); + image_model_src = + ImageModel::FromImageGenerator(generator, gfx::Size(16, 16)); + EXPECT_NE(image_model_src, image_model_dest); + image_model_dest = + ImageModel::FromImageGenerator(generator, gfx::Size(16, 16)); + EXPECT_EQ(image_model_src, image_model_dest); + image_model_dest = ImageModel::FromImageGenerator(generator, gfx::Size(8, 8)); + EXPECT_NE(image_model_src, image_model_dest); + image_model_dest = ImageModel::FromImageGenerator( + base::BindRepeating([](const ui::NativeTheme*) { + return gfx::test::CreateImage(8, 8).AsImageSkia(); + }), + gfx::Size(16, 16)); + EXPECT_NE(image_model_src, image_model_dest); + image_model_src = image_model_dest; + EXPECT_EQ(image_model_src, image_model_dest); } } // namespace ui diff --git a/chromium/ui/base/models/menu_model.cc b/chromium/ui/base/models/menu_model.cc index b2621d4d882..765db4a01bf 100644 --- a/chromium/ui/base/models/menu_model.cc +++ b/chromium/ui/base/models/menu_model.cc @@ -76,6 +76,10 @@ bool MenuModel::MayHaveMnemonicsAt(int index) const { return true; } +std::u16string MenuModel::GetAccessibleNameAt(int index) const { + return std::u16string(); +} + const gfx::FontList* MenuModel::GetLabelFontListAt(int index) const { return nullptr; } diff --git a/chromium/ui/base/models/menu_model.h b/chromium/ui/base/models/menu_model.h index 279be87a33e..f095bfc3011 100644 --- a/chromium/ui/base/models/menu_model.h +++ b/chromium/ui/base/models/menu_model.h @@ -90,6 +90,9 @@ class COMPONENT_EXPORT(UI_BASE) MenuModel // in which case it should always be treated as plain text. virtual bool MayHaveMnemonicsAt(int index) const; + // Returns the accessible name for the menu item at the specified index. + virtual std::u16string GetAccessibleNameAt(int index) const; + // Returns the font list used for the label at the specified index. // If NULL, then the default font list should be used. virtual const gfx::FontList* GetLabelFontListAt(int index) const; diff --git a/chromium/ui/base/models/menu_model_delegate.h b/chromium/ui/base/models/menu_model_delegate.h index 19e1ce18a70..29eb15060a5 100644 --- a/chromium/ui/base/models/menu_model_delegate.h +++ b/chromium/ui/base/models/menu_model_delegate.h @@ -9,8 +9,10 @@ namespace ui { class MenuModelDelegate { public: - // Invoked when an icon has been loaded from history. - virtual void OnIconChanged(int index) = 0; + // Invoked when an icon has been loaded from history. The |command_id| + // may be part of a submenu, which is why we use command id here rather + // than index. + virtual void OnIconChanged(int command_id) = 0; // Invoked after items in |MenuModel| have been removed and/or added, // delegate should assume the entire contents of the model has changed. @@ -21,7 +23,7 @@ class MenuModelDelegate { virtual void OnMenuClearingDelegate() {} protected: - virtual ~MenuModelDelegate() {} + virtual ~MenuModelDelegate() = default; }; } // namespace ui diff --git a/chromium/ui/base/models/simple_menu_model.cc b/chromium/ui/base/models/simple_menu_model.cc index 976a45c49c8..1bfe8fb9067 100644 --- a/chromium/ui/base/models/simple_menu_model.cc +++ b/chromium/ui/base/models/simple_menu_model.cc @@ -74,8 +74,7 @@ bool SimpleMenuModel::Delegate::GetAcceleratorForCommandId( SimpleMenuModel::SimpleMenuModel(Delegate* delegate) : delegate_(delegate) {} -SimpleMenuModel::~SimpleMenuModel() { -} +SimpleMenuModel::~SimpleMenuModel() = default; void SimpleMenuModel::AddItem(int command_id, const std::u16string& label) { AppendItem(Item(command_id, TYPE_COMMAND, label)); @@ -186,6 +185,16 @@ void SimpleMenuModel::AddSubMenuWithStringId(int command_id, AddSubMenu(command_id, l10n_util::GetStringUTF16(string_id), model); } +void SimpleMenuModel::AddSubMenuWithIcon(int command_id, + const std::u16string& label, + MenuModel* model, + const ImageModel& icon) { + Item item(command_id, TYPE_SUBMENU, label); + item.submenu = model; + item.icon = icon; + AppendItem(std::move(item)); +} + void SimpleMenuModel::AddSubMenuWithStringIdAndIcon(int command_id, int string_id, MenuModel* model, @@ -330,6 +339,11 @@ void SimpleMenuModel::SetMayHaveMnemonicsAt(int index, items_[ValidateItemIndex(index)].may_have_mnemonics = may_have_mnemonics; } +void SimpleMenuModel::SetAccessibleNameAt(int index, + std::u16string accessible_name) { + items_[ValidateItemIndex(index)].accessible_name = std::move(accessible_name); +} + void SimpleMenuModel::SetElementIdentifierAt(int index, ElementIdentifier unique_id) { items_[ValidateItemIndex(index)].unique_id = unique_id; @@ -466,6 +480,10 @@ bool SimpleMenuModel::MayHaveMnemonicsAt(int index) const { return items_[ValidateItemIndex(index)].may_have_mnemonics; } +std::u16string SimpleMenuModel::GetAccessibleNameAt(int index) const { + return items_[ValidateItemIndex(index)].accessible_name; +} + ElementIdentifier SimpleMenuModel::GetElementIdentifierAt(int index) const { return items_[ValidateItemIndex(index)].unique_id; } diff --git a/chromium/ui/base/models/simple_menu_model.h b/chromium/ui/base/models/simple_menu_model.h index 6127fb6a161..529be99f6c5 100644 --- a/chromium/ui/base/models/simple_menu_model.h +++ b/chromium/ui/base/models/simple_menu_model.h @@ -31,7 +31,7 @@ class COMPONENT_EXPORT(UI_BASE) SimpleMenuModel : public MenuModel { class COMPONENT_EXPORT(UI_BASE) Delegate : public AcceleratorProvider { public: - ~Delegate() override {} + ~Delegate() override = default; // Makes |command_id| appear toggled true if it's a "check" or "radio" type // menu item. This has no effect for menu items with no boolean state. @@ -111,6 +111,10 @@ class COMPONENT_EXPORT(UI_BASE) SimpleMenuModel : public MenuModel { const std::u16string& label, MenuModel* model); void AddSubMenuWithStringId(int command_id, int string_id, MenuModel* model); + void AddSubMenuWithIcon(int command_id, + const std::u16string& label, + MenuModel* model, + const ImageModel& icon); void AddSubMenuWithStringIdAndIcon(int command_id, int string_id, MenuModel* model, @@ -170,6 +174,10 @@ class COMPONENT_EXPORT(UI_BASE) SimpleMenuModel : public MenuModel { // Sets whether the item at |index| is may have mnemonics. void SetMayHaveMnemonicsAt(int index, bool may_have_mnemonics); + + // Sets the accessible name of item at |index|. + void SetAccessibleNameAt(int index, std::u16string accessible_name); + // Sets an application-window unique identifier associated with this menu item // allowing it to be tracked without knowledge of menu-specific command IDs. void SetElementIdentifierAt(int index, ElementIdentifier unique_id); @@ -201,6 +209,7 @@ class COMPONENT_EXPORT(UI_BASE) SimpleMenuModel : public MenuModel { bool IsAlertedAt(int index) const override; bool IsNewFeatureAt(int index) const override; bool MayHaveMnemonicsAt(int index) const override; + std::u16string GetAccessibleNameAt(int index) const override; ElementIdentifier GetElementIdentifierAt(int index) const override; void ActivatedAt(int index) override; void ActivatedAt(int index, int event_flags) override; @@ -237,6 +246,7 @@ class COMPONENT_EXPORT(UI_BASE) SimpleMenuModel : public MenuModel { bool visible = true; bool is_new_feature = false; bool may_have_mnemonics = true; + std::u16string accessible_name; ElementIdentifier unique_id; }; diff --git a/chromium/ui/base/models/table_model.cc b/chromium/ui/base/models/table_model.cc index 9ebae294f57..9f8e19d7826 100644 --- a/chromium/ui/base/models/table_model.cc +++ b/chromium/ui/base/models/table_model.cc @@ -8,7 +8,7 @@ #include "base/i18n/string_compare.h" #include "base/notreached.h" #include "ui/base/l10n/l10n_util.h" -#include "ui/gfx/image/image_skia.h" +#include "ui/base/models/image_model.h" namespace ui { @@ -38,13 +38,15 @@ TableColumn::TableColumn(int id, Alignment alignment, int width, float percent) TableColumn::TableColumn(const TableColumn& other) = default; +TableColumn& TableColumn::operator=(const TableColumn& other) = default; + // TableModel ----------------------------------------------------------------- // Used for sorting. static icu::Collator* collator = NULL; -gfx::ImageSkia TableModel::GetIcon(int row) { - return gfx::ImageSkia(); +ui::ImageModel TableModel::GetIcon(int row) { + return ui::ImageModel(); } std::u16string TableModel::GetTooltip(int row) { diff --git a/chromium/ui/base/models/table_model.h b/chromium/ui/base/models/table_model.h index c7313d48c4f..7cd5fca9eff 100644 --- a/chromium/ui/base/models/table_model.h +++ b/chromium/ui/base/models/table_model.h @@ -11,12 +11,9 @@ #include "base/component_export.h" #include "third_party/icu/source/i18n/unicode/coll.h" -namespace gfx { -class ImageSkia; -} - namespace ui { +class ImageModel; class TableModelObserver; // The model driving the TableView. @@ -33,9 +30,9 @@ class COMPONENT_EXPORT(UI_BASE) TableModel { // Returns the small icon (|kIconSize| x |kIconSize|) that should be displayed // in the first column before the text. This is only used when the TableView - // was created with the ICON_AND_TEXT table type. Returns an isNull() image if - // there is no image. - virtual gfx::ImageSkia GetIcon(int row); + // was created with the ICON_AND_TEXT table type. An empty ImageModel if there + // is no image. + virtual ui::ImageModel GetIcon(int row); // Returns the tooltip, if any, to show for a particular row. If there are // multiple columns in the row, this will only be shown when hovering over @@ -73,6 +70,7 @@ struct COMPONENT_EXPORT(UI_BASE) TableColumn { TableColumn(); TableColumn(int id, Alignment alignment, int width, float percent); TableColumn(const TableColumn& other); + TableColumn& operator=(const TableColumn& other); // A unique identifier for the column. int id; diff --git a/chromium/ui/base/models/tree_node_model.h b/chromium/ui/base/models/tree_node_model.h index 41be3614bb4..4d6b884ff1c 100644 --- a/chromium/ui/base/models/tree_node_model.h +++ b/chromium/ui/base/models/tree_node_model.h @@ -241,7 +241,7 @@ class TreeNodeModel : public TreeModel { std::unique_ptr<NodeType> Remove(NodeType* parent, NodeType* node) { DCHECK(parent); - return Remove(parent, size_t{parent->GetIndexOf(node)}); + return Remove(parent, static_cast<size_t>(parent->GetIndexOf(node))); } void NotifyObserverTreeNodesAdded(NodeType* parent, diff --git a/chromium/ui/base/mojom/BUILD.gn b/chromium/ui/base/mojom/BUILD.gn index dd4b545a49c..68cabeef018 100644 --- a/chromium/ui/base/mojom/BUILD.gn +++ b/chromium/ui/base/mojom/BUILD.gn @@ -53,4 +53,5 @@ mojom("mojom") { export_class_attribute_blink = "BLINK_PLATFORM_EXPORT" export_define_blink = "BLINK_PLATFORM_IMPLEMENTATION=1" export_header_blink = "third_party/blink/public/platform/web_common.h" + webui_module_path = "chrome://resources/mojo/ui/base/mojom" } diff --git a/chromium/ui/base/mojom/window_open_disposition.mojom b/chromium/ui/base/mojom/window_open_disposition.mojom index 3bab2371882..694433127fb 100644 --- a/chromium/ui/base/mojom/window_open_disposition.mojom +++ b/chromium/ui/base/mojom/window_open_disposition.mojom @@ -29,3 +29,13 @@ enum WindowOpenDisposition { // NEW_FOREGROUND_TAB when no existing tab is found. SWITCH_TO_TAB, }; + +// Click information needed to determine user's desired window disposition using +// ui::DispositionFromClick(). +struct ClickModifiers { + bool middle_button; + bool alt_key; + bool ctrl_key; + bool meta_key; + bool shift_key; +}; diff --git a/chromium/ui/base/page_transition_types.cc b/chromium/ui/base/page_transition_types.cc index 45577be6b66..d9408ceb9e7 100644 --- a/chromium/ui/base/page_transition_types.cc +++ b/chromium/ui/base/page_transition_types.cc @@ -89,10 +89,8 @@ const char* PageTransitionGetCoreTransitionString(PageTransition type) { case PAGE_TRANSITION_RELOAD: return "reload"; case PAGE_TRANSITION_KEYWORD: return "keyword"; case PAGE_TRANSITION_KEYWORD_GENERATED: return "keyword_generated"; - case PAGE_TRANSITION_FROM_API_3: - return "api3"; } - return NULL; + return nullptr; } } // namespace ui diff --git a/chromium/ui/base/page_transition_types.h b/chromium/ui/base/page_transition_types.h index 5b8425cf0ba..41f22441b12 100644 --- a/chromium/ui/base/page_transition_types.h +++ b/chromium/ui/base/page_transition_types.h @@ -111,14 +111,11 @@ enum PageTransition { // Any of the core values above can be augmented by one or more qualifiers. // These qualifiers further define the transition. - // TODO(https://crbug.com/1141501): these are for an experiment, and will be - // removed once data is collected from experiment. - // Both of these transition types are for experiments to exclude visits from - // appearing in the omnibox. PAGE_TRANSITION_FROM_API_3 also makes it so - // the visit does not surface in the history page. Neither transition type - // is used with TYPED. - PAGE_TRANSITION_FROM_API_3 = 0x00200000, - PAGE_TRANSITION_FROM_API_2 = 0x00400000, + // The values 0x00200000 (PAGE_TRANSITION_FROM_API_3) and 0x00400000 + // (PAGE_TRANSITION_FROM_API_2) were used for experiments and were removed + // around 6/2021. The experiments ended well before 6/2021, but it's possible + // some databases still have the values. See https://crbug.com/1141501 for + // more. // A managed user attempted to visit a URL but was blocked. PAGE_TRANSITION_BLOCKED = 0x00800000, diff --git a/chromium/ui/base/pointer/DIR_METADATA b/chromium/ui/base/pointer/DIR_METADATA index 198db497480..f3434449946 100644 --- a/chromium/ui/base/pointer/DIR_METADATA +++ b/chromium/ui/base/pointer/DIR_METADATA @@ -1,10 +1,10 @@ # Metadata information for this directory. # # For more information on DIR_METADATA files, see: -# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md +# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/README.md # # For the schema of this file, see Metadata message: -# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto +# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto monorail { component: "Internals>Input" diff --git a/chromium/ui/base/pointer/touch_ui_controller.h b/chromium/ui/base/pointer/touch_ui_controller.h index 441bfe51309..3743f5b46d7 100644 --- a/chromium/ui/base/pointer/touch_ui_controller.h +++ b/chromium/ui/base/pointer/touch_ui_controller.h @@ -6,7 +6,6 @@ #define UI_BASE_POINTER_TOUCH_UI_CONTROLLER_H_ #include <memory> -#include <string> #include "base/callback_list.h" #include "base/component_export.h" diff --git a/chromium/ui/base/prediction/OWNERS b/chromium/ui/base/prediction/OWNERS new file mode 100644 index 00000000000..e1fbf38f49a --- /dev/null +++ b/chromium/ui/base/prediction/OWNERS @@ -0,0 +1,2 @@ +flackr@chromium.org +sadrul@chromium.org
\ No newline at end of file diff --git a/chromium/ui/base/prediction/README.md b/chromium/ui/base/prediction/README.md new file mode 100644 index 00000000000..3790a8e769b --- /dev/null +++ b/chromium/ui/base/prediction/README.md @@ -0,0 +1,17 @@ +#ui/base/prediction +This directory implements general purpose predictors. + +Examples of usage in scrolling can be found at third_party/blink/renderer/ +platform/widget/input/scroll_predictor.cc. + +# PredictionMetricsHandler + +Metrics from all predictors are logged by PredictorMetricsHandler. + +This is an example of the points used by +PredictionMetricsHandler::ComputeFrameOverUnderPredictionMetric. + +The analogous is valid for ComputeOverUnderPredictionMetric, using +`interpolated_` instead of `frame_interpolated_` + +![ComputeOverUnderPredictionMetric Overview](/docs/ui/base/prediction/images/frame_prediction_score.png) diff --git a/chromium/ui/base/prediction/linear_resampling.cc b/chromium/ui/base/prediction/linear_resampling.cc index b0dabac18ec..740f5fcd61f 100644 --- a/chromium/ui/base/prediction/linear_resampling.cc +++ b/chromium/ui/base/prediction/linear_resampling.cc @@ -89,11 +89,16 @@ std::unique_ptr<InputPredictor::InputData> LinearResampling::GeneratePrediction( latency_calculator_.GetResampleLatency(frame_interval); base::TimeTicks sample_time = frame_time + resample_latency; - base::TimeDelta max_prediction = - std::min(kResampleMaxPrediction, events_dt_ / 2.0); - - sample_time = - std::min(sample_time, events_queue_[0].time_stamp + max_prediction); + // Clamping shouldn't affect prediction experiment, as we're predicting + // further in the future. + if (!base::FeatureList::IsEnabled( + ::features::kResamplingScrollEventsExperimentalPrediction)) { + base::TimeDelta max_prediction = + std::min(kResampleMaxPrediction, events_dt_ / 2.0); + + sample_time = + std::min(sample_time, events_queue_[0].time_stamp + max_prediction); + } return std::make_unique<InputData>( lerp(events_queue_[0], events_queue_[1], sample_time), sample_time); diff --git a/chromium/ui/base/prediction/prediction_metrics_handler.cc b/chromium/ui/base/prediction/prediction_metrics_handler.cc index f8d4354d38c..d5b1e869953 100644 --- a/chromium/ui/base/prediction/prediction_metrics_handler.cc +++ b/chromium/ui/base/prediction/prediction_metrics_handler.cc @@ -141,6 +141,8 @@ void PredictionMetricsHandler::ComputeMetrics() { predicted_events_queue_.front().frame_time, &frame_interpolated_); next_real_ = events_queue_[low_idx_interpolated + 1].pos; + next_real_point_after_frame_ = + events_queue_[low_idx_frame_interpolated + 1].pos; int first_needed_event = std::min(low_idx_interpolated, low_idx_frame_interpolated); @@ -159,6 +161,20 @@ void PredictionMetricsHandler::ComputeMetrics() { base::UmaHistogramCounts1000( base::StrCat({histogram_name_, ".UnderPrediction"}), -score); } + base::UmaHistogramCounts1000( + base::StrCat({histogram_name_, ".PredictionScore"}), std::abs(score)); + + double frame_score = ComputeFrameOverUnderPredictionMetric(); + if (frame_score >= 0) { + base::UmaHistogramCounts1000( + base::StrCat({histogram_name_, ".FrameOverPrediction"}), frame_score); + } else { + base::UmaHistogramCounts1000( + base::StrCat({histogram_name_, ".FrameUnderPrediction"}), -frame_score); + } + base::UmaHistogramCounts1000( + base::StrCat({histogram_name_, ".FramePredictionScore"}), + std::abs(frame_score)); // Need |last_predicted_| to compute WrongDirection and Jitter metrics. if (!last_predicted_.has_value()) @@ -173,7 +189,7 @@ void PredictionMetricsHandler::ComputeMetrics() { ComputeVisualJitterMetric()); } -double PredictionMetricsHandler::ComputeOverUnderPredictionMetric() { +double PredictionMetricsHandler::ComputeOverUnderPredictionMetric() const { gfx::Vector2dF real_direction = next_real_ - interpolated_; gfx::Vector2dF relative_direction = predicted_events_queue_.front().pos - interpolated_; @@ -183,6 +199,17 @@ double PredictionMetricsHandler::ComputeOverUnderPredictionMetric() { return -relative_direction.Length(); } +double PredictionMetricsHandler::ComputeFrameOverUnderPredictionMetric() const { + gfx::Vector2dF real_direction = + next_real_point_after_frame_ - frame_interpolated_; + gfx::Vector2dF relative_direction = + predicted_events_queue_.front().pos - frame_interpolated_; + if (gfx::DotProduct(real_direction, relative_direction) >= 0) + return relative_direction.Length(); + else + return -relative_direction.Length(); +} + bool PredictionMetricsHandler::ComputeWrongDirectionMetric() { gfx::Vector2dF real_direction = next_real_ - interpolated_; gfx::Vector2dF predicted_direction = diff --git a/chromium/ui/base/prediction/prediction_metrics_handler.h b/chromium/ui/base/prediction/prediction_metrics_handler.h index 55dd4a33ebe..b0ca353d672 100644 --- a/chromium/ui/base/prediction/prediction_metrics_handler.h +++ b/chromium/ui/base/prediction/prediction_metrics_handler.h @@ -65,7 +65,12 @@ class COMPONENT_EXPORT(UI_BASE_PREDICTION) PredictionMetricsHandler { // The score is the amount of pixels the predicted point is ahead of // the real point. If the score is positive, the prediction is OverPredicting, // otherwise UnderPredicting. - double ComputeOverUnderPredictionMetric(); + double ComputeOverUnderPredictionMetric() const; + + // The score is the amount of pixels the predicted point is ahead/behind of + // the real input curve. The curve point being an interpolation of the real + // input points at the `frame_time` from the current predicted point. + double ComputeFrameOverUnderPredictionMetric() const; // Compute the PredictionJitterMetric score. // The score is the euclidean distance between 2 successive variation of @@ -104,6 +109,9 @@ class COMPONENT_EXPORT(UI_BASE_PREDICTION) PredictionMetricsHandler { // The first real event position which time is later than the predicted time. gfx::PointF next_real_; + // The first real event position which time is later than the frame time. + gfx::PointF next_real_point_after_frame_; + // Beginning of the full histogram name. It will have the various metrics' // names (.OverPrediction, .UnderPrediction, .WrongDirection, // .PredictionJitter, .VisualJitter) appended to it when counting the metric diff --git a/chromium/ui/base/prediction/prediction_metrics_handler_unittest.cc b/chromium/ui/base/prediction/prediction_metrics_handler_unittest.cc index 8ed0f380ab2..47c08628efb 100644 --- a/chromium/ui/base/prediction/prediction_metrics_handler_unittest.cc +++ b/chromium/ui/base/prediction/prediction_metrics_handler_unittest.cc @@ -162,30 +162,35 @@ void AddEvents(PredictionMetricsHandler* metrics_handler) { MillisecondsToTestTimeTicks(48), MillisecondsToTestTimeTicks(54)); // R5 - // P0 | Interpolation from R0-R1 is (1.5,1.5) + // P0 | Interpolation from R0-R1 is (1.25,1.25) + // P0 | Frame Interpolation from R0-R1 is (1.5,1.5) // UnderPrediction metrics_handler->AddPredictedEvent(gfx::PointF(1, 1), - MillisecondsToTestTimeTicks(12), + MillisecondsToTestTimeTicks(10), MillisecondsToTestTimeTicks(12)); - // P1 | Interpolation from R1-R2 is (3,3) + // P1 | Interpolation from R1-R2 is (2.5,2.5) + // P1 | Frame Interpolation from R1-R2 is (3,3) // OverPrediction | RightDirection metrics_handler->AddPredictedEvent(gfx::PointF(3.5, 3.5), - MillisecondsToTestTimeTicks(20), + MillisecondsToTestTimeTicks(18), MillisecondsToTestTimeTicks(20)); - // P2 | Interpolation from R2-R3 is (5.5,5.5) + // P2 | Interpolation from R2-R3 is (4.75,4.75) + // P2 | Frame Interpolation from R2-R3 is (5.5,5.5) // UnderPrediction | RightDirection metrics_handler->AddPredictedEvent(gfx::PointF(5, 5), - MillisecondsToTestTimeTicks(28), + MillisecondsToTestTimeTicks(26), MillisecondsToTestTimeTicks(28)); - // P3 | Interpolation from R3-R4 is (6,6) + // P3 | Interpolation from R3-R4 is (6.5,6.5) + // P3 | Frame Interpolation from R3-R4 is (6,6) // UnderPrediction | WrongDirection metrics_handler->AddPredictedEvent(gfx::PointF(7, 7), - MillisecondsToTestTimeTicks(36), + MillisecondsToTestTimeTicks(34), MillisecondsToTestTimeTicks(36)); - // P4 | Interpolation from R4-R5 is (4,4) + // P4 | Interpolation from R4-R5 is (4.5,4.5) + // P4 | Frame Interpolation from R4-R5 is (4,4) // OverPrediction | RightDirection metrics_handler->AddPredictedEvent(gfx::PointF(3, 3), - MillisecondsToTestTimeTicks(44), + MillisecondsToTestTimeTicks(42), MillisecondsToTestTimeTicks(44)); } @@ -195,19 +200,35 @@ TEST_F(PredictionMetricsHandlerTest, PredictionMetricTest) { EXPECT_THAT(histogram_tester().GetAllSamples( "Event.InputEventPrediction.Scroll.OverPrediction"), - ElementsAre(Bucket(0, 1), Bucket(1, 1))); + ElementsAre(Bucket(0, 1), Bucket(1, 1), Bucket(2, 1))); EXPECT_THAT(histogram_tester().GetAllSamples( "Event.InputEventPrediction.Scroll.UnderPrediction"), + ElementsAre(Bucket(0, 2))); + + EXPECT_THAT(histogram_tester().GetAllSamples( + "Event.InputEventPrediction.Scroll.PredictionScore"), + ElementsAre(Bucket(0, 3), Bucket(1, 1), Bucket(2, 1))); + + EXPECT_THAT(histogram_tester().GetAllSamples( + "Event.InputEventPrediction.Scroll.FrameOverPrediction"), + ElementsAre(Bucket(0, 1), Bucket(1, 1))); + + EXPECT_THAT(histogram_tester().GetAllSamples( + "Event.InputEventPrediction.Scroll.FrameUnderPrediction"), ElementsAre(Bucket(0, 2), Bucket(1, 1))); EXPECT_THAT(histogram_tester().GetAllSamples( + "Event.InputEventPrediction.Scroll.FramePredictionScore"), + ElementsAre(Bucket(0, 3), Bucket(1, 2))); + + EXPECT_THAT(histogram_tester().GetAllSamples( "Event.InputEventPrediction.Scroll.WrongDirection"), ElementsAre(Bucket(0, 3), Bucket(1, 1))); EXPECT_THAT(histogram_tester().GetAllSamples( "Event.InputEventPrediction.Scroll.PredictionJitter"), - ElementsAre(Bucket(1, 2), Bucket(2, 2))); + ElementsAre(Bucket(0, 1), Bucket(1, 2), Bucket(2, 1))); EXPECT_THAT(histogram_tester().GetAllSamples( "Event.InputEventPrediction.Scroll.VisualJitter"), diff --git a/chromium/ui/base/resource/DIR_METADATA b/chromium/ui/base/resource/DIR_METADATA index 63fc482f75d..d072fa6aeae 100644 --- a/chromium/ui/base/resource/DIR_METADATA +++ b/chromium/ui/base/resource/DIR_METADATA @@ -1,10 +1,10 @@ # Metadata information for this directory. # # For more information on DIR_METADATA files, see: -# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md +# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/README.md # # For the schema of this file, see Metadata message: -# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto +# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto monorail { component: "Internals" diff --git a/chromium/ui/base/resource/data_pack.cc b/chromium/ui/base/resource/data_pack.cc index a3f14e9a1ea..6ae2eb014c8 100644 --- a/chromium/ui/base/resource/data_pack.cc +++ b/chromium/ui/base/resource/data_pack.cc @@ -246,29 +246,28 @@ class DataPack::StringDataSource : public DataPack::DataSource { class DataPack::BufferDataSource : public DataPack::DataSource { public: - explicit BufferDataSource(base::StringPiece buffer) : buffer_(buffer) {} + explicit BufferDataSource(base::span<const uint8_t> buffer) + : buffer_(buffer) {} ~BufferDataSource() override {} // DataPack::DataSource: - size_t GetLength() const override { return buffer_.length(); } - const uint8_t* GetData() const override { - return reinterpret_cast<const uint8_t*>(buffer_.data()); - } + size_t GetLength() const override { return buffer_.size(); } + const uint8_t* GetData() const override { return buffer_.data(); } private: - base::StringPiece buffer_; + base::span<const uint8_t> buffer_; DISALLOW_COPY_AND_ASSIGN(BufferDataSource); }; -DataPack::DataPack(ui::ScaleFactor scale_factor) +DataPack::DataPack(ResourceScaleFactor resource_scale_factor) : resource_table_(nullptr), resource_count_(0), alias_table_(nullptr), alias_count_(0), text_encoding_type_(BINARY), - scale_factor_(scale_factor) { + resource_scale_factor_(resource_scale_factor) { // Static assert must be within a DataPack member to appease visiblity rules. static_assert(sizeof(Entry) == 6, "size of Entry must be 6"); static_assert(sizeof(Alias) == 4, "size of Alias must be 4"); @@ -329,7 +328,7 @@ bool DataPack::LoadFromFileRegion( return LoadImpl(std::make_unique<MemoryMappedDataSource>(std::move(mmap))); } -bool DataPack::LoadFromBuffer(base::StringPiece buffer) { +bool DataPack::LoadFromBuffer(base::span<const uint8_t> buffer) { return LoadImpl(std::make_unique<BufferDataSource>(buffer)); } @@ -483,8 +482,8 @@ ResourceHandle::TextEncodingType DataPack::GetTextEncodingType() const { return text_encoding_type_; } -ui::ScaleFactor DataPack::GetScaleFactor() const { - return scale_factor_; +ResourceScaleFactor DataPack::GetResourceScaleFactor() const { + return resource_scale_factor_; } #if DCHECK_IS_ON() @@ -492,9 +491,11 @@ void DataPack::CheckForDuplicateResources( const std::vector<std::unique_ptr<ResourceHandle>>& packs) { for (size_t i = 0; i < resource_count_ + 1; ++i) { const uint16_t resource_id = resource_table_[i].resource_id; - const float resource_scale = GetScaleForScaleFactor(scale_factor_); + const float resource_scale = + GetScaleForResourceScaleFactor(resource_scale_factor_); for (const auto& handle : packs) { - if (GetScaleForScaleFactor(handle->GetScaleFactor()) != resource_scale) + if (GetScaleForResourceScaleFactor(handle->GetResourceScaleFactor()) != + resource_scale) continue; DCHECK(!handle->HasResource(resource_id)) << "Duplicate resource " << resource_id << " with scale " diff --git a/chromium/ui/base/resource/data_pack.h b/chromium/ui/base/resource/data_pack.h index ee61a0aac96..02b9853d019 100644 --- a/chromium/ui/base/resource/data_pack.h +++ b/chromium/ui/base/resource/data_pack.h @@ -29,11 +29,11 @@ class RefCountedStaticMemory; } namespace ui { -enum ScaleFactor : int; +enum ResourceScaleFactor : int; class UI_DATA_PACK_EXPORT DataPack : public ResourceHandle { public: - explicit DataPack(ui::ScaleFactor scale_factor); + explicit DataPack(ResourceScaleFactor resource_scale_factor); ~DataPack() override; // Load a pack file from |path|, returning false on error. If the final @@ -54,7 +54,7 @@ class UI_DATA_PACK_EXPORT DataPack : public ResourceHandle { // Loads a pack file from |buffer|, returning false on error. // Data is not copied, |buffer| should stay alive during |DataPack| lifetime. - bool LoadFromBuffer(base::StringPiece buffer); + bool LoadFromBuffer(base::span<const uint8_t> buffer); // Writes a pack file containing |resources| to |path|. If there are any // text resources to be written, their encoding must already agree to the @@ -71,7 +71,7 @@ class UI_DATA_PACK_EXPORT DataPack : public ResourceHandle { base::RefCountedStaticMemory* GetStaticMemory( uint16_t resource_id) const override; TextEncodingType GetTextEncodingType() const override; - ui::ScaleFactor GetScaleFactor() const override; + ResourceScaleFactor GetResourceScaleFactor() const override; #if DCHECK_IS_ON() // Checks to see if any resource in this DataPack already exists in the list @@ -112,7 +112,7 @@ class UI_DATA_PACK_EXPORT DataPack : public ResourceHandle { // The scale of the image in this resource pack relative to images in the 1x // resource pak. - ui::ScaleFactor scale_factor_; + ResourceScaleFactor resource_scale_factor_; DISALLOW_COPY_AND_ASSIGN(DataPack); }; diff --git a/chromium/ui/base/resource/data_pack_literal.cc b/chromium/ui/base/resource/data_pack_literal.cc index f6669ed8244..b9e344d68ce 100644 --- a/chromium/ui/base/resource/data_pack_literal.cc +++ b/chromium/ui/base/resource/data_pack_literal.cc @@ -2,14 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include <stddef.h> - #include "ui/base/resource/data_pack_literal.h" #include "ui/base/resource/resource_bundle.h" namespace ui { -const char kSamplePakContentsV4[] = { +const uint8_t kSamplePakContentsV4[] = { 0x04, 0x00, 0x00, 0x00, // header(version 0x04, 0x00, 0x00, 0x00, // no. entries 0x01, // encoding) @@ -23,7 +21,7 @@ const char kSamplePakContentsV4[] = { const size_t kSamplePakSizeV4 = sizeof(kSamplePakContentsV4); -const char kSampleCompressPakContentsV5[] = { +const uint8_t kSampleCompressPakContentsV5[] = { 0x05, 0x00, 0x00, 0x00, // version 0x01, 0x00, 0x00, 0x00, // encoding + padding 0x03, 0x00, 0x01, 0x00, // num_resources, num_aliases @@ -51,7 +49,7 @@ const char kSampleCompressPakContentsV5[] = { const size_t kSampleCompressPakSizeV5 = sizeof(kSampleCompressPakContentsV5); -const char kSampleCompressScaledPakContents[] = { +const uint8_t kSampleCompressScaledPakContents[] = { 0x05, 0x00, 0x00, 0x00, // version 0x01, 0x00, 0x00, 0x00, // encoding + padding 0x03, 0x00, 0x01, 0x00, // num_resources, num_aliases @@ -76,7 +74,7 @@ const char kSampleCompressScaledPakContents[] = { const size_t kSampleCompressScaledPakSize = sizeof(kSampleCompressScaledPakContents); -const char kSampleCorruptPakContents[] = { +const uint8_t kSampleCorruptPakContents[] = { 0x04, 0x00, 0x00, 0x00, // header(version 0x04, 0x00, 0x00, 0x00, // no. entries 0x01, // encoding) @@ -91,7 +89,7 @@ const char kSampleCorruptPakContents[] = { const size_t kSampleCorruptPakSize = sizeof(kSampleCorruptPakContents); -const char kSamplePakContents2x[] = { +const uint8_t kSamplePakContents2x[] = { 0x04, 0x00, 0x00, 0x00, // header(version 0x01, 0x00, 0x00, 0x00, // no. entries 0x01, // encoding) @@ -102,7 +100,7 @@ const char kSamplePakContents2x[] = { const size_t kSamplePakSize2x = sizeof(kSamplePakContents2x); -const char kEmptyPakContents[] = { +const uint8_t kEmptyPakContents[] = { 0x04, 0x00, 0x00, 0x00, // header(version 0x00, 0x00, 0x00, 0x00, // no. entries 0x01, // encoding) diff --git a/chromium/ui/base/resource/data_pack_literal.h b/chromium/ui/base/resource/data_pack_literal.h index 83a8dc04c14..32232076b41 100644 --- a/chromium/ui/base/resource/data_pack_literal.h +++ b/chromium/ui/base/resource/data_pack_literal.h @@ -5,19 +5,22 @@ #ifndef UI_BASE_RESOURCE_DATA_PACK_LITERAL_H_ #define UI_BASE_RESOURCE_DATA_PACK_LITERAL_H_ +#include <stddef.h> +#include <stdint.h> + namespace ui { -extern const char kSamplePakContentsV4[]; +extern const uint8_t kSamplePakContentsV4[]; extern const size_t kSamplePakSizeV4; -extern const char kSampleCompressPakContentsV5[]; +extern const uint8_t kSampleCompressPakContentsV5[]; extern const size_t kSampleCompressPakSizeV5; -extern const char kSampleCompressScaledPakContents[]; +extern const uint8_t kSampleCompressScaledPakContents[]; extern const size_t kSampleCompressScaledPakSize; -extern const char kSamplePakContents2x[]; +extern const uint8_t kSamplePakContents2x[]; extern const size_t kSamplePakSize2x; -extern const char kEmptyPakContents[]; +extern const uint8_t kEmptyPakContents[]; extern const size_t kEmptyPakSize; -extern const char kSampleCorruptPakContents[]; +extern const uint8_t kSampleCorruptPakContents[]; extern const size_t kSampleCorruptPakSize; } // namespace ui diff --git a/chromium/ui/base/resource/data_pack_unittest.cc b/chromium/ui/base/resource/data_pack_unittest.cc index 25d3063a709..f3ff7d55566 100644 --- a/chromium/ui/base/resource/data_pack_unittest.cc +++ b/chromium/ui/base/resource/data_pack_unittest.cc @@ -36,8 +36,8 @@ TEST(DataPackTest, LoadFromPath) { dir.GetPath().Append(FILE_PATH_LITERAL("sample.pak")); // Dump contents into the pak file. - ASSERT_TRUE(base::WriteFile( - data_path, base::StringPiece(kSamplePakContentsV4, kSamplePakSizeV4))); + ASSERT_TRUE( + base::WriteFile(data_path, {kSamplePakContentsV4, kSamplePakSizeV4})); // Load the file through the data pack API. DataPack pack(SCALE_FACTOR_100P); @@ -71,7 +71,7 @@ TEST(DataPackTest, LoadFromPathCompressed) { // Dump contents into a compressed pak file. std::string compressed; ASSERT_TRUE(compression::GzipCompress( - base::StringPiece(kSamplePakContentsV4, kSamplePakSizeV4), &compressed)); + {kSamplePakContentsV4, kSamplePakSizeV4}, &compressed)); ASSERT_TRUE(base::WriteFile(data_path, compressed)); // Load the file through the data pack API. @@ -104,8 +104,8 @@ TEST(DataPackTest, LoadFromFile) { dir.GetPath().Append(FILE_PATH_LITERAL("sample.pak")); // Dump contents into the pak file. - ASSERT_TRUE(base::WriteFile( - data_path, base::StringPiece(kSamplePakContentsV4, kSamplePakSizeV4))); + ASSERT_TRUE( + base::WriteFile(data_path, {kSamplePakContentsV4, kSamplePakSizeV4})); base::File file(data_path, base::File::FLAG_OPEN | base::File::FLAG_READ); ASSERT_TRUE(file.IsValid()); @@ -143,8 +143,8 @@ TEST(DataPackTest, LoadFromFileRegion) { // by the actual pak file content. const uint8_t kPadding[5678] = {0}; ASSERT_TRUE(base::WriteFile(data_path, kPadding)); - ASSERT_TRUE(base::AppendToFile( - data_path, base::StringPiece(kSamplePakContentsV4, kSamplePakSizeV4))); + ASSERT_TRUE( + base::AppendToFile(data_path, {kSamplePakContentsV4, kSamplePakSizeV4})); base::File file(data_path, base::File::FLAG_OPEN | base::File::FLAG_READ); ASSERT_TRUE(file.IsValid()); @@ -176,8 +176,7 @@ TEST(DataPackTest, LoadFromFileRegion) { TEST(DataPackTest, LoadFromBufferV4) { DataPack pack(SCALE_FACTOR_100P); - ASSERT_TRUE(pack.LoadFromBuffer( - base::StringPiece(kSamplePakContentsV4, kSamplePakSizeV4))); + ASSERT_TRUE(pack.LoadFromBuffer({kSamplePakContentsV4, kSamplePakSizeV4})); base::StringPiece data; ASSERT_TRUE(pack.HasResource(4)); @@ -201,8 +200,8 @@ TEST(DataPackTest, LoadFromBufferV4) { TEST(DataPackTest, LoadFromBufferV5) { DataPack pack(SCALE_FACTOR_100P); - ASSERT_TRUE(pack.LoadFromBuffer(base::StringPiece( - kSampleCompressPakContentsV5, kSampleCompressPakSizeV5))); + ASSERT_TRUE(pack.LoadFromBuffer( + {kSampleCompressPakContentsV5, kSampleCompressPakSizeV5})); base::StringPiece data; ASSERT_TRUE(pack.HasResource(4)); @@ -342,8 +341,8 @@ TEST(DataPackTest, ModifiedWhileUsed) { dir.GetPath().Append(FILE_PATH_LITERAL("sample.pak")); // Dump contents into the pak file. - ASSERT_TRUE(base::WriteFile( - data_path, base::StringPiece(kSamplePakContentsV4, kSamplePakSizeV4))); + ASSERT_TRUE( + base::WriteFile(data_path, {kSamplePakContentsV4, kSamplePakSizeV4})); base::File file(data_path, base::File::FLAG_OPEN | base::File::FLAG_READ); ASSERT_TRUE(file.IsValid()); @@ -357,8 +356,7 @@ TEST(DataPackTest, ModifiedWhileUsed) { ASSERT_TRUE(pack.GetStringPiece(10, &data)); ASSERT_TRUE(base::WriteFile( - data_path, - base::StringPiece(kSampleCorruptPakContents, kSampleCorruptPakSize))); + data_path, {kSampleCorruptPakContents, kSampleCorruptPakSize})); // Reading asset #10 should now fail as it extends past the end of the file. ASSERT_TRUE(pack.HasResource(10)); diff --git a/chromium/ui/base/resource/mock_resource_bundle_delegate.h b/chromium/ui/base/resource/mock_resource_bundle_delegate.h index 1bf8531a47b..6c55c6440b0 100644 --- a/chromium/ui/base/resource/mock_resource_bundle_delegate.h +++ b/chromium/ui/base/resource/mock_resource_bundle_delegate.h @@ -17,7 +17,7 @@ class MockResourceBundleDelegate : public ResourceBundle::Delegate { MOCK_METHOD2(GetPathForResourcePack, base::FilePath(const base::FilePath& pack_path, - ScaleFactor scale_factor)); + ResourceScaleFactor scale_factor)); MOCK_METHOD2(GetPathForLocalePack, base::FilePath(const base::FilePath& pack_path, const std::string& locale)); @@ -25,12 +25,12 @@ class MockResourceBundleDelegate : public ResourceBundle::Delegate { MOCK_METHOD1(GetNativeImageNamed, gfx::Image(int resource_id)); MOCK_METHOD2(LoadDataResourceBytes, base::RefCountedMemory*(int resource_id, - ScaleFactor scale_factor)); + ResourceScaleFactor scale_factor)); MOCK_METHOD1(LoadDataResourceString, absl::optional<std::string>(int resource_id)); MOCK_CONST_METHOD3(GetRawDataResource, bool(int resource_id, - ScaleFactor scale_factor, + ResourceScaleFactor scale_factor, base::StringPiece* value)); MOCK_CONST_METHOD2(GetLocalizedString, bool(int message_id, std::u16string* value)); diff --git a/chromium/ui/base/resource/resource_bundle.cc b/chromium/ui/base/resource/resource_bundle.cc index 65b1649050a..276ef652516 100644 --- a/chromium/ui/base/resource/resource_bundle.cc +++ b/chromium/ui/base/resource/resource_bundle.cc @@ -62,6 +62,9 @@ #if defined(OS_WIN) #include "ui/display/win/dpi.h" + +// To avoid conflicts with the macro from the Windows SDK... +#undef LoadBitmap #endif namespace ui { @@ -186,7 +189,7 @@ class ResourceBundle::ResourceBundleImageSource : public gfx::ImageSkiaSource { gfx::ImageSkiaRep GetImageForScale(float scale) override { SkBitmap image; bool fell_back_to_1x = false; - ScaleFactor scale_factor = GetSupportedScaleFactor(scale); + ResourceScaleFactor scale_factor = GetSupportedResourceScaleFactor(scale); bool found = rb_->LoadBitmap(resource_id_, &scale_factor, &image, &fell_back_to_1x); if (!found) { @@ -213,7 +216,7 @@ class ResourceBundle::ResourceBundleImageSource : public gfx::ImageSkiaSource { base::ClampCeil(image.width() * scale), base::ClampCeil(image.height() * scale)); } else { - scale = GetScaleForScaleFactor(scale_factor); + scale = GetScaleForResourceScaleFactor(scale_factor); } return gfx::ImageSkiaRep(image, scale); } @@ -325,24 +328,18 @@ bool ResourceBundle::LocaleDataPakExists(const std::string& locale) { #endif // !defined(OS_ANDROID) void ResourceBundle::AddDataPackFromPath(const base::FilePath& path, - ScaleFactor scale_factor) { + ResourceScaleFactor scale_factor) { AddDataPackFromPathInternal(path, scale_factor, false); } -void ResourceBundle::AddOptionalDataPackFromPath(const base::FilePath& path, - ScaleFactor scale_factor) { +void ResourceBundle::AddOptionalDataPackFromPath( + const base::FilePath& path, + ResourceScaleFactor scale_factor) { AddDataPackFromPathInternal(path, scale_factor, true); } -void ResourceBundle::AddDataPackFromFile(base::File file, - ScaleFactor scale_factor) { - AddDataPackFromFileRegion(std::move(file), - base::MemoryMappedFile::Region::kWholeFile, - scale_factor); -} - -void ResourceBundle::AddDataPackFromBuffer(base::StringPiece buffer, - ScaleFactor scale_factor) { +void ResourceBundle::AddDataPackFromBuffer(base::span<const uint8_t> buffer, + ResourceScaleFactor scale_factor) { std::unique_ptr<DataPack> data_pack(new DataPack(scale_factor)); if (data_pack->LoadFromBuffer(buffer)) { AddDataPack(std::move(data_pack)); @@ -354,8 +351,8 @@ void ResourceBundle::AddDataPackFromBuffer(base::StringPiece buffer, void ResourceBundle::AddDataPackFromFileRegion( base::File file, const base::MemoryMappedFile::Region& region, - ScaleFactor scale_factor) { - std::unique_ptr<DataPack> data_pack(new DataPack(scale_factor)); + ResourceScaleFactor scale_factor) { + auto data_pack = std::make_unique<DataPack>(scale_factor); if (data_pack->LoadFromFileRegion(std::move(file), region)) { AddDataPack(std::move(data_pack)); } else { @@ -407,7 +404,7 @@ std::string ResourceBundle::LoadLocaleResources(const std::string& pref_locale, return std::string(); } - std::unique_ptr<DataPack> data_pack(new DataPack(SCALE_FACTOR_100P)); + auto data_pack = std::make_unique<DataPack>(SCALE_FACTOR_100P); if (!data_pack->LoadFromPath(locale_file_path) && crash_on_failure) { // https://crbug.com/1076423: Chrome can't start when the locale file cannot // be loaded. Crash early and gather some data. @@ -431,11 +428,12 @@ std::string ResourceBundle::LoadLocaleResources(const std::string& pref_locale, void ResourceBundle::LoadTestResources(const base::FilePath& path, const base::FilePath& locale_path) { is_test_resources_ = true; - DCHECK(!ui::GetSupportedScaleFactors().empty()); + DCHECK(!ui::GetSupportedResourceScaleFactors().empty()); // Use the given resource pak for both common and localized resources. if (!path.empty()) { - const ScaleFactor scale_factor(ui::GetSupportedScaleFactors()[0]); + const ResourceScaleFactor scale_factor( + ui::GetSupportedResourceScaleFactors()[0]); auto data_pack = std::make_unique<DataPack>(scale_factor); CHECK(data_pack->LoadFromPath(path)); AddDataPack(std::move(data_pack)); @@ -534,14 +532,13 @@ gfx::Image& ResourceBundle::GetImageNamed(int resource_id) { DCHECK(!data_packs_.empty()) << "Missing call to SetResourcesDataDLL?"; #if BUILDFLAG(IS_CHROMEOS_ASH) - ui::ScaleFactor scale_factor_to_load = GetMaxScaleFactor(); + ResourceScaleFactor scale_factor_to_load = GetMaxResourceScaleFactor(); #elif defined(OS_WIN) - ui::ScaleFactor scale_factor_to_load = - display::win::GetDPIScale() > 1.25 - ? GetMaxScaleFactor() - : ui::SCALE_FACTOR_100P; + ResourceScaleFactor scale_factor_to_load = + display::win::GetDPIScale() > 1.25 ? GetMaxResourceScaleFactor() + : ui::SCALE_FACTOR_100P; #else - ui::ScaleFactor scale_factor_to_load = ui::SCALE_FACTOR_100P; + ResourceScaleFactor scale_factor_to_load = ui::SCALE_FACTOR_100P; #endif // TODO(oshima): Consider reading the image size from png IHDR chunk and // skip decoding here and remove #ifdef below. @@ -550,7 +547,7 @@ gfx::Image& ResourceBundle::GetImageNamed(int resource_id) { // destroyed before the resource bundle is destroyed. gfx::ImageSkia image_skia( std::make_unique<ResourceBundleImageSource>(this, resource_id), - GetScaleForScaleFactor(scale_factor_to_load)); + GetScaleForResourceScaleFactor(scale_factor_to_load)); if (image_skia.isNull()) { LOG(WARNING) << "Unable to load image with id " << resource_id; NOTREACHED(); // Want to assert in debug mode. @@ -576,7 +573,7 @@ base::RefCountedMemory* ResourceBundle::LoadDataResourceBytes( base::RefCountedMemory* ResourceBundle::LoadDataResourceBytesForScale( int resource_id, - ScaleFactor scale_factor) const { + ResourceScaleFactor scale_factor) const { TRACE_EVENT("ui", "ResourceBundle::LoadDataResourceBytesForScale", [&](perfetto::EventContext ctx) { auto* event = @@ -612,7 +609,7 @@ base::StringPiece ResourceBundle::GetRawDataResource(int resource_id) const { base::StringPiece ResourceBundle::GetRawDataResourceForScale( int resource_id, - ScaleFactor scale_factor) const { + ResourceScaleFactor scale_factor) const { base::StringPiece data; if (delegate_ && delegate_->GetRawDataResource(resource_id, scale_factor, &data)) { @@ -621,7 +618,7 @@ base::StringPiece ResourceBundle::GetRawDataResourceForScale( if (scale_factor != ui::SCALE_FACTOR_100P) { for (size_t i = 0; i < data_packs_.size(); i++) { - if (data_packs_[i]->GetScaleFactor() == scale_factor && + if (data_packs_[i]->GetResourceScaleFactor() == scale_factor && data_packs_[i]->GetStringPiece(static_cast<uint16_t>(resource_id), &data)) return data; @@ -629,10 +626,10 @@ base::StringPiece ResourceBundle::GetRawDataResourceForScale( } for (size_t i = 0; i < data_packs_.size(); i++) { - if ((data_packs_[i]->GetScaleFactor() == ui::SCALE_FACTOR_100P || - data_packs_[i]->GetScaleFactor() == ui::SCALE_FACTOR_200P || - data_packs_[i]->GetScaleFactor() == ui::SCALE_FACTOR_300P || - data_packs_[i]->GetScaleFactor() == ui::SCALE_FACTOR_NONE) && + if ((data_packs_[i]->GetResourceScaleFactor() == ui::SCALE_FACTOR_100P || + data_packs_[i]->GetResourceScaleFactor() == ui::SCALE_FACTOR_200P || + data_packs_[i]->GetResourceScaleFactor() == ui::SCALE_FACTOR_300P || + data_packs_[i]->GetResourceScaleFactor() == ui::SCALE_FACTOR_NONE) && data_packs_[i]->GetStringPiece(static_cast<uint16_t>(resource_id), &data)) { return data; @@ -655,7 +652,7 @@ std::string ResourceBundle::LoadDataResourceString(int resource_id) const { std::string ResourceBundle::LoadDataResourceStringForScale( int resource_id, - ScaleFactor scaling_factor) const { + ResourceScaleFactor scaling_factor) const { std::string output; DecompressIfNeeded(GetRawDataResourceForScale(resource_id, scaling_factor), &output); @@ -816,17 +813,17 @@ void ResourceBundle::ReloadFonts() { font_cache_.clear(); } -ScaleFactor ResourceBundle::GetMaxScaleFactor() const { +ResourceScaleFactor ResourceBundle::GetMaxResourceScaleFactor() const { #if defined(OS_WIN) || defined(OS_LINUX) || defined(OS_CHROMEOS) return max_scale_factor_; #else - return GetSupportedScaleFactors().back(); + return GetSupportedResourceScaleFactors().back(); #endif } -bool ResourceBundle::IsScaleFactorSupported(ScaleFactor scale_factor) { - const std::vector<ScaleFactor>& supported_scale_factors = - ui::GetSupportedScaleFactors(); +bool ResourceBundle::IsScaleFactorSupported(ResourceScaleFactor scale_factor) { + const std::vector<ResourceScaleFactor>& supported_scale_factors = + ui::GetSupportedResourceScaleFactors(); return base::Contains(supported_scale_factors, scale_factor); } @@ -854,7 +851,7 @@ ResourceBundle::~ResourceBundle() { void ResourceBundle::InitSharedInstance(Delegate* delegate) { DCHECK(g_shared_instance_ == nullptr) << "ResourceBundle initialized twice"; g_shared_instance_ = new ResourceBundle(delegate); - std::vector<ScaleFactor> supported_scale_factors; + std::vector<ResourceScaleFactor> supported_scale_factors; #if defined(OS_IOS) display::Display display = display::Screen::GetScreen()->GetPrimaryDisplay(); if (display.device_scale_factor() > 2.0) { @@ -875,7 +872,7 @@ void ResourceBundle::InitSharedInstance(Delegate* delegate) { supported_scale_factors.push_back(SCALE_FACTOR_200P); #endif #endif - ui::SetSupportedScaleFactors(supported_scale_factors); + ui::SetSupportedResourceScaleFactors(supported_scale_factors); } void ResourceBundle::FreeImages() { @@ -899,7 +896,7 @@ void ResourceBundle::LoadChromeResources() { void ResourceBundle::AddDataPackFromPathInternal( const base::FilePath& path, - ScaleFactor scale_factor, + ResourceScaleFactor scale_factor, bool optional) { // Do not pass an empty |path| value to this method. If the absolute path is // unknown pass just the pack file name. @@ -913,7 +910,7 @@ void ResourceBundle::AddDataPackFromPathInternal( if (pack_path.empty() || !pack_path.IsAbsolute()) return; - std::unique_ptr<DataPack> data_pack(new DataPack(scale_factor)); + auto data_pack = std::make_unique<DataPack>(scale_factor); if (data_pack->LoadFromPath(pack_path)) { AddDataPack(std::move(data_pack)); } else if (!optional) { @@ -927,9 +924,9 @@ void ResourceBundle::AddDataPack(std::unique_ptr<DataPack> data_pack) { data_pack->CheckForDuplicateResources(data_packs_); #endif - if (GetScaleForScaleFactor(data_pack->GetScaleFactor()) > - GetScaleForScaleFactor(max_scale_factor_)) - max_scale_factor_ = data_pack->GetScaleFactor(); + if (GetScaleForResourceScaleFactor(data_pack->GetResourceScaleFactor()) > + GetScaleForResourceScaleFactor(max_scale_factor_)) + max_scale_factor_ = data_pack->GetResourceScaleFactor(); data_packs_.push_back(std::move(data_pack)); } @@ -984,19 +981,19 @@ bool ResourceBundle::LoadBitmap(const ResourceHandle& data_handle, } bool ResourceBundle::LoadBitmap(int resource_id, - ScaleFactor* scale_factor, + ResourceScaleFactor* scale_factor, SkBitmap* bitmap, bool* fell_back_to_1x) const { DCHECK(fell_back_to_1x); for (const auto& pack : data_packs_) { - if (pack->GetScaleFactor() == ui::SCALE_FACTOR_NONE && + if (pack->GetResourceScaleFactor() == ui::SCALE_FACTOR_NONE && LoadBitmap(*pack, resource_id, bitmap, fell_back_to_1x)) { DCHECK(!*fell_back_to_1x); *scale_factor = ui::SCALE_FACTOR_NONE; return true; } - if (pack->GetScaleFactor() == *scale_factor && + if (pack->GetResourceScaleFactor() == *scale_factor && LoadBitmap(*pack, resource_id, bitmap, fell_back_to_1x)) { return true; } @@ -1006,7 +1003,7 @@ bool ResourceBundle::LoadBitmap(int resource_id, // resources. if (is_test_resources_ && *scale_factor != ui::SCALE_FACTOR_100P) { for (const auto& pack : data_packs_) { - if (pack->GetScaleFactor() == ui::SCALE_FACTOR_100P && + if (pack->GetResourceScaleFactor() == ui::SCALE_FACTOR_100P && LoadBitmap(*pack, resource_id, bitmap, fell_back_to_1x)) { *fell_back_to_1x = true; return true; @@ -1063,15 +1060,7 @@ std::u16string ResourceBundle::GetLocalizedStringImpl(int resource_id) const { // Fall back on the main data pack (shouldn't be any strings here except // in unittests). data = GetRawDataResource(resource_id); -#if defined(OS_FUCHSIA) - CHECK(!data.empty()); -#else // !defined(OS_FUCHSIA) - if (data.empty()) { - LOG(WARNING) << "unable to find resource: " << resource_id; - NOTREACHED(); - return std::u16string(); - } -#endif // !defined(OS_FUCHSIA) + CHECK(!data.empty()) << "Unable to find resource: " << resource_id; } } diff --git a/chromium/ui/base/resource/resource_bundle.h b/chromium/ui/base/resource/resource_bundle.h index c20987a4b16..c1aee3d5f85 100644 --- a/chromium/ui/base/resource/resource_bundle.h +++ b/chromium/ui/base/resource/resource_bundle.h @@ -103,7 +103,7 @@ class COMPONENT_EXPORT(UI_BASE) ResourceBundle { // known or just the pack file name otherwise. virtual base::FilePath GetPathForResourcePack( const base::FilePath& pack_path, - ScaleFactor scale_factor) = 0; + ResourceScaleFactor scale_factor) = 0; // Called before a locale pack file is loaded. Return the full path for // the pack file to continue loading or an empty value to cancel loading. @@ -125,7 +125,7 @@ class COMPONENT_EXPORT(UI_BASE) ResourceBundle { // default resource. virtual base::RefCountedMemory* LoadDataResourceBytes( int resource_id, - ScaleFactor scale_factor) = 0; + ResourceScaleFactor scale_factor) = 0; // Supports intercepting of ResourceBundle::LoadDataResourceString(): Return // a populated absl::optional instance to override the value that @@ -138,7 +138,7 @@ class COMPONENT_EXPORT(UI_BASE) ResourceBundle { // Retrieve a raw data resource. Return true if a resource was provided or // false to attempt retrieval of the default resource. virtual bool GetRawDataResource(int resource_id, - ScaleFactor scale_factor, + ResourceScaleFactor scale_factor, base::StringPiece* value) const = 0; // Retrieve a localized string. Return true if a string was provided or @@ -147,7 +147,7 @@ class COMPONENT_EXPORT(UI_BASE) ResourceBundle { std::u16string* value) const = 0; protected: - virtual ~Delegate() {} + virtual ~Delegate() = default; }; // Initialize the ResourceBundle for this process. Does not take ownership of @@ -207,24 +207,21 @@ class COMPONENT_EXPORT(UI_BASE) ResourceBundle { // relative to the images in the 1x resource pak. This method is not thread // safe! You should call it immediately after calling InitSharedInstance. void AddDataPackFromPath(const base::FilePath& path, - ScaleFactor scale_factor); - - // Same as above but using an already open file. - void AddDataPackFromFile(base::File file, ScaleFactor scale_factor); + ResourceScaleFactor scale_factor); // Same as above but using only a region (offset + size) of the file. void AddDataPackFromFileRegion(base::File file, const base::MemoryMappedFile::Region& region, - ScaleFactor scale_factor); + ResourceScaleFactor scale_factor); // Same as above but using contents of the given buffer. - void AddDataPackFromBuffer(base::StringPiece buffer, - ScaleFactor scale_factor); + void AddDataPackFromBuffer(base::span<const uint8_t> buffer, + ResourceScaleFactor scale_factor); // Same as AddDataPackFromPath but does not log an error if the pack fails to // load. void AddOptionalDataPackFromPath(const base::FilePath& path, - ScaleFactor scale_factor); + ResourceScaleFactor scale_factor); // Changes the locale for an already-initialized ResourceBundle, returning the // name of the newly-loaded locale, or an empty string if initialization @@ -274,7 +271,7 @@ class COMPONENT_EXPORT(UI_BASE) ResourceBundle { // to read the resource. base::RefCountedMemory* LoadDataResourceBytesForScale( int resource_id, - ScaleFactor scale_factor) const; + ResourceScaleFactor scale_factor) const; // Return the contents of a scale independent resource in a // StringPiece given the resource id. @@ -284,8 +281,9 @@ class COMPONENT_EXPORT(UI_BASE) ResourceBundle { // nearest the scale factor |scale_factor|. // Use ResourceHandle::SCALE_FACTOR_NONE for scale independent image resources // (such as wallpaper). - base::StringPiece GetRawDataResourceForScale(int resource_id, - ScaleFactor scale_factor) const; + base::StringPiece GetRawDataResourceForScale( + int resource_id, + ResourceScaleFactor scale_factor) const; // Return the contents of a scale independent resource, decompressed // into a newly allocated string given the resource id. Todo: Look into @@ -295,8 +293,9 @@ class COMPONENT_EXPORT(UI_BASE) ResourceBundle { // Return the contents of a scale dependent resource, decompressed into // a newly allocated string given the resource id. - std::string LoadDataResourceStringForScale(int resource_id, - ScaleFactor scaling_factor) const; + std::string LoadDataResourceStringForScale( + int resource_id, + ResourceScaleFactor scaling_factor) const; // Return the contents of a localized resource, decompressed into a // newly allocated string given the resource id. @@ -350,10 +349,10 @@ class COMPONENT_EXPORT(UI_BASE) ResourceBundle { // Returns the maximum scale factor currently loaded. // Returns SCALE_FACTOR_100P if no resource is loaded. - ScaleFactor GetMaxScaleFactor() const; + ResourceScaleFactor GetMaxResourceScaleFactor() const; // Returns true if |scale_factor| is supported by this platform. - static bool IsScaleFactorSupported(ScaleFactor scale_factor); + static bool IsScaleFactorSupported(ResourceScaleFactor scale_factor); // Checks whether overriding locale strings is supported. This will fail with // a DCHECK if the first string resource has already been queried. @@ -406,7 +405,7 @@ class COMPONENT_EXPORT(UI_BASE) ResourceBundle { // Implementation for the public methods which add a DataPack from a path. If // |optional| is false, an error is logged on failure to load. void AddDataPackFromPathInternal(const base::FilePath& path, - ScaleFactor scale_factor, + ResourceScaleFactor scale_factor, bool optional); // Inserts |data_pack| to |data_pack_| and updates |max_scale_factor_| @@ -447,14 +446,10 @@ class COMPONENT_EXPORT(UI_BASE) ResourceBundle { // the data pack with SCALE_FACTOR_NONE, and when this happens, // |scale_factor| will be set to SCALE_FACTOR_NONE. bool LoadBitmap(int resource_id, - ScaleFactor* scale_factor, + ResourceScaleFactor* scale_factor, SkBitmap* bitmap, bool* fell_back_to_1x) const; - // Returns true if missing scaled resources should be visually indicated when - // drawing the fallback (e.g., by tinting the image). - static bool ShouldHighlightMissingScaledResources(); - // Returns true if the data in |buf| is a PNG that has the special marker // added by GRIT that indicates that the image is actually 1x data. static bool PNGContainsFallbackMarker(const unsigned char* buf, size_t size); @@ -499,7 +494,7 @@ class COMPONENT_EXPORT(UI_BASE) ResourceBundle { std::vector<std::unique_ptr<ResourceHandle>> data_packs_; // The maximum scale factor currently loaded. - ScaleFactor max_scale_factor_; + ResourceScaleFactor max_scale_factor_; // Cached images. The ResourceBundle caches all retrieved images and keeps // ownership of the pointers. diff --git a/chromium/ui/base/resource/resource_bundle_ios.mm b/chromium/ui/base/resource/resource_bundle_ios.mm index f623bdf562d..2df78a5d447 100644 --- a/chromium/ui/base/resource/resource_bundle_ios.mm +++ b/chromium/ui/base/resource/resource_bundle_ios.mm @@ -102,7 +102,7 @@ gfx::Image& ResourceBundle::GetNativeImageNamed(int resource_id) { // Load the raw data from the resource pack at the current supported scale // factor. This code assumes that only one of the possible scale factors is // supported at runtime, based on the device resolution. - ui::ScaleFactor scale_factor = GetMaxScaleFactor(); + ui::ResourceScaleFactor scale_factor = GetMaxResourceScaleFactor(); scoped_refptr<base::RefCountedMemory> data( LoadDataResourceBytesForScale(resource_id, scale_factor)); @@ -118,7 +118,7 @@ gfx::Image& ResourceBundle::GetNativeImageNamed(int resource_id) { bool is_fallback = PNGContainsFallbackMarker(data->front(), data->size()); // Create the image from the data. - CGFloat target_scale = ui::GetScaleForScaleFactor(scale_factor); + CGFloat target_scale = ui::GetScaleForResourceScaleFactor(scale_factor); CGFloat source_scale = is_fallback ? 1.0 : target_scale; base::scoped_nsobject<UIImage> ui_image( [[UIImage alloc] initWithData:ns_data scale:source_scale]); diff --git a/chromium/ui/base/resource/resource_bundle_unittest.cc b/chromium/ui/base/resource/resource_bundle_unittest.cc index 3bc04888f49..f036ebe1923 100644 --- a/chromium/ui/base/resource/resource_bundle_unittest.cc +++ b/chromium/ui/base/resource/resource_bundle_unittest.cc @@ -11,11 +11,11 @@ #include "base/base_paths.h" #include "base/big_endian.h" #include "base/check_op.h" +#include "base/cxx17_backports.h" #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/memory/ref_counted_memory.h" -#include "base/stl_util.h" #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" #include "build/chromeos_buildflags.h" @@ -138,7 +138,7 @@ TEST_F(ResourceBundleTest, DelegateGetPathForResourcePack) { ResourceBundle* resource_bundle = CreateResourceBundle(&delegate); base::FilePath pack_path(FILE_PATH_LITERAL("/path/to/test_path.pak")); - ui::ScaleFactor pack_scale_factor = ui::SCALE_FACTOR_200P; + ResourceScaleFactor pack_scale_factor = ui::SCALE_FACTOR_200P; EXPECT_CALL(delegate, GetPathForResourcePack( @@ -224,7 +224,7 @@ TEST_F(ResourceBundleTest, DelegateLoadDataResourceBytes) { new base::RefCountedStaticMemory(data, sizeof(data))); int resource_id = 5; - ui::ScaleFactor scale_factor = ui::SCALE_FACTOR_NONE; + ResourceScaleFactor scale_factor = ui::SCALE_FACTOR_NONE; EXPECT_CALL(delegate, LoadDataResourceBytes(resource_id, scale_factor)) .Times(1).WillOnce(Return(static_memory.get())); @@ -259,9 +259,8 @@ TEST_F(ResourceBundleTest, IsGzipped) { base::FilePath data_path = temp_dir_.GetPath().Append(FILE_PATH_LITERAL("sample.pak")); // Dump contents into a pak file and load it. - ASSERT_TRUE( - base::WriteFile(data_path, base::StringPiece(kSampleCompressPakContentsV5, - kSampleCompressPakSizeV5))); + ASSERT_TRUE(base::WriteFile( + data_path, {kSampleCompressPakContentsV5, kSampleCompressPakSizeV5})); ResourceBundle* resource_bundle = CreateResourceBundle(nullptr); resource_bundle->AddDataPackFromPath(data_path, SCALE_FACTOR_100P); @@ -278,9 +277,8 @@ TEST_F(ResourceBundleTest, IsBrotli) { base::FilePath data_path = temp_dir_.GetPath().Append(FILE_PATH_LITERAL("sample.pak")); // Dump contents into a pak file and load it. - ASSERT_TRUE( - base::WriteFile(data_path, base::StringPiece(kSampleCompressPakContentsV5, - kSampleCompressPakSizeV5))); + ASSERT_TRUE(base::WriteFile( + data_path, {kSampleCompressPakContentsV5, kSampleCompressPakSizeV5})); ResourceBundle* resource_bundle = CreateResourceBundle(nullptr); resource_bundle->AddDataPackFromPath(data_path, SCALE_FACTOR_100P); @@ -374,12 +372,12 @@ class ResourceBundleImageTest : public ResourceBundleTest { } // Returns resource bundle which uses an empty data pak for locale data. - ui::ResourceBundle* CreateResourceBundleWithEmptyLocalePak() { + ResourceBundle* CreateResourceBundleWithEmptyLocalePak() { // Write an empty data pak for locale data. const base::FilePath& locale_path = dir_path().Append( FILE_PATH_LITERAL("locale.pak")); - EXPECT_TRUE(base::WriteFile( - locale_path, base::StringPiece(kEmptyPakContents, kEmptyPakSize))); + EXPECT_TRUE( + base::WriteFile(locale_path, {kEmptyPakContents, kEmptyPakSize})); ui::ResourceBundle* resource_bundle = CreateResourceBundle(nullptr); @@ -407,9 +405,8 @@ TEST_F(ResourceBundleImageTest, LoadDataResourceBytes) { base::FilePath data_path = dir_path().Append(FILE_PATH_LITERAL("sample.pak")); // Dump contents into the pak files. - ASSERT_TRUE( - base::WriteFile(data_path, base::StringPiece(kSampleCompressPakContentsV5, - kSampleCompressPakSizeV5))); + ASSERT_TRUE(base::WriteFile( + data_path, {kSampleCompressPakContentsV5, kSampleCompressPakSizeV5})); // Load pak file. ResourceBundle* resource_bundle = CreateResourceBundleWithEmptyLocalePak(); @@ -440,8 +437,7 @@ TEST_F(ResourceBundleImageTest, LoadDataResourceBytesNotFound) { base::FilePath data_path = dir_path().Append(FILE_PATH_LITERAL("sample.pak")); // Dump contents into the pak files. - ASSERT_TRUE(base::WriteFile( - data_path, base::StringPiece(kEmptyPakContents, kEmptyPakSize))); + ASSERT_TRUE(base::WriteFile(data_path, {kEmptyPakContents, kEmptyPakSize})); // Create a resource bundle from the file. ResourceBundle* resource_bundle = CreateResourceBundleWithEmptyLocalePak(); @@ -465,12 +461,10 @@ TEST_F(ResourceBundleImageTest, LoadDataResourceStringForScale) { dir_path().Append(FILE_PATH_LITERAL("sample_2x.pak")); // Dump content into pak files. - ASSERT_TRUE( - base::WriteFile(data_path, base::StringPiece(kSampleCompressPakContentsV5, - kSampleCompressPakSizeV5))); ASSERT_TRUE(base::WriteFile( - data_2x_path, base::StringPiece(kSampleCompressScaledPakContents, - kSampleCompressScaledPakSize))); + data_path, {kSampleCompressPakContentsV5, kSampleCompressPakSizeV5})); + ASSERT_TRUE(base::WriteFile(data_2x_path, {kSampleCompressScaledPakContents, + kSampleCompressScaledPakSize})); // Load pak files. ResourceBundle* resource_bundle = CreateResourceBundleWithEmptyLocalePak(); @@ -488,9 +482,8 @@ TEST_F(ResourceBundleImageTest, LoadDataResourceStringForScale) { TEST_F(ResourceBundleImageTest, LoadLocalizedResourceString) { base::FilePath data_path = dir_path().Append(FILE_PATH_LITERAL("sample.pak")); // Dump content into pak file. - ASSERT_TRUE( - base::WriteFile(data_path, base::StringPiece(kSampleCompressPakContentsV5, - kSampleCompressPakSizeV5))); + ASSERT_TRUE(base::WriteFile( + data_path, {kSampleCompressPakContentsV5, kSampleCompressPakSizeV5})); // Load pak file. ResourceBundle* resource_bundle = CreateResourceBundleWithEmptyLocalePak(); resource_bundle->AddDataPackFromPath(data_path, SCALE_FACTOR_NONE); @@ -503,9 +496,8 @@ TEST_F(ResourceBundleImageTest, LoadLocalizedResourceString) { TEST_F(ResourceBundleImageTest, LoadDataResourceString) { base::FilePath data_path = dir_path().Append(FILE_PATH_LITERAL("sample.pak")); // Dump content into pak file. - ASSERT_TRUE( - base::WriteFile(data_path, base::StringPiece(kSampleCompressPakContentsV5, - kSampleCompressPakSizeV5))); + ASSERT_TRUE(base::WriteFile( + data_path, {kSampleCompressPakContentsV5, kSampleCompressPakSizeV5})); // Load pak file. ResourceBundle* resource_bundle = CreateResourceBundleWithEmptyLocalePak(); resource_bundle->AddDataPackFromPath(data_path, SCALE_FACTOR_NONE); @@ -526,10 +518,10 @@ TEST_F(ResourceBundleImageTest, GetRawDataResource) { dir_path().Append(FILE_PATH_LITERAL("sample_2x.pak")); // Dump contents into the pak files. - ASSERT_TRUE(base::WriteFile( - data_path, base::StringPiece(kSamplePakContentsV4, kSamplePakSizeV4))); - ASSERT_TRUE(base::WriteFile( - data_2x_path, base::StringPiece(kSamplePakContents2x, kSamplePakSize2x))); + ASSERT_TRUE( + base::WriteFile(data_path, {kSamplePakContentsV4, kSamplePakSizeV4})); + ASSERT_TRUE( + base::WriteFile(data_2x_path, {kSamplePakContents2x, kSamplePakSize2x})); // Load the regular and 2x pak files. ResourceBundle* resource_bundle = CreateResourceBundleWithEmptyLocalePak(); @@ -557,10 +549,11 @@ TEST_F(ResourceBundleImageTest, GetImageNamed) { #if defined(OS_WIN) display::win::SetDefaultDeviceScaleFactor(2.0); #endif - std::vector<ScaleFactor> supported_factors; + std::vector<ResourceScaleFactor> supported_factors; supported_factors.push_back(SCALE_FACTOR_100P); supported_factors.push_back(SCALE_FACTOR_200P); - test::ScopedSetSupportedScaleFactors scoped_supported(supported_factors); + test::ScopedSetSupportedResourceScaleFactors scoped_supported( + supported_factors); base::FilePath data_1x_path = dir_path().AppendASCII("sample_1x.pak"); base::FilePath data_2x_path = dir_path().AppendASCII("sample_2x.pak"); @@ -573,35 +566,35 @@ TEST_F(ResourceBundleImageTest, GetImageNamed) { resource_bundle->AddDataPackFromPath(data_1x_path, SCALE_FACTOR_100P); resource_bundle->AddDataPackFromPath(data_2x_path, SCALE_FACTOR_200P); - EXPECT_EQ(SCALE_FACTOR_200P, resource_bundle->GetMaxScaleFactor()); + EXPECT_EQ(SCALE_FACTOR_200P, resource_bundle->GetMaxResourceScaleFactor()); gfx::ImageSkia* image_skia = resource_bundle->GetImageSkiaNamed(3); #if BUILDFLAG(IS_CHROMEOS_ASH) || defined(OS_WIN) // ChromeOS/Windows load highest scale factor first. - EXPECT_EQ(ui::SCALE_FACTOR_200P, - GetSupportedScaleFactor(image_skia->image_reps()[0].scale())); + EXPECT_EQ(ui::SCALE_FACTOR_200P, GetSupportedResourceScaleFactor( + image_skia->image_reps()[0].scale())); #else - EXPECT_EQ(ui::SCALE_FACTOR_100P, - GetSupportedScaleFactor(image_skia->image_reps()[0].scale())); + EXPECT_EQ(ui::SCALE_FACTOR_100P, GetSupportedResourceScaleFactor( + image_skia->image_reps()[0].scale())); #endif // Resource ID 3 exists in both 1x and 2x paks. Image reps should be // available for both scale factors in |image_skia|. - gfx::ImageSkiaRep image_rep = - image_skia->GetRepresentation( - GetScaleForScaleFactor(ui::SCALE_FACTOR_100P)); - EXPECT_EQ(ui::SCALE_FACTOR_100P, GetSupportedScaleFactor(image_rep.scale())); - image_rep = - image_skia->GetRepresentation( - GetScaleForScaleFactor(ui::SCALE_FACTOR_200P)); - EXPECT_EQ(ui::SCALE_FACTOR_200P, GetSupportedScaleFactor(image_rep.scale())); - - // The 1.4x pack was not loaded. Requesting the 1.4x resource should return - // either the 1x or the 2x resource. + gfx::ImageSkiaRep image_rep = image_skia->GetRepresentation( + GetScaleForResourceScaleFactor(ui::SCALE_FACTOR_100P)); + EXPECT_EQ(ui::SCALE_FACTOR_100P, + GetSupportedResourceScaleFactor(image_rep.scale())); image_rep = image_skia->GetRepresentation( - ui::GetScaleForScaleFactor(ui::SCALE_FACTOR_140P)); - ui::ScaleFactor scale_factor = GetSupportedScaleFactor(image_rep.scale()); + GetScaleForResourceScaleFactor(ui::SCALE_FACTOR_200P)); + EXPECT_EQ(ui::SCALE_FACTOR_200P, + GetSupportedResourceScaleFactor(image_rep.scale())); + + // Requesting the 1.4x resource should return either the 1x or the 2x + // resource. + image_rep = image_skia->GetRepresentation(1.4f); + ResourceScaleFactor scale_factor = + GetSupportedResourceScaleFactor(image_rep.scale()); EXPECT_TRUE(scale_factor == ui::SCALE_FACTOR_100P || scale_factor == ui::SCALE_FACTOR_200P); @@ -613,10 +606,11 @@ TEST_F(ResourceBundleImageTest, GetImageNamed) { // Test that GetImageNamed() behaves properly for images which GRIT has // annotated as having fallen back to 1x. TEST_F(ResourceBundleImageTest, GetImageNamedFallback1x) { - std::vector<ScaleFactor> supported_factors; + std::vector<ResourceScaleFactor> supported_factors; supported_factors.push_back(SCALE_FACTOR_100P); supported_factors.push_back(SCALE_FACTOR_200P); - test::ScopedSetSupportedScaleFactors scoped_supported(supported_factors); + test::ScopedSetSupportedResourceScaleFactors scoped_supported( + supported_factors); base::FilePath data_path = dir_path().AppendASCII("sample.pak"); base::FilePath data_2x_path = dir_path().AppendASCII("sample_2x.pak"); @@ -638,66 +632,24 @@ TEST_F(ResourceBundleImageTest, GetImageNamedFallback1x) { // The image rep for 2x should be available. It should be resized to the // proper 2x size. - gfx::ImageSkiaRep image_rep = - image_skia->GetRepresentation(GetScaleForScaleFactor( - ui::SCALE_FACTOR_200P)); - EXPECT_EQ(ui::SCALE_FACTOR_200P, GetSupportedScaleFactor(image_rep.scale())); + gfx::ImageSkiaRep image_rep = image_skia->GetRepresentation( + GetScaleForResourceScaleFactor(ui::SCALE_FACTOR_200P)); + EXPECT_EQ(ui::SCALE_FACTOR_200P, + GetSupportedResourceScaleFactor(image_rep.scale())); EXPECT_EQ(20, image_rep.pixel_width()); EXPECT_EQ(20, image_rep.pixel_height()); } -#if defined(OS_WIN) -// Tests GetImageNamed() behaves properly when the size of a scaled image -// requires rounding as a result of using a non-integer scale factor. -// Scale factors of 140 and 1805 are Windows specific. -TEST_F(ResourceBundleImageTest, GetImageNamedFallback1xRounding) { - std::vector<ScaleFactor> supported_factors; - supported_factors.push_back(SCALE_FACTOR_100P); - supported_factors.push_back(SCALE_FACTOR_140P); - supported_factors.push_back(SCALE_FACTOR_180P); - test::ScopedSetSupportedScaleFactors scoped_supported(supported_factors); - - base::FilePath data_path = dir_path().AppendASCII("sample.pak"); - base::FilePath data_140P_path = dir_path().AppendASCII("sample_140P.pak"); - base::FilePath data_180P_path = dir_path().AppendASCII("sample_180P.pak"); - - CreateDataPackWithSingleBitmap(data_path, 8, base::StringPiece()); - // Mark 140% and 180% images as requiring 1x fallback. - CreateDataPackWithSingleBitmap( - data_140P_path, 8, - base::StringPiece(reinterpret_cast<const char*>(kPngScaleChunk), - base::size(kPngScaleChunk))); - CreateDataPackWithSingleBitmap( - data_180P_path, 8, - base::StringPiece(reinterpret_cast<const char*>(kPngScaleChunk), - base::size(kPngScaleChunk))); - - ResourceBundle* resource_bundle = CreateResourceBundleWithEmptyLocalePak(); - resource_bundle->AddDataPackFromPath(data_path, SCALE_FACTOR_100P); - resource_bundle->AddDataPackFromPath(data_140P_path, SCALE_FACTOR_140P); - resource_bundle->AddDataPackFromPath(data_180P_path, SCALE_FACTOR_180P); - - // Non-integer dimensions should be rounded up. - gfx::ImageSkia* image_skia = resource_bundle->GetImageSkiaNamed(3); - gfx::ImageSkiaRep image_rep = - image_skia->GetRepresentation( - GetScaleForScaleFactor(ui::SCALE_FACTOR_140P)); - EXPECT_EQ(12, image_rep.pixel_width()); - image_rep = image_skia->GetRepresentation( - GetScaleForScaleFactor(ui::SCALE_FACTOR_180P)); - EXPECT_EQ(15, image_rep.pixel_width()); -} -#endif - TEST_F(ResourceBundleImageTest, FallbackToNone) { - std::vector<ScaleFactor> supported_factors; + std::vector<ResourceScaleFactor> supported_factors; supported_factors.push_back(SCALE_FACTOR_100P); supported_factors.push_back(SCALE_FACTOR_200P); supported_factors.push_back(SCALE_FACTOR_300P); // Presents a consistent set of supported scale factors for all platforms. // iOS does not include SCALE_FACTOR_100P, which breaks the test below. - test::ScopedSetSupportedScaleFactors scoped_supported(supported_factors); + test::ScopedSetSupportedResourceScaleFactors scoped_supported( + supported_factors); base::FilePath data_default_path = dir_path().AppendASCII("sample.pak"); @@ -711,8 +663,8 @@ TEST_F(ResourceBundleImageTest, FallbackToNone) { gfx::ImageSkia* image_skia = resource_bundle->GetImageSkiaNamed(3); EXPECT_EQ(1u, image_skia->image_reps().size()); EXPECT_TRUE(image_skia->image_reps()[0].unscaled()); - EXPECT_EQ(ui::SCALE_FACTOR_100P, - GetSupportedScaleFactor(image_skia->image_reps()[0].scale())); + EXPECT_EQ(ui::SCALE_FACTOR_100P, GetSupportedResourceScaleFactor( + image_skia->image_reps()[0].scale())); } } // namespace ui diff --git a/chromium/ui/base/resource/resource_data_dll_win.cc b/chromium/ui/base/resource/resource_data_dll_win.cc index 77a1c3c4962..ab78b95cb60 100644 --- a/chromium/ui/base/resource/resource_data_dll_win.cc +++ b/chromium/ui/base/resource/resource_data_dll_win.cc @@ -58,7 +58,7 @@ ResourceHandle::TextEncodingType ResourceDataDLL::GetTextEncodingType() const { return BINARY; } -ScaleFactor ResourceDataDLL::GetScaleFactor() const { +ResourceScaleFactor ResourceDataDLL::GetResourceScaleFactor() const { return ui::SCALE_FACTOR_NONE; } diff --git a/chromium/ui/base/resource/resource_data_dll_win.h b/chromium/ui/base/resource/resource_data_dll_win.h index c5f0e60ac38..fce43feb42a 100644 --- a/chromium/ui/base/resource/resource_data_dll_win.h +++ b/chromium/ui/base/resource/resource_data_dll_win.h @@ -26,7 +26,7 @@ class ResourceDataDLL : public ResourceHandle { base::RefCountedStaticMemory* GetStaticMemory( uint16_t resource_id) const override; TextEncodingType GetTextEncodingType() const override; - ScaleFactor GetScaleFactor() const override; + ResourceScaleFactor GetResourceScaleFactor() const override; private: const HINSTANCE module_; diff --git a/chromium/ui/base/resource/resource_handle.h b/chromium/ui/base/resource/resource_handle.h index 055b0370868..20e963f8706 100644 --- a/chromium/ui/base/resource/resource_handle.h +++ b/chromium/ui/base/resource/resource_handle.h @@ -47,7 +47,7 @@ class UI_DATA_PACK_EXPORT ResourceHandle { // The scale of images in this resource pack relative to images in the 1x // resource pak. - virtual ScaleFactor GetScaleFactor() const = 0; + virtual ResourceScaleFactor GetResourceScaleFactor() const = 0; }; } // namespace ui diff --git a/chromium/ui/base/resource/scale_factor.cc b/chromium/ui/base/resource/scale_factor.cc index aa52f48804e..c6aa5f2ffe5 100644 --- a/chromium/ui/base/resource/scale_factor.cc +++ b/chromium/ui/base/resource/scale_factor.cc @@ -4,21 +4,20 @@ #include "ui/base/resource/scale_factor.h" -#include "base/stl_util.h" +#include "base/cxx17_backports.h" namespace ui { namespace { -const float kScaleFactorScales[] = {1.0f, 1.0f, 1.25f, 1.33f, 1.4f, 1.5f, 1.8f, - 2.0f, 2.5f, 3.0f}; -static_assert(NUM_SCALE_FACTORS == base::size(kScaleFactorScales), +const float kResourceScaleFactorScales[] = {1.0f, 1.0f, 2.0f, 3.0f}; +static_assert(NUM_SCALE_FACTORS == base::size(kResourceScaleFactorScales), "kScaleFactorScales has incorrect size"); } // namespace -float GetScaleForScaleFactor(ScaleFactor scale_factor) { - return kScaleFactorScales[scale_factor]; +float GetScaleForResourceScaleFactor(ResourceScaleFactor scale_factor) { + return kResourceScaleFactorScales[scale_factor]; } } // namespace ui diff --git a/chromium/ui/base/resource/scale_factor.h b/chromium/ui/base/resource/scale_factor.h index 06205851715..24bfbe16a78 100644 --- a/chromium/ui/base/resource/scale_factor.h +++ b/chromium/ui/base/resource/scale_factor.h @@ -9,28 +9,23 @@ namespace ui { -// Supported UI scale factors for the platform. This is used as an index +// Supported resource scale factors for the platform. This is used as an index // into the array |kScaleFactorScales| which maps the enum value to a float. // SCALE_FACTOR_NONE is used for density independent resources such as // string, html/js files or an image that can be used for any scale factors // (such as wallpapers). -enum ScaleFactor : int { +enum ResourceScaleFactor : int { SCALE_FACTOR_NONE = 0, SCALE_FACTOR_100P, - SCALE_FACTOR_125P, - SCALE_FACTOR_133P, - SCALE_FACTOR_140P, - SCALE_FACTOR_150P, - SCALE_FACTOR_180P, SCALE_FACTOR_200P, - SCALE_FACTOR_250P, SCALE_FACTOR_300P, NUM_SCALE_FACTORS // This always appears last. }; // Returns the image scale for the scale factor passed in. -UI_DATA_PACK_EXPORT float GetScaleForScaleFactor(ScaleFactor scale_factor); +UI_DATA_PACK_EXPORT float GetScaleForResourceScaleFactor( + ResourceScaleFactor scale_factor); } // namespace ui diff --git a/chromium/ui/base/resource/scale_factor_unittest.cc b/chromium/ui/base/resource/scale_factor_unittest.cc index 52bf39b2ab8..13a5a3cb435 100644 --- a/chromium/ui/base/resource/scale_factor_unittest.cc +++ b/chromium/ui/base/resource/scale_factor_unittest.cc @@ -8,16 +8,10 @@ namespace ui { -TEST(ScaleFactorTest, GetScaleFactorScale) { - EXPECT_FLOAT_EQ(1.0f, GetScaleForScaleFactor(SCALE_FACTOR_100P)); - EXPECT_FLOAT_EQ(1.25f, GetScaleForScaleFactor(SCALE_FACTOR_125P)); - EXPECT_FLOAT_EQ(1.33f, GetScaleForScaleFactor(SCALE_FACTOR_133P)); - EXPECT_FLOAT_EQ(1.4f, GetScaleForScaleFactor(SCALE_FACTOR_140P)); - EXPECT_FLOAT_EQ(1.5f, GetScaleForScaleFactor(SCALE_FACTOR_150P)); - EXPECT_FLOAT_EQ(1.8f, GetScaleForScaleFactor(SCALE_FACTOR_180P)); - EXPECT_FLOAT_EQ(2.0f, GetScaleForScaleFactor(SCALE_FACTOR_200P)); - EXPECT_FLOAT_EQ(2.5f, GetScaleForScaleFactor(SCALE_FACTOR_250P)); - EXPECT_FLOAT_EQ(3.0f, GetScaleForScaleFactor(SCALE_FACTOR_300P)); +TEST(ScaleFactorTest, GetResourceScaleFactorScale) { + EXPECT_FLOAT_EQ(1.0f, GetScaleForResourceScaleFactor(SCALE_FACTOR_100P)); + EXPECT_FLOAT_EQ(2.0f, GetScaleForResourceScaleFactor(SCALE_FACTOR_200P)); + EXPECT_FLOAT_EQ(3.0f, GetScaleForResourceScaleFactor(SCALE_FACTOR_300P)); } } // namespace ui diff --git a/chromium/ui/base/template_expressions.cc b/chromium/ui/base/template_expressions.cc index c28d58da732..639da5ed71f 100644 --- a/chromium/ui/base/template_expressions.cc +++ b/chromium/ui/base/template_expressions.cc @@ -9,7 +9,7 @@ #include <ostream> #include "base/check_op.h" -#include "base/stl_util.h" +#include "base/cxx17_backports.h" #include "base/strings/string_piece.h" #include "base/values.h" #include "build/chromeos_buildflags.h" @@ -213,13 +213,12 @@ bool ReplaceTemplateExpressionsInternal( namespace ui { void TemplateReplacementsFromDictionaryValue( - const base::DictionaryValue& dictionary, + const base::Value& dictionary, TemplateReplacements* replacements) { - for (base::DictionaryValue::Iterator it(dictionary); !it.IsAtEnd(); - it.Advance()) { - std::string str_value; - if (it.value().GetAsString(&str_value)) - (*replacements)[it.key()] = str_value; + for (auto pair : dictionary.DictItems()) { + const std::string* value = pair.second.GetIfString(); + if (value) + (*replacements)[pair.first] = pair.second.GetString(); } } @@ -265,7 +264,6 @@ bool ReplaceTemplateExpressionsInJS(base::StringPiece source, remaining = remaining.substr(current_template.start + current_template.length); } - return true; } std::string ReplaceTemplateExpressions(base::StringPiece source, diff --git a/chromium/ui/base/template_expressions.h b/chromium/ui/base/template_expressions.h index 470d54b3734..e63e9515634 100644 --- a/chromium/ui/base/template_expressions.h +++ b/chromium/ui/base/template_expressions.h @@ -15,7 +15,7 @@ #include "base/strings/string_piece.h" namespace base { -class DictionaryValue; +class Value; } namespace ui { @@ -28,7 +28,7 @@ typedef std::map<const std::string, std::string> TemplateReplacements; // TODO(dschuyler): remove this function by using TemplateReplacements directly. COMPONENT_EXPORT(UI_BASE) void TemplateReplacementsFromDictionaryValue( - const base::DictionaryValue& dictionary, + const base::Value& dictionary, TemplateReplacements* replacements); // Replace $i18n*{foo} in the format string with the value for the foo key in diff --git a/chromium/ui/base/text/DIR_METADATA b/chromium/ui/base/text/DIR_METADATA index 55a8d5782a3..b514febf9c9 100644 --- a/chromium/ui/base/text/DIR_METADATA +++ b/chromium/ui/base/text/DIR_METADATA @@ -1,10 +1,10 @@ # Metadata information for this directory. # # For more information on DIR_METADATA files, see: -# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md +# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/README.md # # For the schema of this file, see Metadata message: -# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto +# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto monorail { component: "UI>Localization" diff --git a/chromium/ui/base/text/bytes_formatting.cc b/chromium/ui/base/text/bytes_formatting.cc index 69380ebe2cd..eda25e95b2b 100644 --- a/chromium/ui/base/text/bytes_formatting.cc +++ b/chromium/ui/base/text/bytes_formatting.cc @@ -4,10 +4,12 @@ #include "ui/base/text/bytes_formatting.h" +#include <ostream> + #include "base/check.h" +#include "base/cxx17_backports.h" #include "base/i18n/number_formatting.h" #include "base/notreached.h" -#include "base/stl_util.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "ui/base/l10n/l10n_util.h" diff --git a/chromium/ui/base/text/bytes_formatting_unittest.cc b/chromium/ui/base/text/bytes_formatting_unittest.cc index eba175478a8..5bb2c46dd76 100644 --- a/chromium/ui/base/text/bytes_formatting_unittest.cc +++ b/chromium/ui/base/text/bytes_formatting_unittest.cc @@ -5,7 +5,7 @@ #include <stddef.h> #include <stdint.h> -#include "base/stl_util.h" +#include "base/cxx17_backports.h" #include "base/strings/utf_string_conversions.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/text/bytes_formatting.h" diff --git a/chromium/ui/base/theme_provider.h b/chromium/ui/base/theme_provider.h index f47a724d56d..c7c4c7a9658 100644 --- a/chromium/ui/base/theme_provider.h +++ b/chromium/ui/base/theme_provider.h @@ -66,7 +66,7 @@ class COMPONENT_EXPORT(UI_BASE) ThemeProvider { // implementations of ThemeProvider. Returns NULL on error. virtual base::RefCountedMemory* GetRawData( int id, - ui::ScaleFactor scale_factor) const = 0; + ui::ResourceScaleFactor scale_factor) const = 0; }; } // namespace ui diff --git a/chromium/ui/base/ui_base_features.cc b/chromium/ui/base/ui_base_features.cc index 55c38066699..f4c5ff7c63f 100644 --- a/chromium/ui/base/ui_base_features.cc +++ b/chromium/ui/base/ui_base_features.cc @@ -6,10 +6,6 @@ #include "build/chromeos_buildflags.h" -#if !defined(OS_IOS) -#include "media/media_buildflags.h" // nogncheck -#endif - #if defined(OS_WIN) #include "base/win/windows_version.h" #endif @@ -32,11 +28,6 @@ const base::Feature kScreenPowerListenerForNativeWinOcclusion{ base::FEATURE_ENABLED_BY_DEFAULT}; #endif // OW_WIN -// Whether or not filenames are supported on the clipboard. -// https://crbug.com/1175483 -const base::Feature kClipboardFilenames{"ClipboardFilenames", - base::FEATURE_ENABLED_BY_DEFAULT}; - // Whether or not to delegate color queries to the color provider. const base::Feature kColorProviderRedirection = { "ColorProviderRedirection", base::FEATURE_DISABLED_BY_DEFAULT}; @@ -147,10 +138,23 @@ const base::Feature kExperimentalFlingAnimation { #endif }; -#if defined(OS_WIN) -const base::Feature kElasticOverscrollWin = {"ElasticOverscrollWin", - base::FEATURE_DISABLED_BY_DEFAULT}; +#if defined(OS_WIN) || defined(OS_ANDROID) +// Cached in Java as well, make sure defaults are updated together. +const base::Feature kElasticOverscroll = {"ElasticOverscroll", + base::FEATURE_DISABLED_BY_DEFAULT}; +#endif // defined(OS_WIN) || defined(OS_ANDROID) + +#if defined(OS_ANDROID) +const char kElasticOverscrollType[] = "type"; +const char kElasticOverscrollTypeFilter[] = "filter"; +const char kElasticOverscrollTypeTransform[] = "transform"; +#endif // defined(OS_ANDROID) +// Enables focus follow follow cursor (sloppyfocus). +const base::Feature kFocusFollowsCursor = {"FocusFollowsCursor", + base::FEATURE_DISABLED_BY_DEFAULT}; + +#if defined(OS_WIN) // Enables InputPane API for controlling on screen keyboard. const base::Feature kInputPaneOnScreenKeyboard = { "InputPaneOnScreenKeyboard", base::FEATURE_ENABLED_BY_DEFAULT}; @@ -194,20 +198,6 @@ bool IsDeprecateAltBasedSixPackEnabled() { } #endif // defined(OS_CHROMEOS) -#if defined(OS_WIN) || defined(OS_APPLE) || defined(OS_LINUX) || \ - defined(OS_CHROMEOS) -// Enables stylus appearing as touch when in contact with digitizer. -const base::Feature kDirectManipulationStylus = { - "DirectManipulationStylus", -#if defined(OS_WIN) - base::FEATURE_ENABLED_BY_DEFAULT -#else - base::FEATURE_DISABLED_BY_DEFAULT -#endif -}; -#endif // defined(OS_WIN) || defined(OS_APPLE) || defined(OS_LINUX) || - // defined(OS_CHROMEOS) - // Enables forced colors mode for web content. const base::Feature kForcedColors{"ForcedColors", base::FEATURE_ENABLED_BY_DEFAULT}; @@ -231,26 +221,7 @@ const base::Feature kEyeDropper { }; bool IsEyeDropperEnabled() { - return IsFormControlsRefreshEnabled() && - base::FeatureList::IsEnabled(features::kEyeDropper); -} - -const base::Feature kCSSColorSchemeUARendering = { - "CSSColorSchemeUARendering", base::FEATURE_ENABLED_BY_DEFAULT}; - -bool IsCSSColorSchemeUARenderingEnabled() { - static const bool css_color_scheme_ua_rendering_enabled = - base::FeatureList::IsEnabled(features::kCSSColorSchemeUARendering); - return css_color_scheme_ua_rendering_enabled; -} - -const base::Feature kFormControlsRefresh = {"FormControlsRefresh", - base::FEATURE_ENABLED_BY_DEFAULT}; - -bool IsFormControlsRefreshEnabled() { - static const bool form_controls_refresh_enabled = - base::FeatureList::IsEnabled(features::kFormControlsRefresh); - return form_controls_refresh_enabled; + return base::FeatureList::IsEnabled(features::kEyeDropper); } // Enable the common select popup. @@ -277,18 +248,7 @@ const base::Feature kHandwritingGesture = {"HandwritingGesture", #endif const base::Feature kSynchronousPageFlipTesting{ - "SynchronousPageFlipTesting", -#if defined(OS_IOS) - base::FEATURE_DISABLED_BY_DEFAULT -#else -// We can't combine these directives because BUILDFLAG won't be defined on iOS. -#if BUILDFLAG(USE_CHROMEOS_PROTECTED_MEDIA) - base::FEATURE_ENABLED_BY_DEFAULT -#else - base::FEATURE_DISABLED_BY_DEFAULT -#endif -#endif -}; + "SynchronousPageFlipTesting", base::FEATURE_ENABLED_BY_DEFAULT}; bool IsSynchronousPageFlipTestingEnabled() { return base::FeatureList::IsEnabled(kSynchronousPageFlipTesting); diff --git a/chromium/ui/base/ui_base_features.h b/chromium/ui/base/ui_base_features.h index 9699e8b8f57..7571147d87c 100644 --- a/chromium/ui/base/ui_base_features.h +++ b/chromium/ui/base/ui_base_features.h @@ -16,13 +16,13 @@ namespace features { // Keep sorted! COMPONENT_EXPORT(UI_BASE_FEATURES) -extern const base::Feature kClipboardFilenames; -COMPONENT_EXPORT(UI_BASE_FEATURES) extern const base::Feature kColorProviderRedirection; COMPONENT_EXPORT(UI_BASE_FEATURES) extern const base::Feature kCompositorThreadedScrollbarScrolling; COMPONENT_EXPORT(UI_BASE_FEATURES) extern const base::Feature kExperimentalFlingAnimation; +COMPONENT_EXPORT(UI_BASE_FEATURES) +extern const base::Feature kFocusFollowsCursor; #if BUILDFLAG(IS_CHROMEOS_ASH) COMPONENT_EXPORT(UI_BASE_FEATURES) extern const base::Feature kSettingsShowsPerKeyboardSettings; @@ -46,12 +46,24 @@ COMPONENT_EXPORT(UI_BASE_FEATURES) bool IsNotificationIndicatorEnabled(); COMPONENT_EXPORT(UI_BASE_FEATURES) bool IsUiGpuRasterizationEnabled(); +#if defined(OS_WIN) || defined(OS_ANDROID) +COMPONENT_EXPORT(UI_BASE_FEATURES) +extern const base::Feature kElasticOverscroll; +#endif // defined(OS_WIN) || defined(OS_ANDROID) + +#if defined(OS_ANDROID) +COMPONENT_EXPORT(UI_BASE_FEATURES) +extern const char kElasticOverscrollType[]; +COMPONENT_EXPORT(UI_BASE_FEATURES) +extern const char kElasticOverscrollTypeFilter[]; +COMPONENT_EXPORT(UI_BASE_FEATURES) +extern const char kElasticOverscrollTypeTransform[]; +#endif // defined(OS_ANDROID) + #if defined(OS_WIN) COMPONENT_EXPORT(UI_BASE_FEATURES) extern const base::Feature kCalculateNativeWinOcclusion; COMPONENT_EXPORT(UI_BASE_FEATURES) -extern const base::Feature kElasticOverscrollWin; -COMPONENT_EXPORT(UI_BASE_FEATURES) extern const base::Feature kInputPaneOnScreenKeyboard; COMPONENT_EXPORT(UI_BASE_FEATURES) extern const base::Feature kPointerEventsForTouch; @@ -77,13 +89,6 @@ COMPONENT_EXPORT(UI_BASE_FEATURES) bool IsDeprecateAltBasedSixPackEnabled(); #endif // defined(OS_CHROMEOS) -#if defined(OS_WIN) || defined(OS_APPLE) || defined(OS_LINUX) || \ - defined(OS_CHROMEOS) -COMPONENT_EXPORT(UI_BASE_FEATURES) -extern const base::Feature kDirectManipulationStylus; -#endif // defined(OS_WIN) || defined(OS_APPLE) || defined(OS_LINUX) || - // defined(OS_CHROMEOS) - // Used to enable forced colors mode for web content. COMPONENT_EXPORT(UI_BASE_FEATURES) extern const base::Feature kForcedColors; COMPONENT_EXPORT(UI_BASE_FEATURES) bool IsForcedColorsEnabled(); @@ -92,16 +97,6 @@ COMPONENT_EXPORT(UI_BASE_FEATURES) bool IsForcedColorsEnabled(); COMPONENT_EXPORT(UI_BASE_FEATURES) extern const base::Feature kEyeDropper; COMPONENT_EXPORT(UI_BASE_FEATURES) bool IsEyeDropperEnabled(); -// Used to enable form controls and scrollbar dark mode rendering. -COMPONENT_EXPORT(UI_BASE_FEATURES) -extern const base::Feature kCSSColorSchemeUARendering; -COMPONENT_EXPORT(UI_BASE_FEATURES) bool IsCSSColorSchemeUARenderingEnabled(); - -// Used to enable the new controls UI. -COMPONENT_EXPORT(UI_BASE_FEATURES) -extern const base::Feature kFormControlsRefresh; -COMPONENT_EXPORT(UI_BASE_FEATURES) bool IsFormControlsRefreshEnabled(); - // Used to enable the common select popup. COMPONENT_EXPORT(UI_BASE_FEATURES) extern const base::Feature kUseCommonSelectPopup; diff --git a/chromium/ui/base/ui_base_paths.cc b/chromium/ui/base/ui_base_paths.cc index b63668fba03..c98aa3f53bf 100644 --- a/chromium/ui/base/ui_base_paths.cc +++ b/chromium/ui/base/ui_base_paths.cc @@ -24,19 +24,21 @@ bool PathProvider(int key, base::FilePath* result) { base::FilePath cur; switch (key) { case DIR_LOCALES: +#if defined(OS_ANDROID) + if (!base::PathService::Get(DIR_RESOURCE_PAKS_ANDROID, &cur)) + return false; +#elif defined(OS_APPLE) if (!base::PathService::Get(base::DIR_MODULE, &cur)) return false; -#if defined(OS_APPLE) // On Mac, locale files are in Contents/Resources, a sibling of the // App dir. cur = cur.DirName(); cur = cur.Append(FILE_PATH_LITERAL("Resources")); -#elif defined(OS_ANDROID) - if (!base::PathService::Get(DIR_RESOURCE_PAKS_ANDROID, &cur)) +#else // !defined(OS_APPLE) + if (!base::PathService::Get(base::DIR_ASSETS, &cur)) return false; -#else cur = cur.Append(FILE_PATH_LITERAL("locales")); -#endif +#endif // !defined(OS_APPLE) create_dir = true; break; // The following are only valid in the development environment, and diff --git a/chromium/ui/base/ui_base_switches.cc b/chromium/ui/base/ui_base_switches.cc index 384150dcdaa..94430efd0d5 100644 --- a/chromium/ui/base/ui_base_switches.cc +++ b/chromium/ui/base/ui_base_switches.cc @@ -6,6 +6,14 @@ namespace switches { +#if defined(OS_ANDROID) +// Disable overscroll edge effects like those found in Android views. +const char kDisableOverscrollEdgeEffect[] = "disable-overscroll-edge-effect"; + +// Disable the pull-to-refresh effect when vertically overscrolling content. +const char kDisablePullToRefreshEffect[] = "disable-pull-to-refresh-effect"; +#endif + #if defined(OS_MAC) // Disable use of AVFoundation to draw video content. const char kDisableAVFoundationOverlays[] = "disable-avfoundation-overlays"; diff --git a/chromium/ui/base/ui_base_switches.h b/chromium/ui/base/ui_base_switches.h index 07ce187cebd..bae5bce634c 100644 --- a/chromium/ui/base/ui_base_switches.h +++ b/chromium/ui/base/ui_base_switches.h @@ -12,6 +12,11 @@ namespace switches { +#if defined(OS_ANDROID) +COMPONENT_EXPORT(UI_BASE) extern const char kDisableOverscrollEdgeEffect[]; +COMPONENT_EXPORT(UI_BASE) extern const char kDisablePullToRefreshEffect[]; +#endif + #if defined(OS_MAC) COMPONENT_EXPORT(UI_BASE) extern const char kDisableAVFoundationOverlays[]; COMPONENT_EXPORT(UI_BASE) extern const char kDisableMacOverlays[]; diff --git a/chromium/ui/base/ui_base_switches_util.cc b/chromium/ui/base/ui_base_switches_util.cc index 1763af36824..c5d79875931 100644 --- a/chromium/ui/base/ui_base_switches_util.cc +++ b/chromium/ui/base/ui_base_switches_util.cc @@ -7,10 +7,33 @@ #include "base/command_line.h" #include "build/build_config.h" #include "build/chromeos_buildflags.h" +#include "ui/base/ui_base_features.h" #include "ui/base/ui_base_switches.h" +#if defined(OS_ANDROID) +#include "base/android/build_info.h" +#endif + namespace switches { +bool IsElasticOverscrollEnabled() { +// On macOS this value is adjusted in `UpdateScrollbarTheme()`, +// but the system default is true. +#if defined(OS_MAC) + return true; +#elif defined(OS_WIN) + return base::FeatureList::IsEnabled(features::kElasticOverscroll); +#elif defined(OS_ANDROID) + return base::android::BuildInfo::GetInstance()->sdk_int() >= + base::android::SDK_VERSION_S && + !base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kDisableOverscrollEdgeEffect) && + base::FeatureList::IsEnabled(features::kElasticOverscroll); +#else + return false; +#endif +} + bool IsTouchDragDropEnabled() { const auto* const command_line = base::CommandLine::ForCurrentProcess(); #if BUILDFLAG(IS_CHROMEOS_ASH) || defined(OS_ANDROID) diff --git a/chromium/ui/base/ui_base_switches_util.h b/chromium/ui/base/ui_base_switches_util.h index 9928e56708b..a5ae8ff3ab6 100644 --- a/chromium/ui/base/ui_base_switches_util.h +++ b/chromium/ui/base/ui_base_switches_util.h @@ -9,6 +9,7 @@ namespace switches { +COMPONENT_EXPORT(UI_BASE) bool IsElasticOverscrollEnabled(); COMPONENT_EXPORT(UI_BASE) bool IsTouchDragDropEnabled(); } // namespace switches diff --git a/chromium/ui/base/ui_base_types.h b/chromium/ui/base/ui_base_types.h index a74baa1c36e..2d03f5384be 100644 --- a/chromium/ui/base/ui_base_types.h +++ b/chromium/ui/base/ui_base_types.h @@ -94,6 +94,19 @@ enum MenuSourceType { MENU_SOURCE_TYPE_LAST = MENU_SOURCE_ADJUST_SELECTION_RESET }; +// Menu types that are used to position menu windows correctly. +enum class MenuType { + // A context menu opened either via a right click or a long tap. + kRootContextMenu = 0, + + // A root non-context menu. Example: The three dot menu. + kRootMenu, + + // A child menu opened by clicking on a nested menu entry of either + // |kRootContextMenu| or |kRootParentMenu|. + kChildMenu, +}; + COMPONENT_EXPORT(UI_BASE) MenuSourceType GetMenuSourceTypeForEvent(const ui::Event& event); diff --git a/chromium/ui/base/ui_features.gni b/chromium/ui/base/ui_features.gni index c664f73e57f..7865a568bbb 100644 --- a/chromium/ui/base/ui_features.gni +++ b/chromium/ui/base/ui_features.gni @@ -31,4 +31,4 @@ declare_args() { # AXPlatformNode to implement a native C++ API, instead it bridges to a Java API. has_platform_accessibility_support = has_native_accessibility || is_android -enable_hidpi = is_mac || is_win || is_linux || is_chromeos || is_ios +enable_hidpi = !is_android diff --git a/chromium/ui/base/user_activity/user_activity_detector.h b/chromium/ui/base/user_activity/user_activity_detector.h index caffb50fdf8..f87f25dd0ec 100644 --- a/chromium/ui/base/user_activity/user_activity_detector.h +++ b/chromium/ui/base/user_activity/user_activity_detector.h @@ -38,6 +38,9 @@ class COMPONENT_EXPORT(UI_BASE) UserActivityDetector std::string last_activity_name() const { return last_activity_name_; } void set_now_for_test(base::TimeTicks now) { now_for_test_ = now; } + void set_last_activity_time_for_test(base::TimeTicks value) { + last_activity_time_ = value; + } bool HasObserver(const UserActivityObserver* observer) const; void AddObserver(UserActivityObserver* observer); diff --git a/chromium/ui/base/webui/DIR_METADATA b/chromium/ui/base/webui/DIR_METADATA index cf1499073d6..f0076025212 100644 --- a/chromium/ui/base/webui/DIR_METADATA +++ b/chromium/ui/base/webui/DIR_METADATA @@ -1,10 +1,10 @@ # Metadata information for this directory. # # For more information on DIR_METADATA files, see: -# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md +# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/README.md # # For the schema of this file, see Metadata message: -# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto +# https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/tools/dirmd/proto/dir_metadata.proto monorail { component: "UI>Browser>WebUI" diff --git a/chromium/ui/base/webui/jstemplate_builder.cc b/chromium/ui/base/webui/jstemplate_builder.cc index db4026e6b16..536fbcb9e38 100644 --- a/chromium/ui/base/webui/jstemplate_builder.cc +++ b/chromium/ui/base/webui/jstemplate_builder.cc @@ -23,7 +23,7 @@ namespace { // Appends a script tag with a variable name |templateData| that has the JSON // assigned to it. -void AppendJsonHtml(const base::DictionaryValue* json, std::string* output) { +void AppendJsonHtml(const base::Value* json, std::string* output) { std::string javascript_string; AppendJsonJS(json, &javascript_string, /*from_js_module=*/false); @@ -72,7 +72,7 @@ void AppendJsTemplateSourceHtml(std::string* output) { // Appends the code that processes the JsTemplate with the JSON. You should // call AppendJsTemplateSourceHtml and AppendLoadTimeData before calling this. -void AppendJsTemplateProcessHtml(const base::DictionaryValue* json, +void AppendJsTemplateProcessHtml(const base::Value* json, const base::StringPiece& template_id, std::string* output) { std::string jstext; @@ -94,7 +94,7 @@ void AppendJsTemplateProcessHtml(const base::DictionaryValue* json, } // namespace std::string GetI18nTemplateHtml(const base::StringPiece& html_template, - const base::DictionaryValue* json) { + const base::Value* json) { ui::TemplateReplacements replacements; ui::TemplateReplacementsFromDictionaryValue(*json, &replacements); std::string output = @@ -107,7 +107,7 @@ std::string GetI18nTemplateHtml(const base::StringPiece& html_template, } std::string GetTemplatesHtml(const base::StringPiece& html_template, - const base::DictionaryValue* json, + const base::Value* json, const base::StringPiece& template_id) { ui::TemplateReplacements replacements; ui::TemplateReplacementsFromDictionaryValue(*json, &replacements); @@ -120,7 +120,7 @@ std::string GetTemplatesHtml(const base::StringPiece& html_template, return output; } -void AppendJsonJS(const base::DictionaryValue* json, +void AppendJsonJS(const base::Value* json, std::string* output, bool from_js_module) { // Convert the template data to a json string. diff --git a/chromium/ui/base/webui/jstemplate_builder.h b/chromium/ui/base/webui/jstemplate_builder.h index 97a4a558a2d..7d2613bc467 100644 --- a/chromium/ui/base/webui/jstemplate_builder.h +++ b/chromium/ui/base/webui/jstemplate_builder.h @@ -19,7 +19,7 @@ #include "base/strings/string_piece.h" namespace base { -class DictionaryValue; +class Value; } namespace webui { @@ -29,19 +29,19 @@ namespace webui { // full page with support for i18n Templates. COMPONENT_EXPORT(UI_BASE) std::string GetI18nTemplateHtml(const base::StringPiece& html_template, - const base::DictionaryValue* json); + const base::Value* json); // A helper function that generates a string of HTML to be loaded. The // string includes the HTML and the javascript code necessary to generate the // full page with support for both i18n Templates and JsTemplates. COMPONENT_EXPORT(UI_BASE) std::string GetTemplatesHtml(const base::StringPiece& html_template, - const base::DictionaryValue* json, + const base::Value* json, const base::StringPiece& template_id); // Assigns the given json data into |loadTimeData|, without a <script> tag. COMPONENT_EXPORT(UI_BASE) -void AppendJsonJS(const base::DictionaryValue* json, +void AppendJsonJS(const base::Value* json, std::string* output, bool from_js_module); diff --git a/chromium/ui/base/webui/web_ui_util.cc b/chromium/ui/base/webui/web_ui_util.cc index c6cd6de9ee1..b324acf5775 100644 --- a/chromium/ui/base/webui/web_ui_util.cc +++ b/chromium/ui/base/webui/web_ui_util.cc @@ -72,17 +72,13 @@ std::string GetPngDataUrl(const unsigned char* data, size_t size) { WindowOpenDisposition GetDispositionFromClick(const base::ListValue* args, int start_index) { - double button = 0.0; - bool alt_key = false; - bool ctrl_key = false; - bool meta_key = false; - bool shift_key = false; - - CHECK(args->GetDouble(start_index++, &button)); - CHECK(args->GetBoolean(start_index++, &alt_key)); - CHECK(args->GetBoolean(start_index++, &ctrl_key)); - CHECK(args->GetBoolean(start_index++, &meta_key)); - CHECK(args->GetBoolean(start_index++, &shift_key)); + base::Value::ConstListView list = args->GetList(); + double button = list[start_index].GetDouble(); + bool alt_key = list[start_index + 1].GetBool(); + bool ctrl_key = list[start_index + 2].GetBool(); + bool meta_key = list[start_index + 3].GetBool(); + bool shift_key = list[start_index + 4].GetBool(); + return ui::DispositionFromClick( button == 1.0, alt_key, ctrl_key, meta_key, shift_key); } @@ -184,11 +180,12 @@ void ParsePathAndScale(const GURL& url, } void SetLoadTimeDataDefaults(const std::string& app_locale, - base::DictionaryValue* localized_strings) { - localized_strings->SetString("fontfamily", GetFontFamily()); - localized_strings->SetString("fontsize", GetFontSize()); - localized_strings->SetString("language", l10n_util::GetLanguage(app_locale)); - localized_strings->SetString("textdirection", GetTextDirection()); + base::Value* localized_strings) { + localized_strings->SetStringKey("fontfamily", GetFontFamily()); + localized_strings->SetStringKey("fontsize", GetFontSize()); + localized_strings->SetStringKey("language", + l10n_util::GetLanguage(app_locale)); + localized_strings->SetStringKey("textdirection", GetTextDirection()); } void SetLoadTimeDataDefaults(const std::string& app_locale, diff --git a/chromium/ui/base/webui/web_ui_util.h b/chromium/ui/base/webui/web_ui_util.h index a0c1ca2fee6..8ac209ad5ac 100644 --- a/chromium/ui/base/webui/web_ui_util.h +++ b/chromium/ui/base/webui/web_ui_util.h @@ -67,7 +67,7 @@ void ParsePathAndScale(const GURL& url, std::string* path, float* scale_factor); // application locale (i.e. g_browser_process->GetApplicationLocale()). COMPONENT_EXPORT(UI_BASE) void SetLoadTimeDataDefaults(const std::string& app_locale, - base::DictionaryValue* localized_strings); + base::Value* localized_strings); COMPONENT_EXPORT(UI_BASE) void SetLoadTimeDataDefaults(const std::string& app_locale, ui::TemplateReplacements* replacements); diff --git a/chromium/ui/base/win/event_creation_utils.cc b/chromium/ui/base/win/event_creation_utils.cc index 097f5d6b530..636608e76ce 100644 --- a/chromium/ui/base/win/event_creation_utils.cc +++ b/chromium/ui/base/win/event_creation_utils.cc @@ -9,7 +9,7 @@ #include <algorithm> -#include "base/numerics/ranges.h" +#include "base/cxx17_backports.h" #include "ui/gfx/geometry/point.h" namespace ui { @@ -20,8 +20,8 @@ bool SendMouseEvent(const gfx::Point& point, int flags) { // coordinates required by SendInput. const int screen_width = ::GetSystemMetrics(SM_CXSCREEN); const int screen_height = ::GetSystemMetrics(SM_CYSCREEN); - int screen_x = base::ClampToRange(point.x(), 0, screen_width - 1); - int screen_y = base::ClampToRange(point.y(), 0, screen_height - 1); + int screen_x = base::clamp(point.x(), 0, screen_width - 1); + int screen_y = base::clamp(point.y(), 0, screen_height - 1); // In normalized absolute coordinates, (0, 0) maps onto the upper-left corner // of the display surface, while (65535, 65535) maps onto the lower-right diff --git a/chromium/ui/base/x/BUILD.gn b/chromium/ui/base/x/BUILD.gn index 5dc75c90901..bfe340a3f97 100644 --- a/chromium/ui/base/x/BUILD.gn +++ b/chromium/ui/base/x/BUILD.gn @@ -84,6 +84,7 @@ component("x") { "//ui/base/cursor:theme_manager", "//ui/base/cursor/mojom:cursor_type", "//ui/display/util", + "//ui/display/util:gpu_info_util", "//ui/events", "//ui/events:dom_keycode_converter", "//ui/events/devices/x11", diff --git a/chromium/ui/base/x/selection_requestor.cc b/chromium/ui/base/x/selection_requestor.cc index 2fd537c1821..a5687014cd6 100644 --- a/chromium/ui/base/x/selection_requestor.cc +++ b/chromium/ui/base/x/selection_requestor.cc @@ -9,6 +9,7 @@ #include "base/memory/ref_counted_memory.h" #include "ui/base/x/selection_owner.h" #include "ui/base/x/selection_utils.h" +#include "ui/base/x/x11_clipboard_helper.h" #include "ui/base/x/x11_util.h" #include "ui/gfx/x/x11_atom_cache.h" #include "ui/gfx/x/xproto.h" @@ -40,8 +41,11 @@ std::vector<uint8_t> CombineData( } // namespace -SelectionRequestor::SelectionRequestor(x11::Window x_window) - : x_window_(x_window), x_property_(x11::GetAtom(kChromeSelection)) {} +SelectionRequestor::SelectionRequestor(x11::Window x_window, + XClipboardHelper* helper) + : x_window_(x_window), + helper_(helper), + x_property_(x11::GetAtom(kChromeSelection)) {} SelectionRequestor::~SelectionRequestor() = default; @@ -224,17 +228,8 @@ void SelectionRequestor::BlockTillSelectionNotifyForRequest(Request* request) { size_t events_size = events.size(); for (; i < events_size; ++i) { auto& event = events[i]; - if (auto* notify = event.As<x11::SelectionNotifyEvent>()) { - if (notify->requestor == x_window_) { - OnSelectionNotify(*notify); - event = x11::Event(); - } - } else if (auto* prop = event.As<x11::PropertyNotifyEvent>()) { - if (CanDispatchPropertyEvent(*prop)) { - OnPropertyEvent(*prop); - event = x11::Event(); - } - } + if (helper_->DispatchEvent(event)) + event = x11::Event(); } DCHECK_EQ(events_size, events.size()); } diff --git a/chromium/ui/base/x/selection_requestor.h b/chromium/ui/base/x/selection_requestor.h index cd7b2e41d64..3d318f6a62c 100644 --- a/chromium/ui/base/x/selection_requestor.h +++ b/chromium/ui/base/x/selection_requestor.h @@ -15,6 +15,7 @@ namespace ui { class SelectionData; +class XClipboardHelper; // Requests and later receives data from the X11 server through the selection // system. @@ -25,7 +26,7 @@ class SelectionData; // implement per-component fast-paths. class COMPONENT_EXPORT(UI_BASE_X) SelectionRequestor { public: - explicit SelectionRequestor(x11::Window xwindow); + SelectionRequestor(x11::Window xwindow, XClipboardHelper* helper); SelectionRequestor(const SelectionRequestor&) = delete; SelectionRequestor& operator=(const SelectionRequestor&) = delete; ~SelectionRequestor(); @@ -112,6 +113,9 @@ class COMPONENT_EXPORT(UI_BASE_X) SelectionRequestor { // Our X11 state. const x11::Window x_window_; + // Not owned. + XClipboardHelper* const helper_; + // The property on |x_window_| set by the selection owner with the value of // the selection. const x11::Atom x_property_; diff --git a/chromium/ui/base/x/selection_requestor_unittest.cc b/chromium/ui/base/x/selection_requestor_unittest.cc index 0bffa98afab..ae177ba4acf 100644 --- a/chromium/ui/base/x/selection_requestor_unittest.cc +++ b/chromium/ui/base/x/selection_requestor_unittest.cc @@ -15,6 +15,7 @@ #include "base/threading/thread_task_runner_handle.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/x/selection_utils.h" +#include "ui/base/x/x11_clipboard_helper.h" #include "ui/base/x/x11_util.h" #include "ui/gfx/x/connection.h" #include "ui/gfx/x/event.h" @@ -52,11 +53,14 @@ class SelectionRequestorTest : public testing::Test { void SetUp() override { // Create a window for the selection requestor to use. x_window_ = x11::CreateDummyWindow(); - requestor_ = std::make_unique<SelectionRequestor>(x_window_); + helper_ = std::make_unique<XClipboardHelper>( + base::BindRepeating([](ClipboardBuffer buffer) {})); + requestor_ = helper_->GetSelectionRequestorForTest(); } void TearDown() override { - requestor_.reset(); + helper_.reset(); + requestor_ = nullptr; connection_->DestroyWindow({x_window_}); } @@ -65,7 +69,8 @@ class SelectionRequestorTest : public testing::Test { // |requestor_|'s window. x11::Window x_window_ = x11::Window::None; - std::unique_ptr<SelectionRequestor> requestor_; + std::unique_ptr<XClipboardHelper> helper_; + SelectionRequestor* requestor_ = nullptr; base::test::SingleThreadTaskEnvironment task_environment_{ base::test::SingleThreadTaskEnvironment::MainThreadType::UI}; @@ -107,7 +112,7 @@ TEST_F(SelectionRequestorTest, DISABLED_NestedRequests) { base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(&PerformBlockingConvertSelection, - base::Unretained(requestor_.get()), selection, + base::Unretained(requestor_), selection, target2, "Data2")); base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, @@ -117,8 +122,7 @@ TEST_F(SelectionRequestorTest, DISABLED_NestedRequests) { FROM_HERE, base::BindOnce(&SelectionRequestorTest::SendSelectionNotify, base::Unretained(this), selection, target2, "Data2")); - PerformBlockingConvertSelection(requestor_.get(), selection, target1, - "Data1"); + PerformBlockingConvertSelection(requestor_, selection, target1, "Data1"); } } // namespace ui diff --git a/chromium/ui/base/x/selection_utils.cc b/chromium/ui/base/x/selection_utils.cc index c6799bf083b..382918f5d04 100644 --- a/chromium/ui/base/x/selection_utils.cc +++ b/chromium/ui/base/x/selection_utils.cc @@ -22,25 +22,21 @@ namespace ui { std::vector<x11::Atom> GetTextAtomsFrom() { - std::vector<x11::Atom> atoms; - atoms.push_back(x11::GetAtom(kMimeTypeLinuxUtf8String)); - atoms.push_back(x11::GetAtom(kMimeTypeLinuxString)); - atoms.push_back(x11::GetAtom(kMimeTypeLinuxText)); - atoms.push_back(x11::GetAtom(kMimeTypeText)); - atoms.push_back(x11::GetAtom(kMimeTypeTextUtf8)); + static const std::vector<x11::Atom> atoms = { + x11::GetAtom(kMimeTypeLinuxUtf8String), + x11::GetAtom(kMimeTypeLinuxString), x11::GetAtom(kMimeTypeLinuxText), + x11::GetAtom(kMimeTypeText), x11::GetAtom(kMimeTypeTextUtf8)}; return atoms; } std::vector<x11::Atom> GetURLAtomsFrom() { - std::vector<x11::Atom> atoms; - atoms.push_back(x11::GetAtom(kMimeTypeURIList)); - atoms.push_back(x11::GetAtom(kMimeTypeMozillaURL)); + static const std::vector<x11::Atom> atoms = { + x11::GetAtom(kMimeTypeURIList), x11::GetAtom(kMimeTypeMozillaURL)}; return atoms; } std::vector<x11::Atom> GetURIListAtomsFrom() { - std::vector<x11::Atom> atoms; - atoms.push_back(x11::GetAtom(kMimeTypeURIList)); + static const std::vector<x11::Atom> atoms = {x11::GetAtom(kMimeTypeURIList)}; return atoms; } diff --git a/chromium/ui/base/x/visual_picker_glx.cc b/chromium/ui/base/x/visual_picker_glx.cc index 349598fdcd2..fccf1a216be 100644 --- a/chromium/ui/base/x/visual_picker_glx.cc +++ b/chromium/ui/base/x/visual_picker_glx.cc @@ -10,8 +10,8 @@ #include <numeric> #include <vector> +#include "base/cxx17_backports.h" #include "base/memory/singleton.h" -#include "base/stl_util.h" #include "ui/gfx/x/future.h" // These constants are obtained from GL/glx.h and GL/glxext.h. @@ -215,7 +215,8 @@ void VisualPickerGlx::FillConfigMap() { std::make_unique<base::flat_map<gfx::BufferFormat, x11::Glx::FbConfig>>(); if (auto configs = connection_->glx() - .GetFBConfigs({connection_->DefaultScreenId()}) + .GetFBConfigs({static_cast<uint32_t>( + connection_->DefaultScreenId())}) .Sync()) { const auto n_cfgs = configs->num_FB_configs; const auto n_props = configs->num_properties; @@ -286,9 +287,11 @@ void VisualPickerGlx::FillConfigMap() { } VisualPickerGlx::VisualPickerGlx() : connection_(x11::Connection::Get()) { - auto configs = connection_->glx() - .GetVisualConfigs({connection_->DefaultScreenId()}) - .Sync(); + auto configs = + connection_->glx() + .GetVisualConfigs( + {static_cast<uint32_t>(connection_->DefaultScreenId())}) + .Sync(); if (configs) { system_visual_ = PickBestSystemVisual(*configs.reply); diff --git a/chromium/ui/base/x/x11_clipboard_helper.cc b/chromium/ui/base/x/x11_clipboard_helper.cc index 04ce85ab9e3..20b359a2977 100644 --- a/chromium/ui/base/x/x11_clipboard_helper.cc +++ b/chromium/ui/base/x/x11_clipboard_helper.cc @@ -122,7 +122,7 @@ class XClipboardHelper::TargetList { bool ContainsText() const { for (const auto& atom : GetTextAtomsFrom()) { - if (ContainsAtom(atom)) + if (base::Contains(target_list_, atom)) return true; } return false; @@ -130,10 +130,6 @@ class XClipboardHelper::TargetList { bool ContainsFormat(const ClipboardFormatType& format_type) const { x11::Atom atom = x11::GetAtom(format_type.GetName().c_str()); - return ContainsAtom(atom); - } - - bool ContainsAtom(x11::Atom atom) const { return base::Contains(target_list_, atom); } @@ -146,7 +142,8 @@ XClipboardHelper::XClipboardHelper( : connection_(x11::Connection::Get()), x_root_window_(ui::GetX11RootWindow()), x_window_(x11::CreateDummyWindow("Chromium Clipboard Window")), - selection_requestor_(std::make_unique<SelectionRequestor>(x_window_)), + selection_requestor_( + std::make_unique<SelectionRequestor>(x_window_, this)), clipboard_owner_(connection_, x_window_, x11::GetAtom(kClipboard)), primary_owner_(connection_, x_window_, x11::Atom::PRIMARY) { DCHECK(selection_requestor_); @@ -217,18 +214,15 @@ std::vector<std::string> XClipboardHelper::GetAvailableTypes( if (target_list.ContainsText()) available_types.push_back(kMimeTypeText); - if (target_list.ContainsFormat(ClipboardFormatType::GetHtmlType())) + if (target_list.ContainsFormat(ClipboardFormatType::HtmlType())) available_types.push_back(kMimeTypeHTML); - if (target_list.ContainsFormat(ClipboardFormatType::GetRtfType())) + if (target_list.ContainsFormat(ClipboardFormatType::RtfType())) available_types.push_back(kMimeTypeRTF); - if (target_list.ContainsFormat(ClipboardFormatType::GetBitmapType())) + if (target_list.ContainsFormat(ClipboardFormatType::BitmapType())) available_types.push_back(kMimeTypePNG); - // Only support filenames if chrome://flags#clipboard-filenames is enabled. - if (target_list.ContainsFormat(ClipboardFormatType::GetFilenamesType()) && - base::FeatureList::IsEnabled(features::kClipboardFilenames)) { + if (target_list.ContainsFormat(ClipboardFormatType::FilenamesType())) available_types.push_back(kMimeTypeURIList); - } - if (target_list.ContainsFormat(ClipboardFormatType::GetWebCustomDataType())) + if (target_list.ContainsFormat(ClipboardFormatType::WebCustomDataType())) available_types.push_back(kMimeTypeWebCustomData); return available_types; @@ -259,8 +253,8 @@ std::vector<std::string> XClipboardHelper::GetAvailableAtomNames( bool XClipboardHelper::IsFormatAvailable(ClipboardBuffer buffer, const ClipboardFormatType& format) { auto target_list = GetTargetList(buffer); - if (format == ClipboardFormatType::GetPlainTextType() || - format == ClipboardFormatType::GetUrlType()) { + if (format == ClipboardFormatType::PlainTextType() || + format == ClipboardFormatType::UrlType()) { return target_list.ContainsText(); } return target_list.ContainsFormat(format); @@ -353,10 +347,10 @@ XClipboardHelper::TargetList XClipboardHelper::GetTargetList( return XClipboardHelper::TargetList(out); } -void XClipboardHelper::OnEvent(const x11::Event& xev) { +bool XClipboardHelper::DispatchEvent(const x11::Event& xev) { if (auto* request = xev.As<x11::SelectionRequestEvent>()) { if (request->owner != x_window_) - return; + return false; if (request->selection == x11::Atom::PRIMARY) { primary_owner_.OnSelectionRequest(*request); } else { @@ -368,9 +362,11 @@ void XClipboardHelper::OnEvent(const x11::Event& xev) { } else if (auto* notify = xev.As<x11::SelectionNotifyEvent>()) { if (notify->requestor == x_window_) selection_requestor_->OnSelectionNotify(*notify); + else + return false; } else if (auto* clear = xev.As<x11::SelectionClearEvent>()) { if (clear->owner != x_window_) - return; + return false; if (clear->selection == x11::Atom::PRIMARY) { primary_owner_.OnSelectionClear(*clear); } else { @@ -382,11 +378,24 @@ void XClipboardHelper::OnEvent(const x11::Event& xev) { } else if (auto* prop = xev.As<x11::PropertyNotifyEvent>()) { if (primary_owner_.CanDispatchPropertyEvent(*prop)) primary_owner_.OnPropertyEvent(*prop); - if (clipboard_owner_.CanDispatchPropertyEvent(*prop)) + else if (clipboard_owner_.CanDispatchPropertyEvent(*prop)) clipboard_owner_.OnPropertyEvent(*prop); - if (selection_requestor_->CanDispatchPropertyEvent(*prop)) + else if (selection_requestor_->CanDispatchPropertyEvent(*prop)) selection_requestor_->OnPropertyEvent(*prop); + else + return false; + } else { + return false; } + return true; +} + +SelectionRequestor* XClipboardHelper::GetSelectionRequestorForTest() { + return selection_requestor_.get(); +} + +void XClipboardHelper::OnEvent(const x11::Event& xev) { + DispatchEvent(xev); } x11::Atom XClipboardHelper::LookupSelectionForClipboardBuffer( diff --git a/chromium/ui/base/x/x11_clipboard_helper.h b/chromium/ui/base/x/x11_clipboard_helper.h index 79ce9f4f47a..98f5b6e9825 100644 --- a/chromium/ui/base/x/x11_clipboard_helper.h +++ b/chromium/ui/base/x/x11_clipboard_helper.h @@ -91,6 +91,11 @@ class COMPONENT_EXPORT(UI_BASE_X) XClipboardHelper : public x11::EventObserver { // ownership of it. void StoreCopyPasteDataAndWait(); + // Returns true if the event was handled. + bool DispatchEvent(const x11::Event& xev); + + SelectionRequestor* GetSelectionRequestorForTest(); + private: class TargetList; diff --git a/chromium/ui/base/x/x11_cursor_loader.cc b/chromium/ui/base/x/x11_cursor_loader.cc index 90a486cd8cf..a69f7b4a3f3 100644 --- a/chromium/ui/base/x/x11_cursor_loader.cc +++ b/chromium/ui/base/x/x11_cursor_loader.cc @@ -11,6 +11,7 @@ #include "base/bind.h" #include "base/compiler_specific.h" +#include "base/cxx17_backports.h" #include "base/environment.h" #include "base/files/file_path.h" #include "base/files/file_util.h" @@ -18,7 +19,6 @@ #include "base/memory/scoped_refptr.h" #include "base/no_destructor.h" #include "base/sequence_checker.h" -#include "base/stl_util.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_piece_forward.h" #include "base/strings/string_split.h" @@ -351,7 +351,8 @@ scoped_refptr<X11Cursor> XCursorLoader::CreateCursor( auto cursor = CreateCursor(image.bitmap, image.hotspot); cursors.push_back(cursor); elements.push_back(x11::Render::AnimationCursorElement{ - cursor->xcursor_, image.frame_delay.InMilliseconds()}); + cursor->xcursor_, + static_cast<uint32_t>(image.frame_delay.InMilliseconds())}); } if (elements.empty()) @@ -370,8 +371,8 @@ scoped_refptr<X11Cursor> XCursorLoader::CreateCursor( DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); auto pixmap = connection_->GenerateId<x11::Pixmap>(); auto gc = connection_->GenerateId<x11::GraphicsContext>(); - int width = bitmap.width(); - int height = bitmap.height(); + uint16_t width = bitmap.width(); + uint16_t height = bitmap.height(); connection_->CreatePixmap( {32, pixmap, connection_->default_root(), width, height}); connection_->CreateGC({gc, pixmap}); @@ -395,7 +396,9 @@ scoped_refptr<X11Cursor> XCursorLoader::CreateCursor( connection_->render().CreatePicture({pic, pixmap, pict_format_}); auto cursor = connection_->GenerateId<x11::Cursor>(); - connection_->render().CreateCursor({cursor, pic, hotspot.x(), hotspot.y()}); + connection_->render().CreateCursor({cursor, pic, + static_cast<uint16_t>(hotspot.x()), + static_cast<uint16_t>(hotspot.y())}); connection_->render().FreePicture({pic}); connection_->FreePixmap({pixmap}); @@ -417,10 +420,12 @@ void XCursorLoader::LoadCursorImpl( auto core_char = CursorNamesToChar(names); constexpr uint16_t kFontCursorFgColor = 0; constexpr uint16_t kFontCursorBgColor = 65535; - connection_->CreateGlyphCursor( - {xcursor, cursor_font_, cursor_font_, 2 * core_char, 2 * core_char + 1, - kFontCursorFgColor, kFontCursorFgColor, kFontCursorFgColor, - kFontCursorBgColor, kFontCursorBgColor, kFontCursorBgColor}); + connection_->CreateGlyphCursor({xcursor, cursor_font_, cursor_font_, + static_cast<uint16_t>(2 * core_char), + static_cast<uint16_t>(2 * core_char + 1), + kFontCursorFgColor, kFontCursorFgColor, + kFontCursorFgColor, kFontCursorBgColor, + kFontCursorBgColor, kFontCursorBgColor}); } cursor->SetCursor(xcursor); } diff --git a/chromium/ui/base/x/x11_display_util.cc b/chromium/ui/base/x/x11_display_util.cc index ac0501960d6..37d1378028e 100644 --- a/chromium/ui/base/x/x11_display_util.cc +++ b/chromium/ui/base/x/x11_display_util.cc @@ -11,6 +11,7 @@ #include "base/bits.h" #include "base/compiler_specific.h" #include "base/logging.h" +#include "base/strings/string_util.h" #include "ui/base/x/x11_util.h" #include "ui/display/util/display_util.h" #include "ui/display/util/edid_parser.h" @@ -248,7 +249,7 @@ std::vector<display::Display> BuildDisplaysFromXRandRInfo( GetEDIDProperty(&randr, static_cast<x11::RandR::Output>(output_id))); auto output_32 = static_cast<uint32_t>(output_id); int64_t display_id = - output_32 > 0xff ? 0 : edid_parser.GetDisplayId(output_32); + output_32 > 0xff ? 0 : edid_parser.GetIndexBasedDisplayId(output_32); // It isn't ideal, but if we can't parse the EDID data, fall back on the // display number. if (!display_id) @@ -284,6 +285,10 @@ std::vector<display::Display> BuildDisplaysFromXRandRInfo( if (is_primary_display) explicit_primary_display_index = displays.size(); + const std::string name(output_info->name.begin(), output_info->name.end()); + if (base::StartsWith(name, "eDP") || base::StartsWith(name, "LVDS")) + display::Display::SetInternalDisplayId(display_id); + auto monitor_iter = output_to_monitor.find(static_cast<x11::RandR::Output>(output_id)); if (monitor_iter != output_to_monitor.end() && monitor_iter->second == 0) diff --git a/chromium/ui/base/x/x11_drag_drop_client.cc b/chromium/ui/base/x/x11_drag_drop_client.cc index c96e99425d5..0bddb3e9193 100644 --- a/chromium/ui/base/x/x11_drag_drop_client.cc +++ b/chromium/ui/base/x/x11_drag_drop_client.cc @@ -13,6 +13,7 @@ #include "ui/base/dragdrop/os_exchange_data.h" #include "ui/base/x/x11_os_exchange_data_provider.h" #include "ui/base/x/x11_util.h" +#include "ui/events/event_constants.h" #include "ui/gfx/x/connection.h" #include "ui/gfx/x/x11_atom_cache.h" #include "ui/gfx/x/xproto.h" diff --git a/chromium/ui/base/x/x11_gl_egl_utility.cc b/chromium/ui/base/x/x11_gl_egl_utility.cc index 624c6f9483b..8464e449cde 100644 --- a/chromium/ui/base/x/x11_gl_egl_utility.cc +++ b/chromium/ui/base/x/x11_gl_egl_utility.cc @@ -39,8 +39,20 @@ namespace ui { void GetPlatformExtraDisplayAttribs(EGLenum platform_type, std::vector<EGLAttrib>* attributes) { - attributes->push_back(EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE); - attributes->push_back(EGL_PLATFORM_X11_EXT); + // ANGLE_NULL and SwiftShader backends don't use the visual, + // and may run without X11 where we can't get it anyway. + if ((platform_type != EGL_PLATFORM_ANGLE_TYPE_NULL_ANGLE) && + (std::find(attributes->begin(), attributes->end(), + EGL_PLATFORM_ANGLE_DEVICE_TYPE_SWIFTSHADER_ANGLE) == + attributes->end())) { + x11::VisualId visual_id; + XVisualManager::GetInstance()->ChooseVisualForWindow( + true, &visual_id, nullptr, nullptr, nullptr); + attributes->push_back(EGL_X11_VISUAL_ID_ANGLE); + attributes->push_back(static_cast<EGLAttrib>(visual_id)); + attributes->push_back(EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE); + attributes->push_back(EGL_PLATFORM_X11_EXT); + } } void ChoosePlatformCustomAlphaAndBufferSize(EGLint* alpha_size, diff --git a/chromium/ui/base/x/x11_global_shortcut_listener.cc b/chromium/ui/base/x/x11_global_shortcut_listener.cc index 658c04c865f..a772666160a 100644 --- a/chromium/ui/base/x/x11_global_shortcut_listener.cc +++ b/chromium/ui/base/x/x11_global_shortcut_listener.cc @@ -7,7 +7,7 @@ #include <stddef.h> #include "base/containers/contains.h" -#include "base/stl_util.h" +#include "base/cxx17_backports.h" #include "ui/base/x/x11_util.h" #include "ui/events/event.h" #include "ui/events/keycodes/keyboard_code_conversion_x.h" diff --git a/chromium/ui/base/x/x11_keyboard_hook.cc b/chromium/ui/base/x/x11_keyboard_hook.cc index 5877761a6e5..7ad97dcc175 100644 --- a/chromium/ui/base/x/x11_keyboard_hook.cc +++ b/chromium/ui/base/x/x11_keyboard_hook.cc @@ -8,7 +8,6 @@ #include <utility> #include "base/check_op.h" -#include "base/stl_util.h" #include "ui/events/event.h" #include "ui/events/keycodes/dom/dom_code.h" #include "ui/events/keycodes/dom/keycode_converter.h" diff --git a/chromium/ui/base/x/x11_menu_registrar.h b/chromium/ui/base/x/x11_menu_registrar.h index 69f0e4d56f2..76d0b190022 100644 --- a/chromium/ui/base/x/x11_menu_registrar.h +++ b/chromium/ui/base/x/x11_menu_registrar.h @@ -8,7 +8,6 @@ #include <stdint.h> #include <memory> -#include <string> #include <vector> #include "base/component_export.h" diff --git a/chromium/ui/base/x/x11_move_loop_delegate.h b/chromium/ui/base/x/x11_move_loop_delegate.h index a9ac2330937..b78e38749e5 100644 --- a/chromium/ui/base/x/x11_move_loop_delegate.h +++ b/chromium/ui/base/x/x11_move_loop_delegate.h @@ -5,6 +5,10 @@ #ifndef UI_BASE_X_X11_MOVE_LOOP_DELEGATE_H_ #define UI_BASE_X_X11_MOVE_LOOP_DELEGATE_H_ +namespace base { +class TimeTicks; +} + namespace gfx { class Point; } diff --git a/chromium/ui/base/x/x11_os_exchange_data_provider.cc b/chromium/ui/base/x/x11_os_exchange_data_provider.cc index cbf6e7f50b8..d3f24a6dce7 100644 --- a/chromium/ui/base/x/x11_os_exchange_data_provider.cc +++ b/chromium/ui/base/x/x11_os_exchange_data_provider.cc @@ -6,6 +6,7 @@ #include <utility> +#include "base/containers/contains.h" #include "base/memory/ref_counted_memory.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" diff --git a/chromium/ui/base/x/x11_screensaver.cc b/chromium/ui/base/x/x11_screensaver.cc index 32d74ffe1c9..7f3d6acec72 100644 --- a/chromium/ui/base/x/x11_screensaver.cc +++ b/chromium/ui/base/x/x11_screensaver.cc @@ -12,6 +12,7 @@ #include "ui/base/x/x11_util.h" #include "ui/gfx/switches.h" #include "ui/gfx/x/connection.h" +#include "ui/gfx/x/event.h" #include "ui/gfx/x/property_cache.h" #include "ui/gfx/x/screensaver.h" #include "ui/gfx/x/x11_atom_cache.h" diff --git a/chromium/ui/base/x/x11_shm_image_pool.cc b/chromium/ui/base/x/x11_shm_image_pool.cc index c00f6468fb4..16f50244c14 100644 --- a/chromium/ui/base/x/x11_shm_image_pool.cc +++ b/chromium/ui/base/x/x11_shm_image_pool.cc @@ -194,7 +194,7 @@ bool XShmImagePool::Resize(const gfx::Size& pixel_size) { auto shmseg = connection_->GenerateId<x11::Shm::Seg>(); auto req = connection_->shm().Attach({ .shmseg = shmseg, - .shmid = state.shmid, + .shmid = static_cast<uint32_t>(state.shmid), // If this class ever needs to use XShmGetImage(), this needs to be // changed to read-write. .read_only = true, diff --git a/chromium/ui/base/x/x11_software_bitmap_presenter.cc b/chromium/ui/base/x/x11_software_bitmap_presenter.cc index 9f35bb15cc4..567a8cabe8e 100644 --- a/chromium/ui/base/x/x11_software_bitmap_presenter.cc +++ b/chromium/ui/base/x/x11_software_bitmap_presenter.cc @@ -63,29 +63,35 @@ bool X11SoftwareBitmapPresenter::CompositeBitmap(x11::Connection* connection, int depth, x11::GraphicsContext gc, const void* data) { - connection->ClearArea({false, widget, x, y, width, height}); + int16_t x_i16 = x; + int16_t y_i16 = y; + uint16_t w_u16 = width; + uint16_t h_u16 = height; + uint8_t d_u8 = depth; + connection->ClearArea({false, widget, x_i16, y_i16, w_u16, h_u16}); constexpr auto kAllPlanes = std::numeric_limits<decltype(x11::GetImageRequest::plane_mask)>::max(); scoped_refptr<base::RefCountedMemory> bg; - auto req = connection->GetImage( - {x11::ImageFormat::ZPixmap, widget, x, y, width, height, kAllPlanes}); + auto req = connection->GetImage({x11::ImageFormat::ZPixmap, widget, x_i16, + y_i16, w_u16, h_u16, kAllPlanes}); if (auto reply = req.Sync()) { bg = reply->data; } else { auto pixmap_id = connection->GenerateId<x11::Pixmap>(); - connection->CreatePixmap({depth, pixmap_id, widget, width, height}); + connection->CreatePixmap({d_u8, pixmap_id, widget, w_u16, h_u16}); ScopedPixmap pixmap(connection, pixmap_id); connection->ChangeGC(x11::ChangeGCRequest{ .gc = gc, .subwindow_mode = x11::SubwindowMode::IncludeInferiors}); - connection->CopyArea({widget, pixmap_id, gc, x, y, 0, 0, width, height}); + connection->CopyArea( + {widget, pixmap_id, gc, x_i16, y_i16, 0, 0, w_u16, h_u16}); connection->ChangeGC(x11::ChangeGCRequest{ .gc = gc, .subwindow_mode = x11::SubwindowMode::ClipByChildren}); - auto req = connection->GetImage({x11::ImageFormat::ZPixmap, pixmap_id, 0, 0, - width, height, kAllPlanes}); + auto req = connection->GetImage( + {x11::ImageFormat::ZPixmap, pixmap_id, 0, 0, w_u16, h_u16, kAllPlanes}); if (auto reply = req.Sync()) bg = reply->data; else @@ -109,8 +115,8 @@ bool X11SoftwareBitmapPresenter::CompositeBitmap(x11::Connection* connection, canvas.drawImage(fg_bitmap.asImage(), 0, 0); canvas.flush(); - connection->PutImage({x11::ImageFormat::ZPixmap, widget, gc, width, height, x, - y, 0, depth, bg}); + connection->PutImage({x11::ImageFormat::ZPixmap, widget, gc, w_u16, h_u16, + x_i16, y_i16, 0, d_u8, bg}); return true; } @@ -203,15 +209,17 @@ void X11SoftwareBitmapPresenter::EndPaint(const gfx::Rect& damage_rect) { x11::Shm::PutImageRequest put_image_request{ .drawable = widget_, .gc = gc_, - .total_width = shm_pool_->CurrentBitmap().width(), - .total_height = shm_pool_->CurrentBitmap().height(), - .src_x = rect.x(), - .src_y = rect.y(), - .src_width = rect.width(), - .src_height = rect.height(), - .dst_x = rect.x(), - .dst_y = rect.y(), - .depth = depth_, + .total_width = + static_cast<uint16_t>(shm_pool_->CurrentBitmap().width()), + .total_height = + static_cast<uint16_t>(shm_pool_->CurrentBitmap().height()), + .src_x = static_cast<uint16_t>(rect.x()), + .src_y = static_cast<uint16_t>(rect.y()), + .src_width = static_cast<uint16_t>(rect.width()), + .src_height = static_cast<uint16_t>(rect.height()), + .dst_x = static_cast<int16_t>(rect.x()), + .dst_y = static_cast<int16_t>(rect.y()), + .depth = static_cast<uint8_t>(depth_), .format = x11::ImageFormat::ZPixmap, .send_event = enable_multibuffering_, .shmseg = shm_pool_->CurrentSegment(), diff --git a/chromium/ui/base/x/x11_util.cc b/chromium/ui/base/x/x11_util.cc index e96116b2d94..47bc0b93c92 100644 --- a/chromium/ui/base/x/x11_util.cc +++ b/chromium/ui/base/x/x11_util.cc @@ -14,63 +14,41 @@ #include <bitset> #include <limits> -#include <list> -#include <map> #include <utility> #include <vector> -#include "base/bind.h" #include "base/command_line.h" -#include "base/compiler_specific.h" #include "base/containers/contains.h" -#include "base/environment.h" #include "base/location.h" #include "base/logging.h" #include "base/memory/ref_counted_memory.h" #include "base/memory/scoped_refptr.h" #include "base/memory/singleton.h" -#include "base/metrics/histogram_macros.h" -#include "base/no_destructor.h" #include "base/notreached.h" #include "base/single_thread_task_runner.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" -#include "base/sys_byteorder.h" -#include "base/task/current_thread.h" -#include "base/threading/thread.h" -#include "base/threading/thread_local_storage.h" -#include "base/threading/thread_task_runner_handle.h" #include "base/trace_event/trace_event.h" +#include "base/values.h" #include "build/build_config.h" #include "build/chromeos_buildflags.h" -#include "skia/ext/image_operations.h" #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkImageInfo.h" -#include "third_party/skia/include/core/SkTypes.h" #include "ui/base/cursor/mojom/cursor_type.mojom-shared.h" #include "ui/base/x/visual_picker_glx.h" -#include "ui/base/x/x11_cursor.h" -#include "ui/base/x/x11_cursor_loader.h" -#include "ui/base/x/x11_menu_list.h" +#include "ui/display/util/gpu_info_util.h" #include "ui/events/devices/x11/device_data_manager_x11.h" #include "ui/events/devices/x11/touch_factory_x11.h" -#include "ui/events/event_utils.h" -#include "ui/events/keycodes/keyboard_code_conversion_x.h" -#include "ui/gfx/canvas.h" #include "ui/gfx/geometry/insets.h" #include "ui/gfx/geometry/point.h" -#include "ui/gfx/geometry/point_conversions.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/size.h" #include "ui/gfx/image/image_skia.h" -#include "ui/gfx/image/image_skia_rep.h" -#include "ui/gfx/skia_util.h" #include "ui/gfx/switches.h" #include "ui/gfx/x/connection.h" #include "ui/gfx/x/screensaver.h" #include "ui/gfx/x/shm.h" -#include "ui/gfx/x/sync.h" #include "ui/gfx/x/x11_atom_cache.h" #include "ui/gfx/x/xproto.h" #include "ui/gfx/x/xproto_util.h" @@ -81,14 +59,6 @@ #endif namespace ui { - -class TLSDestructionCheckerForX11 { - public: - static bool HasBeenDestroyed() { - return base::ThreadLocalStorage::HasBeenDestroyed(); - } -}; - namespace { // Constants that are part of EWMH. @@ -163,13 +133,65 @@ bool IsX11ScreenSaverAvailable() { version->server_minor_version >= 1)); } -// Must be in sync with the copy in //content/browser/gpu/gpu_internals_ui.cc. -base::Value NewDescriptionValuePair(base::StringPiece desc, - base::StringPiece value) { - base::Value dict(base::Value::Type::DICTIONARY); - dict.SetKey("description", base::Value(desc)); - dict.SetKey("value", base::Value(value)); - return dict; +// Returns the bounds of |window| in the screen before adjusting for the frame. +bool GetUndecoratedWindowBounds(x11::Window window, gfx::Rect* rect) { + auto root = GetX11RootWindow(); + + x11::Connection* connection = x11::Connection::Get(); + auto get_geometry = connection->GetGeometry(window); + auto translate_coords = connection->TranslateCoordinates({window, root}); + + // Sync after making both requests so only one round-trip is made. + // Flush so all requests are sent before waiting on any replies. + connection->Flush(); + auto geometry = get_geometry.Sync(); + auto coords = translate_coords.Sync(); + + if (!geometry || !coords) + return false; + + *rect = gfx::Rect(coords->dst_x, coords->dst_y, geometry->width, + geometry->height); + return true; +} + +// Obtains the value of _{NET,GTK}_FRAME_EXTENTS as a gfx::Insets. Returns an +// empty gfx::Insets if the property doesn't exist or is malformed. +gfx::Insets GetFrameExtentsProperty(x11::Window window, x11::Atom property) { + std::vector<int32_t> frame_extents; + GetArrayProperty(window, property, &frame_extents); + if (frame_extents.size() != 4) + return gfx::Insets(); + return gfx::Insets(frame_extents[2] /* top */, frame_extents[0] /* left */, + frame_extents[3] /* bottom */, + frame_extents[1] /* right */); +} + +// Returns the adjustment necessary to obtain the opaque bounds of |window|. +gfx::Insets GetWindowDecorationAdjustment(x11::Window window) { + // _GTK_FRAME_EXTENTS is set by clients using client side decorations to + // subtract the window shadow from the bounds. _NET_FRAME_EXTENTS is set by + // the WM to add the opaque portion of the frame to the bounds. + return GetFrameExtentsProperty(window, x11::GetAtom("_GTK_FRAME_EXTENTS")) - + GetFrameExtentsProperty(window, x11::GetAtom("_NET_FRAME_EXTENTS")); +} + +// Returns the opaque bounds of |window| with it's frame. +bool GetDecoratedWindowBounds(x11::Window window, gfx::Rect* rect) { + if (!GetUndecoratedWindowBounds(window, rect)) + return false; + + rect->Inset(GetWindowDecorationAdjustment(window)); + return true; +} + +// Returns true if the event has event_x and event_y fields. +bool EventHasCoordinates(const x11::Event& event) { + return event.As<x11::KeyEvent>() || event.As<x11::ButtonEvent>() || + event.As<x11::MotionNotifyEvent>() || event.As<x11::CrossingEvent>() || + event.As<x11::Input::LegacyDeviceEvent>() || + event.As<x11::Input::DeviceEvent>() || + event.As<x11::Input::CrossingEvent>(); } } // namespace @@ -295,10 +317,10 @@ void DrawPixmap(x11::Connection* connection, .format = x11::ImageFormat::ZPixmap, .drawable = drawable, .gc = gc, - .width = width, - .height = n_rows, - .dst_x = dst_x, - .dst_y = dst_y + row, + .width = static_cast<uint16_t>(width), + .height = static_cast<uint16_t>(n_rows), + .dst_x = static_cast<int16_t>(dst_x), + .dst_y = static_cast<int16_t>(dst_y + row), .left_pad = 0, .depth = visual_info->format->depth, .data = data, @@ -319,34 +341,34 @@ bool QueryShmSupport() { int CoalescePendingMotionEvents(const x11::Event& x11_event, x11::Event* last_event) { + auto* conn = x11::Connection::Get(); + auto* ddmx11 = ui::DeviceDataManagerX11::GetInstance(); + int num_coalesced = 0; + const auto* motion = x11_event.As<x11::MotionNotifyEvent>(); const auto* device = x11_event.As<x11::Input::DeviceEvent>(); DCHECK(motion || device); - auto* conn = x11::Connection::Get(); - int num_coalesced = 0; + DCHECK(!device || device->opcode == x11::Input::DeviceEvent::Motion || + device->opcode == x11::Input::DeviceEvent::TouchUpdate); conn->ReadResponses(); - if (motion) { - for (auto& next_event : conn->events()) { + for (auto& event : conn->events()) { + if (!EventHasCoordinates(event)) + continue; + + if (motion) { + const auto* next_motion = event.As<x11::MotionNotifyEvent>(); + // Discard all but the most recent motion event that targets the same // window with unchanged state. - const auto* next_motion = next_event.As<x11::MotionNotifyEvent>(); if (next_motion && next_motion->event == motion->event && next_motion->child == motion->child && next_motion->state == motion->state) { - *last_event = std::move(next_event); - } else { - break; + *last_event = std::move(event); + continue; } - } - } else { - DCHECK(device->opcode == x11::Input::DeviceEvent::Motion || - device->opcode == x11::Input::DeviceEvent::TouchUpdate); - - auto* ddmx11 = ui::DeviceDataManagerX11::GetInstance(); - for (auto& event : conn->events()) { + } else { auto* next_device = event.As<x11::Input::DeviceEvent>(); - if (!next_device) break; @@ -360,27 +382,26 @@ int CoalescePendingMotionEvents(const x11::Event& x11_event, continue; } + // Confirm that the motion event is of the same type, is + // targeted at the same window, and that no buttons or modifiers + // have changed. if (next_device->opcode == device->opcode && !ddmx11->IsCMTGestureEvent(event) && - ddmx11->GetScrollClassEventDetail(event) == SCROLL_TYPE_NO_SCROLL) { - // Confirm that the motion event is targeted at the same window - // and that no buttons or modifiers have changed. - if (device->event == next_device->event && - device->child == next_device->child && - device->detail == next_device->detail && - device->button_mask == next_device->button_mask && - device->mods.base == next_device->mods.base && - device->mods.latched == next_device->mods.latched && - device->mods.locked == next_device->mods.locked && - device->mods.effective == next_device->mods.effective) { - *last_event = std::move(event); - num_coalesced++; - continue; - } + ddmx11->GetScrollClassEventDetail(event) == SCROLL_TYPE_NO_SCROLL && + device->event == next_device->event && + device->child == next_device->child && + device->detail == next_device->detail && + device->button_mask == next_device->button_mask && + device->mods.base == next_device->mods.base && + device->mods.latched == next_device->mods.latched && + device->mods.locked == next_device->mods.locked && + device->mods.effective == next_device->mods.effective) { + *last_event = std::move(event); + num_coalesced++; + continue; } - - break; } + break; } return num_coalesced; @@ -456,64 +477,16 @@ bool IsWindowVisible(x11::Window window) { window_desktop == kAllDesktops || window_desktop == current_desktop); } -bool GetInnerWindowBounds(x11::Window window, gfx::Rect* rect) { - auto x11_window = static_cast<x11::Window>(window); - auto root = static_cast<x11::Window>(GetX11RootWindow()); - - x11::Connection* connection = x11::Connection::Get(); - auto get_geometry = connection->GetGeometry(x11_window); - auto translate_coords = connection->TranslateCoordinates({x11_window, root}); - - // Sync after making both requests so only one round-trip is made. - // Flush so all requests are sent before waiting on any replies. - connection->Flush(); - auto geometry = get_geometry.Sync(); - auto coords = translate_coords.Sync(); - - if (!geometry || !coords) - return false; - - *rect = gfx::Rect(coords->dst_x, coords->dst_y, geometry->width, - geometry->height); - return true; -} - -bool GetWindowExtents(x11::Window window, gfx::Insets* extents) { - std::vector<int32_t> insets; - if (!GetArrayProperty(window, x11::GetAtom("_NET_FRAME_EXTENTS"), &insets)) - return false; - if (insets.size() != 4) - return false; - - int left = insets[0]; - int right = insets[1]; - int top = insets[2]; - int bottom = insets[3]; - extents->Set(-top, -left, -bottom, -right); - return true; -} - -bool GetOuterWindowBounds(x11::Window window, gfx::Rect* rect) { - if (!GetInnerWindowBounds(window, rect)) - return false; - - gfx::Insets extents; - if (GetWindowExtents(window, &extents)) - rect->Inset(extents); - // Not all window managers support _NET_FRAME_EXTENTS so return true even if - // requesting the property fails. - - return true; -} - bool WindowContainsPoint(x11::Window window, gfx::Point screen_loc) { TRACE_EVENT0("ui", "WindowContainsPoint"); - gfx::Rect window_rect; - if (!GetOuterWindowBounds(window, &window_rect)) + gfx::Rect undecorated_bounds; + if (!GetUndecoratedWindowBounds(window, &undecorated_bounds)) return false; - if (!window_rect.Contains(screen_loc)) + gfx::Rect decorated_bounds = undecorated_bounds; + decorated_bounds.Inset(GetWindowDecorationAdjustment(window)); + if (!decorated_bounds.Contains(screen_loc)) return false; if (!IsShapeExtensionAvailable()) @@ -548,8 +521,8 @@ bool WindowContainsPoint(x11::Window window, gfx::Point screen_loc) { // The ShapeInput and ShapeBounding rects are to be in window space, so we // have to translate by the window_rect's offset to map to screen space. gfx::Rect shape_rect = - gfx::Rect(rect.x + window_rect.x(), rect.y + window_rect.y(), - rect.width, rect.height); + gfx::Rect(rect.x + undecorated_bounds.x(), + rect.y + undecorated_bounds.y(), rect.width, rect.height); if (shape_rect.Contains(screen_loc)) { is_in_shape_rects = true; break; @@ -615,7 +588,7 @@ void SetWMSpecState(x11::Window window, x11::Atom state2) { SendClientMessage( window, GetX11RootWindow(), x11::GetAtom("_NET_WM_STATE"), - {enabled ? kNetWMStateAdd : kNetWMStateRemove, + {static_cast<uint32_t>(enabled ? kNetWMStateAdd : kNetWMStateRemove), static_cast<uint32_t>(state1), static_cast<uint32_t>(state2), 1, 0}); } @@ -631,7 +604,9 @@ void DoWMMoveResize(x11::Connection* connection, connection->UngrabPointer({x11::Time::CurrentTime}); SendClientMessage(window, root_window, x11::GetAtom("_NET_WM_MOVERESIZE"), - {location_px.x(), location_px.y(), direction, 0, 0}); + {static_cast<uint32_t>(location_px.x()), + static_cast<uint32_t>(location_px.y()), + static_cast<uint32_t>(direction), 0, 0}); } bool HasWMSpecProperty(const base::flat_set<x11::Atom>& properties, @@ -640,28 +615,17 @@ bool HasWMSpecProperty(const base::flat_set<x11::Atom>& properties, } bool GetCustomFramePrefDefault() { - // If the window manager doesn't support enough of EWMH to tell us its name, - // assume that it doesn't want custom frames. For example, _NET_WM_MOVERESIZE - // is needed for frame-drag-initiated window movement. - std::string wm_name; - if (!GetWindowManagerName(&wm_name)) + // _NET_WM_MOVERESIZE is needed for frame-drag-initiated window movement. + if (!WmSupportsHint(x11::GetAtom("_NET_WM_MOVERESIZE"))) return false; - // Also disable custom frames for (at-least-partially-)EWMH-supporting tiling - // window managers. ui::WindowManagerName wm = GuessWindowManager(); - if (wm == WM_AWESOME || wm == WM_I3 || wm == WM_ION3 || wm == WM_MATCHBOX || - wm == WM_NOTION || wm == WM_QTILE || wm == WM_RATPOISON || - wm == WM_STUMPWM || wm == WM_WMII) - return false; - - // Handle a few more window managers that don't get along well with custom - // frames. - if (wm == WM_ICE_WM || wm == WM_KWIN) + // If we don't know which WM is active, conservatively disable custom frames. + if (wm == WM_OTHER || wm == WM_UNNAMED) return false; - // For everything else, use custom frames. - return true; + // Stacking WMs should use custom frames. + return !IsWmTiling(wm); } bool IsWmTiling(WindowManagerName window_manager) { @@ -857,7 +821,7 @@ bool IsX11WindowFullScreen(x11::Window window) { } gfx::Rect window_rect; - if (!ui::GetOuterWindowBounds(window, &window_rect)) + if (!ui::GetDecoratedWindowBounds(window, &window_rect)) return false; // TODO(thomasanderson): We should use @@ -880,14 +844,14 @@ void SuspendX11ScreenSaver(bool suspend) { void StoreGpuExtraInfoIntoListValue(x11::VisualId system_visual, x11::VisualId rgba_visual, base::Value& list_value) { - list_value.Append( - NewDescriptionValuePair("Window manager", ui::GuessWindowManagerName())); - list_value.Append(NewDescriptionValuePair( + list_value.Append(display::BuildGpuInfoEntry("Window manager", + ui::GuessWindowManagerName())); + list_value.Append(display::BuildGpuInfoEntry( "Compositing manager", ui::IsCompositingManagerPresent() ? "Yes" : "No")); - list_value.Append(NewDescriptionValuePair( + list_value.Append(display::BuildGpuInfoEntry( "System visual ID", base::NumberToString(static_cast<uint32_t>(system_visual)))); - list_value.Append(NewDescriptionValuePair( + list_value.Append(display::BuildGpuInfoEntry( "RGBA visual ID", base::NumberToString(static_cast<uint32_t>(rgba_visual)))); } diff --git a/chromium/ui/base/x/x11_util.h b/chromium/ui/base/x/x11_util.h index bf38c1fba42..505c822856f 100644 --- a/chromium/ui/base/x/x11_util.h +++ b/chromium/ui/base/x/x11_util.h @@ -20,19 +20,14 @@ #include "base/memory/ref_counted_memory.h" #include "base/memory/scoped_refptr.h" #include "base/synchronization/lock.h" -#include "base/values.h" #include "build/build_config.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "ui/base/x/x11_cursor.h" -#include "ui/events/event_constants.h" -#include "ui/events/keycodes/keyboard_codes.h" -#include "ui/events/platform_event.h" #include "ui/gfx/icc_profile.h" #include "ui/gfx/image/image_skia.h" #include "ui/gfx/x/connection.h" -#include "ui/gfx/x/event.h" #include "ui/gfx/x/future.h" #include "ui/gfx/x/xproto.h" -#include "ui/gfx/x/xproto_types.h" typedef unsigned long Cursor; class SkPixmap; @@ -43,9 +38,7 @@ struct DefaultSingletonTraits; } namespace gfx { -class Insets; class Point; -class Rect; } // namespace gfx namespace ui { @@ -210,21 +203,6 @@ void SetHideTitlebarWhenMaximizedProperty(x11::Window window, // Returns true if |window| is visible. COMPONENT_EXPORT(UI_BASE_X) bool IsWindowVisible(x11::Window window); -// Returns the inner bounds of |window| (excluding the non-client area). -COMPONENT_EXPORT(UI_BASE_X) -bool GetInnerWindowBounds(x11::Window window, gfx::Rect* rect); - -// Returns the non-client area extents of |window|. This is a negative inset; it -// represents the negative size of the window border on all sides. -// InnerWindowBounds.Inset(WindowExtents) = OuterWindowBounds. -// Returns false if the window manager does not provide extents information. -COMPONENT_EXPORT(UI_BASE_X) -bool GetWindowExtents(x11::Window window, gfx::Insets* extents); - -// Returns the outer bounds of |window| (including the non-client area). -COMPONENT_EXPORT(UI_BASE_X) -bool GetOuterWindowBounds(x11::Window window, gfx::Rect* rect); - // Returns true if |window| contains the point |screen_loc|. COMPONENT_EXPORT(UI_BASE_X) bool WindowContainsPoint(x11::Window window, gfx::Point screen_loc); diff --git a/chromium/ui/base/x/x11_whole_screen_move_loop.cc b/chromium/ui/base/x/x11_whole_screen_move_loop.cc index db678799dbf..48767898182 100644 --- a/chromium/ui/base/x/x11_whole_screen_move_loop.cc +++ b/chromium/ui/base/x/x11_whole_screen_move_loop.cc @@ -13,7 +13,6 @@ #include "base/logging.h" #include "base/run_loop.h" #include "base/single_thread_task_runner.h" -#include "base/stl_util.h" #include "base/task/current_thread.h" #include "base/threading/thread_task_runner_handle.h" #include "ui/base/x/x11_pointer_grab.h" @@ -24,6 +23,7 @@ #include "ui/events/platform/platform_event_source.h" #include "ui/events/platform/scoped_event_dispatcher.h" #include "ui/events/x/events_x_utils.h" +#include "ui/events/x/x11_event_translation.h" #include "ui/gfx/x/connection.h" #include "ui/gfx/x/keysyms/keysyms.h" #include "ui/gfx/x/x11_window_event_manager.h" @@ -75,31 +75,7 @@ X11WholeScreenMoveLoop::~X11WholeScreenMoveLoop() { EndMoveLoop(); } -void X11WholeScreenMoveLoop::DispatchMouseMovement() { - if (!last_motion_in_screen_) - return; - auto weak_ref = weak_factory_.GetWeakPtr(); - delegate_->OnMouseMovement(last_motion_in_screen_->root_location(), - last_motion_in_screen_->flags(), - last_motion_in_screen_->time_stamp()); - // The delegate may delete this during dispatch. - if (!weak_ref) - return; - last_motion_in_screen_.reset(); -} - void X11WholeScreenMoveLoop::PostDispatchIfNeeded(const ui::MouseEvent& event) { - bool dispatch_mouse_event = !last_motion_in_screen_; - last_motion_in_screen_ = std::make_unique<ui::MouseEvent>(event); - if (dispatch_mouse_event) { - // Post a task to dispatch mouse movement event when control returns to the - // message loop. This allows smoother dragging since the events are - // dispatched without waiting for the drag widget updates. - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::BindOnce(&X11WholeScreenMoveLoop::DispatchMouseMovement, - weak_factory_.GetWeakPtr())); - } } //////////////////////////////////////////////////////////////////////////////// @@ -119,14 +95,25 @@ uint32_t X11WholeScreenMoveLoop::DispatchEvent(const ui::PlatformEvent& event) { switch (event->type()) { case ui::ET_MOUSE_MOVED: case ui::ET_MOUSE_DRAGGED: { - PostDispatchIfNeeded(*event->AsMouseEvent()); + auto& current_xevent = *x11::Connection::Get()->dispatching_event(); + x11::Event last_xevent; + std::unique_ptr<ui::Event> last_motion; + auto* mouse_event = event->AsMouseEvent(); + if ((current_xevent.As<x11::MotionNotifyEvent>() || + current_xevent.As<x11::Input::DeviceEvent>()) && + ui::CoalescePendingMotionEvents(current_xevent, &last_xevent)) { + last_motion = ui::BuildEventFromXEvent(last_xevent); + mouse_event = last_motion->AsMouseEvent(); + } + delegate_->OnMouseMovement(mouse_event->root_location(), + mouse_event->flags(), + mouse_event->time_stamp()); return ui::POST_DISPATCH_NONE; } case ui::ET_MOUSE_RELEASED: { if (event->AsMouseEvent()->IsLeftMouseButton()) { // Assume that drags are being done with the left mouse button. Only // break the drag if the left mouse button was released. - DispatchMouseMovement(); delegate_->OnMouseReleased(); if (!grabbed_pointer_) { @@ -211,9 +198,6 @@ void X11WholeScreenMoveLoop::EndMoveLoop() { if (!in_move_loop_) return; - // Prevent DispatchMouseMovement from dispatching any posted motion event. - last_motion_in_screen_.reset(); - // TODO(erg): Is this ungrab the cause of having to click to give input focus // on drawn out windows? Not ungrabbing here screws the X server until I kill // the chrome process. diff --git a/chromium/ui/base/x/x11_whole_screen_move_loop.h b/chromium/ui/base/x/x11_whole_screen_move_loop.h index 62c596b2172..5260287cc4b 100644 --- a/chromium/ui/base/x/x11_whole_screen_move_loop.h +++ b/chromium/ui/base/x/x11_whole_screen_move_loop.h @@ -60,9 +60,6 @@ class COMPONENT_EXPORT(UI_BASE_X) X11WholeScreenMoveLoop // Creates an input-only window to be used during the drag. void CreateDragInputWindow(x11::Connection* connection); - // Dispatch mouse movement event to |delegate_| in a posted task. - void DispatchMouseMovement(); - void PostDispatchIfNeeded(const ui::MouseEvent& event); X11MoveLoopDelegate* delegate_; @@ -91,7 +88,6 @@ class COMPONENT_EXPORT(UI_BASE_X) X11WholeScreenMoveLoop // pressing escape). bool canceled_; - std::unique_ptr<ui::MouseEvent> last_motion_in_screen_; base::WeakPtrFactory<X11WholeScreenMoveLoop> weak_factory_{this}; DISALLOW_COPY_AND_ASSIGN(X11WholeScreenMoveLoop); |