diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-01-20 13:40:20 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-01-22 12:41:23 +0000 |
commit | 7961cea6d1041e3e454dae6a1da660b453efd238 (patch) | |
tree | c0eeb4a9ff9ba32986289c1653d9608e53ccb444 /chromium/components/dbus | |
parent | b7034d0803538058e5c9d904ef03cf5eab34f6ef (diff) | |
download | qtwebengine-chromium-7961cea6d1041e3e454dae6a1da660b453efd238.tar.gz |
BASELINE: Update Chromium to 78.0.3904.130
Change-Id: If185e0c0061b3437531c97c9c8c78f239352a68b
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/components/dbus')
-rw-r--r-- | chromium/components/dbus/menu/BUILD.gn | 11 | ||||
-rw-r--r-- | chromium/components/dbus/menu/DEPS | 2 | ||||
-rw-r--r-- | chromium/components/dbus/menu/menu.cc | 251 | ||||
-rw-r--r-- | chromium/components/dbus/menu/menu.h | 35 | ||||
-rw-r--r-- | chromium/components/dbus/menu/menu_property_list.cc | 123 | ||||
-rw-r--r-- | chromium/components/dbus/menu/menu_property_list.h | 35 | ||||
-rw-r--r-- | chromium/components/dbus/menu/menu_property_list_unittest.cc | 361 | ||||
-rw-r--r-- | chromium/components/dbus/menu/types.cc | 20 | ||||
-rw-r--r-- | chromium/components/dbus/menu/types.h | 48 | ||||
-rw-r--r-- | chromium/components/dbus/menu/types_unittest.cc | 63 |
10 files changed, 840 insertions, 109 deletions
diff --git a/chromium/components/dbus/menu/BUILD.gn b/chromium/components/dbus/menu/BUILD.gn index 394ca48970e..ad0135d107f 100644 --- a/chromium/components/dbus/menu/BUILD.gn +++ b/chromium/components/dbus/menu/BUILD.gn @@ -2,10 +2,14 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//build/config/ui.gni") + component("menu") { sources = [ "menu.cc", "menu.h", + "menu_property_list.cc", + "menu_property_list.h", "properties_interface.cc", "properties_interface.h", "success_barrier_callback.cc", @@ -17,17 +21,24 @@ component("menu") { deps = [ "//base", "//base:i18n", + ] + public_deps = [ "//dbus", "//skia", "//ui/base", "//ui/gfx", ] + if (use_x11) { + configs += [ "//build/config/linux:x11" ] + } } source_set("unit_tests") { testonly = true sources = [ + "menu_property_list_unittest.cc", "success_barrier_callback_unittest.cc", + "types_unittest.cc", ] deps = [ ":menu", diff --git a/chromium/components/dbus/menu/DEPS b/chromium/components/dbus/menu/DEPS index 153f3f495cf..0d3f0e92940 100644 --- a/chromium/components/dbus/menu/DEPS +++ b/chromium/components/dbus/menu/DEPS @@ -1,4 +1,6 @@ include_rules = [ "+dbus", "+ui/base", + "+ui/events/keycodes", + "+ui/gfx/image", ] diff --git a/chromium/components/dbus/menu/menu.cc b/chromium/components/dbus/menu/menu.cc index 5d409095512..b02b8070068 100644 --- a/chromium/components/dbus/menu/menu.cc +++ b/chromium/components/dbus/menu/menu.cc @@ -6,6 +6,7 @@ #include <limits> #include <memory> +#include <set> #include "base/bind.h" #include "base/callback.h" @@ -17,7 +18,6 @@ #include "components/dbus/menu/properties_interface.h" #include "components/dbus/menu/success_barrier_callback.h" #include "ui/base/accelerators/accelerator.h" -#include "ui/base/accelerators/menu_label_accelerator_util_linux.h" #include "ui/base/models/menu_model.h" #include "ui/base/models/simple_menu_model.h" @@ -46,6 +46,7 @@ const char kPropertyValueStatusNormal[] = "normal"; uint32_t kPropertyValueVersion = 3; // Signals. +const char kSignalItemsPropertiesUpdated[] = "ItemsPropertiesUpdated"; const char kSignalLayoutUpdated[] = "LayoutUpdated"; // Creates a variant with the default value for |property_name|, or an empty @@ -78,6 +79,20 @@ DbusString DbusTextDirection() { return DbusString(base::i18n::IsRTL() ? "rtl " : "ltr"); } +void WriteRemovedProperties(dbus::MessageWriter* writer, + const MenuPropertyChanges& removed_props) { + dbus::MessageWriter removed_props_writer(nullptr); + writer->OpenArray("(ias)", &removed_props_writer); + for (const auto& pair : removed_props) { + dbus::MessageWriter struct_writer(nullptr); + removed_props_writer.OpenStruct(&struct_writer); + struct_writer.AppendInt32(pair.first); + struct_writer.AppendArrayOfStrings(pair.second); + removed_props_writer.CloseContainer(&struct_writer); + } + writer->CloseContainer(&removed_props_writer); +} + } // namespace DbusMenu::MenuItem::MenuItem(int32_t id, @@ -165,24 +180,66 @@ DbusMenu::~DbusMenu() = default; void DbusMenu::SetModel(ui::MenuModel* model, bool send_signal) { items_.clear(); + std::map<std::string, DbusVariant> properties; + std::vector<int32_t> children; if (model) { - std::map<std::string, DbusVariant> properties; properties["children-display"] = MakeDbusVariant(DbusString("submenu")); - items_[0] = std::make_unique<MenuItem>( - 0, std::move(properties), ConvertMenu(model), nullptr, nullptr, -1); - } else { - items_[0] = std::make_unique<MenuItem>( - 0, std::map<std::string, DbusVariant>(), std::vector<int32_t>(), - nullptr, nullptr, -1); + children = ConvertMenu(model); } + items_[0] = std::make_unique<MenuItem>( + 0, std::move(properties), std::move(children), nullptr, nullptr, -1); + + if (send_signal) + SendLayoutChangedSignal(0); +} - if (send_signal) { - dbus::Signal signal(kInterfaceDbusMenu, kSignalLayoutUpdated); - dbus::MessageWriter writer(&signal); - writer.AppendUint32(++revision_); // Revision of the new layout. - writer.AppendInt32(0); // Parent item whose layout changed. - menu_->SendSignal(&signal); +void DbusMenu::MenuLayoutUpdated(ui::MenuModel* model) { + MenuItem* item = FindMenuItemForModel(model, items_[0].get()); + DCHECK(item); + DeleteItemChildren(item); + item->children = ConvertMenu(model); + SendLayoutChangedSignal(item->id); +} + +void DbusMenu::MenuItemsPropertiesUpdated( + const std::vector<MenuItemReference>& menu_items) { + if (menu_items.empty()) + return; + + MenuPropertyChanges updated_props; + MenuPropertyChanges removed_props; + for (const auto& menu_item : menu_items) { + ui::MenuModel* menu = menu_item.first; + int index = menu_item.second; + MenuItem* parent = FindMenuItemForModel(menu, items_[0].get()); + MenuItem* item = nullptr; + for (int32_t id : parent->children) { + MenuItem* child = items_[id].get(); + DCHECK_EQ(child->containing_menu, menu); + if (child->containing_menu_index == index) { + item = child; + break; + } + } + DCHECK(item); + + auto old_properties = std::move(item->properties); + item->properties = ComputeMenuPropertiesForMenuItem(menu, index); + MenuPropertyList item_updated_props; + MenuPropertyList item_removed_props; + ComputeMenuPropertyChanges(old_properties, item->properties, + &item_updated_props, &item_removed_props); + if (!item_updated_props.empty()) + updated_props[item->id] = std::move(item_updated_props); + if (!item_removed_props.empty()) + removed_props[item->id] = std::move(item_removed_props); } + + dbus::Signal signal(kInterfaceDbusMenu, kSignalItemsPropertiesUpdated); + dbus::MessageWriter writer(&signal); + WriteUpdatedProperties(&writer, updated_props); + WriteRemovedProperties(&writer, removed_props); + menu_->SendSignal(&signal); } // static @@ -270,12 +327,12 @@ void DbusMenu::OnGetGroupProperties(ScopedMethodResponse* response) { dbus::MessageReader id_reader(nullptr); if (!response->reader().PopArray(&id_reader)) return; - std::set<int32_t> ids; + std::vector<int32_t> ids; while (id_reader.HasMoreData()) { int32_t id; if (!id_reader.PopInt32(&id)) return; - ids.insert(id); + ids.push_back(id); } std::set<std::string> property_filter; @@ -292,15 +349,14 @@ void DbusMenu::OnGetGroupProperties(ScopedMethodResponse* response) { dbus::MessageWriter& writer = response->Writer(); dbus::MessageWriter item_writer(nullptr); writer.OpenArray("(ia{sv})", &item_writer); - for (const auto& item_pair : items_) { - if (!ids.empty() && !base::Contains(ids, item_pair.first)) - continue; + + auto write_item = [&](int32_t id, const MenuItem& item) { dbus::MessageWriter struct_writer(nullptr); item_writer.OpenStruct(&struct_writer); - struct_writer.AppendInt32(item_pair.first); + struct_writer.AppendInt32(id); dbus::MessageWriter property_writer(nullptr); struct_writer.OpenArray("{sv}", &property_writer); - for (const auto& property_pair : item_pair.second->properties) { + for (const auto& property_pair : item.properties) { if (!property_filter.empty() && !base::Contains(property_filter, property_pair.first)) { continue; @@ -313,7 +369,19 @@ void DbusMenu::OnGetGroupProperties(ScopedMethodResponse* response) { } struct_writer.CloseContainer(&property_writer); item_writer.CloseContainer(&struct_writer); + }; + + if (ids.empty()) { + for (const auto& item_pair : items_) + write_item(item_pair.first, *item_pair.second); + } else { + for (int32_t id : ids) { + auto it = items_.find(id); + if (it != items_.end()) + write_item(id, *it->second); + } } + writer.CloseContainer(&item_writer); } @@ -321,7 +389,7 @@ void DbusMenu::OnGetLayout(ScopedMethodResponse* response) { dbus::MessageReader& reader = response->reader(); int32_t id; int32_t depth; - std::vector<std::string> property_filter; + MenuPropertyList property_filter; if (!reader.PopInt32(&id) || !reader.PopInt32(&depth) || depth < -1 || !reader.PopArrayOfStrings(&property_filter)) { return; @@ -393,7 +461,8 @@ bool DbusMenu::EventImpl(dbus::MessageReader* reader, int32_t* id_error) { return false; item->containing_menu->ActivatedAt(item->containing_menu_index); } else { - DCHECK_EQ("hovered", type); + DCHECK(type == "hovered" || type == "opened" || type == "closed") + << "Unexpected type: " << type; // Nothing to do. } @@ -407,84 +476,13 @@ std::vector<int32_t> DbusMenu::ConvertMenu(ui::MenuModel* menu) { items.reserve(menu->GetItemCount()); for (int i = 0; i < menu->GetItemCount(); ++i) { - // Properties should only be set if they differ from the default values. - std::map<std::string, DbusVariant> properties; - - // The dbusmenu interface has no concept of a "sublabel", "minor text", or - // "minor icon" like MenuModel has. Ignore these rather than trying to - // merge them with the regular label and icon. - base::string16 label = menu->GetLabelAt(i); - if (!label.empty()) { - properties["label"] = MakeDbusVariant(DbusString( - ui::ConvertAcceleratorsFromWindowsStyle(base::UTF16ToUTF8(label)))); - } - - if (!menu->IsEnabledAt(i)) - properties["enabled"] = MakeDbusVariant(DbusBoolean(false)); - if (!menu->IsVisibleAt(i)) - properties["visible"] = MakeDbusVariant(DbusBoolean(false)); - - gfx::Image icon; - if (menu->GetIconAt(i, &icon)) { - properties["icon-data"] = - MakeDbusVariant(DbusByteArray(icon.As1xPNGBytes())); - } - - ui::Accelerator accelerator; - if (menu->GetAcceleratorAt(i, &accelerator)) { - std::vector<DbusString> parts; - if (accelerator.IsCtrlDown()) - parts.push_back(DbusString("Control")); - if (accelerator.IsAltDown()) - parts.push_back(DbusString("Alt")); - if (accelerator.IsShiftDown()) - parts.push_back(DbusString("Shift")); - if (accelerator.IsCmdDown()) - parts.push_back(DbusString("Super")); - parts.push_back( - DbusString(base::UTF16ToUTF8(accelerator.KeyCodeToName()))); - properties["shortcut"] = MakeDbusVariant( - MakeDbusArray(DbusArray<DbusString>(std::move(parts)))); - } - - switch (menu->GetTypeAt(i)) { - case ui::MenuModel::TYPE_COMMAND: - case ui::MenuModel::TYPE_HIGHLIGHTED: - // Nothing special to do. - break; - case ui::MenuModel::TYPE_CHECK: - case ui::MenuModel::TYPE_RADIO: - properties["toggle-type"] = MakeDbusVariant(DbusString( - menu->GetTypeAt(i) == ui::MenuModel::TYPE_CHECK ? "checkmark" - : "radio")); - properties["toggle-state"] = - MakeDbusVariant(DbusInt32(menu->IsItemCheckedAt(i) ? 1 : 0)); - break; - case ui::MenuModel::TYPE_SEPARATOR: - // The dbusmenu interface doesn't have multiple types of separators like - // MenuModel. Just use a regular separator in all cases. - properties["type"] = MakeDbusVariant(DbusString("separator")); - break; - case ui::MenuModel::TYPE_BUTTON_ITEM: - // This type of menu represents a row of buttons, but the dbusmenu - // interface has no equivalent of this. Ignore these items for now - // since there's currently no uses of it that plumb into this codepath. - // If there are button menu items in the future, we'd have to fake them - // with multiple menu items. - NOTIMPLEMENTED(); - continue; - case ui::MenuModel::TYPE_SUBMENU: - case ui::MenuModel::TYPE_ACTIONABLE_SUBMENU: - properties["children-display"] = MakeDbusVariant(DbusString("submenu")); - break; - } - ui::MenuModel* submenu = menu->GetSubmenuModelAt(i); std::vector<int32_t> children = ConvertMenu(submenu); int32_t id = NextItemId(); items_[id] = std::make_unique<MenuItem>( - id, std::move(properties), std::move(children), submenu, menu, i); + id, ComputeMenuPropertiesForMenuItem(menu, i), std::move(children), + submenu, menu, i); items.push_back(id); } @@ -500,7 +498,7 @@ int32_t DbusMenu::NextItemId() { void DbusMenu::WriteMenuItem(const MenuItem* item, dbus::MessageWriter* writer, int32_t depth, - const std::vector<std::string>& property_filter) { + const MenuPropertyList& property_filter) const { dbus::MessageWriter struct_writer(nullptr); writer->OpenStruct(&struct_writer); struct_writer.AppendInt32(item->id); @@ -525,7 +523,7 @@ void DbusMenu::WriteMenuItem(const MenuItem* item, for (int32_t child : item->children) { dbus::MessageWriter variant_writer(nullptr); children_writer.OpenVariant("(ia{sv}av)", &variant_writer); - WriteMenuItem(items_[child].get(), &variant_writer, + WriteMenuItem(items_.find(child)->second.get(), &variant_writer, depth == -1 ? -1 : depth - 1, property_filter); children_writer.CloseContainer(&variant_writer); } @@ -534,3 +532,60 @@ void DbusMenu::WriteMenuItem(const MenuItem* item, writer->CloseContainer(&struct_writer); } + +void DbusMenu::WriteUpdatedProperties( + dbus::MessageWriter* writer, + const MenuPropertyChanges& updated_props) const { + dbus::MessageWriter updated_props_writer(nullptr); + writer->OpenArray("(ia{sv})", &updated_props_writer); + for (const auto& pair : updated_props) { + int32_t id = pair.first; + MenuItem* item = items_.find(id)->second.get(); + dbus::MessageWriter struct_writer(nullptr); + updated_props_writer.OpenStruct(&struct_writer); + struct_writer.AppendInt32(id); + dbus::MessageWriter array_writer(nullptr); + struct_writer.OpenArray("{sv}", &array_writer); + for (const std::string& key : pair.second) { + dbus::MessageWriter dict_entry_writer(nullptr); + array_writer.OpenDictEntry(&dict_entry_writer); + dict_entry_writer.AppendString(key); + item->properties[key].Write(&dict_entry_writer); + array_writer.CloseContainer(&dict_entry_writer); + } + struct_writer.CloseContainer(&array_writer); + updated_props_writer.CloseContainer(&struct_writer); + } + writer->CloseContainer(&updated_props_writer); +} + +DbusMenu::MenuItem* DbusMenu::FindMenuItemForModel(const ui::MenuModel* model, + MenuItem* item) const { + if (item->menu == model) + return item; + for (int32_t id : item->children) { + MenuItem* child = items_.find(id)->second.get(); + MenuItem* found = FindMenuItemForModel(model, child); + if (found) + return found; + } + return nullptr; +} + +void DbusMenu::DeleteItem(MenuItem* item) { + DeleteItemChildren(item); + items_.erase(item->id); +} + +void DbusMenu::DeleteItemChildren(MenuItem* item) { + for (int32_t id : item->children) + DeleteItem(items_.find(id)->second.get()); +} + +void DbusMenu::SendLayoutChangedSignal(int32_t id) { + dbus::Signal signal(kInterfaceDbusMenu, kSignalLayoutUpdated); + dbus::MessageWriter writer(&signal); + writer.AppendUint32(++revision_); // Revision of the new layout. + writer.AppendInt32(id); // Parent item whose layout changed. + menu_->SendSignal(&signal); +} diff --git a/chromium/components/dbus/menu/menu.h b/chromium/components/dbus/menu/menu.h index 5198c809aba..e6f702a5c9a 100644 --- a/chromium/components/dbus/menu/menu.h +++ b/chromium/components/dbus/menu/menu.h @@ -7,12 +7,15 @@ #include <map> #include <memory> +#include <string> +#include <utility> #include <vector> #include "base/callback_forward.h" #include "base/component_export.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" +#include "components/dbus/menu/menu_property_list.h" #include "components/dbus/menu/types.h" #include "dbus/bus.h" #include "dbus/exported_object.h" @@ -28,19 +31,30 @@ class DbusPropertiesInterface; class COMPONENT_EXPORT(DBUS) DbusMenu { public: using InitializedCallback = base::OnceCallback<void(bool success)>; + using MenuItemReference = std::pair<ui::MenuModel*, int>; // The exported DBus object will not be unregistered upon deletion. It is the // responsibility of the caller to remove it after |this| is deleted. DbusMenu(dbus::ExportedObject* exported_object, InitializedCallback callback); ~DbusMenu(); + // Should be called when there's a new root menu. void SetModel(ui::MenuModel* model, bool send_signal); + // Should be called when items are added/removed/reordered in a menu. Prefer + // this over SetModel(). + void MenuLayoutUpdated(ui::MenuModel* model); + + // Should be called when properties on (a group of) menu items change. Prefer + // this over SetModel(). + void MenuItemsPropertiesUpdated( + const std::vector<MenuItemReference>& menu_items); + private: struct MenuItem { public: MenuItem(int32_t id, - std::map<std::string, DbusVariant>&& properties, + MenuItemProperties&& properties, std::vector<int32_t>&& children, ui::MenuModel* menu, ui::MenuModel* containing_menu, @@ -48,8 +62,8 @@ class COMPONENT_EXPORT(DBUS) DbusMenu { ~MenuItem(); const int32_t id; - const std::map<std::string, DbusVariant> properties; - const std::vector<int32_t> children; + MenuItemProperties properties; + std::vector<int32_t> children; // The MenuModel corresponding to this MenuItem, or null if this MenuItem is // not a submenu. This can happen for leaf items or an empty root item. @@ -119,7 +133,20 @@ class COMPONENT_EXPORT(DBUS) DbusMenu { void WriteMenuItem(const MenuItem* item, dbus::MessageWriter* writer, int32_t depth, - const std::vector<std::string>& property_filter); + const MenuPropertyList& property_filter) const; + + void WriteUpdatedProperties(dbus::MessageWriter* writer, + const MenuPropertyChanges& updated_props) const; + + // Recursively searches |item| and its descendants for the MenuItem + // corresponding to |model|. + MenuItem* FindMenuItemForModel(const ui::MenuModel* model, + MenuItem* item) const; + + void DeleteItem(MenuItem* item); + void DeleteItemChildren(MenuItem* item); + + void SendLayoutChangedSignal(int32_t id); dbus::ExportedObject* menu_ = nullptr; diff --git a/chromium/components/dbus/menu/menu_property_list.cc b/chromium/components/dbus/menu/menu_property_list.cc new file mode 100644 index 00000000000..acbc74fc3e3 --- /dev/null +++ b/chromium/components/dbus/menu/menu_property_list.cc @@ -0,0 +1,123 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/dbus/menu/menu_property_list.h" + +#include <utility> + +#include "base/strings/utf_string_conversions.h" +#include "ui/base/accelerators/accelerator.h" +#include "ui/base/accelerators/menu_label_accelerator_util_linux.h" +#include "ui/base/models/menu_model.h" +#include "ui/gfx/image/image.h" + +#if defined(USE_X11) +#include <X11/Xlib.h> + +#include "ui/events/keycodes/keyboard_code_conversion_x.h" // nogncheck +#endif + +MenuItemProperties ComputeMenuPropertiesForMenuItem(ui::MenuModel* menu, + int i) { + // Properties should only be set if they differ from the default values. + MenuItemProperties properties; + + // The dbusmenu interface has no concept of a "sublabel", "minor text", or + // "minor icon" like MenuModel has. Ignore these rather than trying to + // merge them with the regular label and icon. + base::string16 label = menu->GetLabelAt(i); + if (!label.empty()) { + properties["label"] = MakeDbusVariant(DbusString( + ui::ConvertAcceleratorsFromWindowsStyle(base::UTF16ToUTF8(label)))); + } + + if (!menu->IsEnabledAt(i)) + properties["enabled"] = MakeDbusVariant(DbusBoolean(false)); + if (!menu->IsVisibleAt(i)) + properties["visible"] = MakeDbusVariant(DbusBoolean(false)); + + gfx::Image icon; + if (menu->GetIconAt(i, &icon)) { + properties["icon-data"] = + MakeDbusVariant(DbusByteArray(icon.As1xPNGBytes())); + } + + ui::Accelerator accelerator; + if (menu->GetAcceleratorAt(i, &accelerator)) { + std::vector<DbusString> parts; + if (accelerator.IsCtrlDown()) + parts.push_back(DbusString("Control")); + if (accelerator.IsAltDown()) + parts.push_back(DbusString("Alt")); + if (accelerator.IsShiftDown()) + parts.push_back(DbusString("Shift")); + if (accelerator.IsCmdDown()) + parts.push_back(DbusString("Super")); +#if defined(USE_X11) + parts.push_back(DbusString(XKeysymToString( + XKeysymForWindowsKeyCode(accelerator.key_code(), false)))); + properties["shortcut"] = + MakeDbusVariant(MakeDbusArray(DbusArray<DbusString>(std::move(parts)))); +#else + NOTIMPLEMENTED(); +#endif + } + + switch (menu->GetTypeAt(i)) { + case ui::MenuModel::TYPE_COMMAND: + case ui::MenuModel::TYPE_HIGHLIGHTED: + // Nothing special to do. + break; + case ui::MenuModel::TYPE_CHECK: + case ui::MenuModel::TYPE_RADIO: + properties["toggle-type"] = MakeDbusVariant(DbusString( + menu->GetTypeAt(i) == ui::MenuModel::TYPE_CHECK ? "checkmark" + : "radio")); + properties["toggle-state"] = + MakeDbusVariant(DbusInt32(menu->IsItemCheckedAt(i) ? 1 : 0)); + break; + case ui::MenuModel::TYPE_SEPARATOR: + // The dbusmenu interface doesn't have multiple types of separators like + // MenuModel. Just use a regular separator in all cases. + properties["type"] = MakeDbusVariant(DbusString("separator")); + break; + case ui::MenuModel::TYPE_BUTTON_ITEM: + // This type of menu represents a row of buttons, but the dbusmenu + // interface has no equivalent of this. Ignore these items for now + // since there's currently no uses of it that plumb into this codepath. + // If there are button menu items in the future, we'd have to fake them + // with multiple menu items. + NOTIMPLEMENTED(); + break; + case ui::MenuModel::TYPE_SUBMENU: + case ui::MenuModel::TYPE_ACTIONABLE_SUBMENU: + properties["children-display"] = MakeDbusVariant(DbusString("submenu")); + break; + } + + return properties; +} + +void ComputeMenuPropertyChanges(const MenuItemProperties& old_properties, + const MenuItemProperties& new_properties, + MenuPropertyList* item_updated_props, + MenuPropertyList* item_removed_props) { + // Compute updated and removed properties. + for (const auto& pair : old_properties) { + const std::string& key = pair.first; + auto new_it = new_properties.find(key); + if (new_it != new_properties.end()) { + if (new_it->second != pair.second) + item_updated_props->push_back(key); + } else { + item_removed_props->push_back(key); + } + } + // Compute added properties. + for (const auto& pair : new_properties) { + const std::string& key = pair.first; + if (!base::Contains(old_properties, key)) + item_updated_props->push_back(key); + } +} diff --git a/chromium/components/dbus/menu/menu_property_list.h b/chromium/components/dbus/menu/menu_property_list.h new file mode 100644 index 00000000000..93fd377d174 --- /dev/null +++ b/chromium/components/dbus/menu/menu_property_list.h @@ -0,0 +1,35 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_DBUS_MENU_MENU_PROPERTY_LIST_H_ +#define COMPONENTS_DBUS_MENU_MENU_PROPERTY_LIST_H_ + +#include <map> +#include <string> +#include <vector> + +#include "components/dbus/menu/types.h" + +using MenuPropertyList = std::vector<std::string>; +using MenuItemProperties = std::map<std::string, DbusVariant>; +using MenuPropertyChanges = std::map<int32_t, MenuPropertyList>; + +namespace ui { +class MenuModel; +} + +// Computes properties for the menu item with index |i| in |menu|. +COMPONENT_EXPORT(DBUS) +MenuItemProperties ComputeMenuPropertiesForMenuItem(ui::MenuModel* menu, int i); + +// Given inputs |old_properties| and |new_properties|, computes outputs +// |item_updated_props| and |item_removed_props| suitable for use in +// com.canonical.dbusmenu.ItemsPropertiesUpdated. +COMPONENT_EXPORT(DBUS) +void ComputeMenuPropertyChanges(const MenuItemProperties& old_properties, + const MenuItemProperties& new_properties, + MenuPropertyList* item_updated_props, + MenuPropertyList* item_removed_props); + +#endif // COMPONENTS_DBUS_MENU_MENU_PROPERTY_LIST_H_ diff --git a/chromium/components/dbus/menu/menu_property_list_unittest.cc b/chromium/components/dbus/menu/menu_property_list_unittest.cc new file mode 100644 index 00000000000..f700bf6ea1b --- /dev/null +++ b/chromium/components/dbus/menu/menu_property_list_unittest.cc @@ -0,0 +1,361 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/dbus/menu/menu_property_list.h" + +#include <memory> + +#include "base/strings/utf_string_conversions.h" +#include "components/dbus/menu/types.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/base/accelerators/accelerator.h" +#include "ui/base/models/menu_model.h" +#include "ui/base/models/menu_separator_types.h" +#include "ui/base/models/simple_menu_model.h" +#include "ui/gfx/image/image.h" +#include "ui/gfx/image/image_skia.h" +#include "ui/gfx/image/image_skia_rep_default.h" + +namespace { + +class TestMenuModel : public ui::SimpleMenuModel, + public ui::SimpleMenuModel::Delegate { + public: + TestMenuModel(bool checked, + bool enabled, + bool visible, + const base::string16& label, + const gfx::Image& icon, + const ui::Accelerator& accelerator) + : ui::SimpleMenuModel(this), + checked_(checked), + enabled_(enabled), + visible_(visible), + label_(label), + icon_(icon), + accelerator_(accelerator) {} + ~TestMenuModel() override = default; + + MenuItemProperties ComputeProperties() { + return ComputeMenuPropertiesForMenuItem(this, 0); + } + + protected: + // ui::MenuModel:: + bool IsItemDynamicAt(int index) const override { + EXPECT_LE(index, 0); + // Return true so that GetIconForCommandId() will always be called. + return true; + } + + // ui::SimpleMenuModel::Delegate: + bool IsCommandIdChecked(int command_id) const override { + EXPECT_LE(command_id, 0); + return checked_; + } + bool IsCommandIdEnabled(int command_id) const override { + EXPECT_LE(command_id, 0); + return enabled_; + } + bool IsCommandIdVisible(int command_id) const override { + EXPECT_LE(command_id, 0); + return visible_; + } + base::string16 GetLabelForCommandId(int command_id) const override { + EXPECT_LE(command_id, 0); + return label_; + } + bool GetIconForCommandId(int command_id, gfx::Image* icon) const override { + EXPECT_LE(command_id, 0); + if (icon_.IsEmpty()) + return false; + *icon = icon_; + return true; + } + void ExecuteCommand(int command_id, int event_flags) override { + EXPECT_LE(command_id, 0); + } + bool GetAcceleratorForCommandId(int command_id, + ui::Accelerator* accelerator) const override { + EXPECT_LE(command_id, 0); + if (accelerator_ == ui::Accelerator()) + return false; + *accelerator = accelerator_; + return true; + } + + private: + const bool checked_; + const bool enabled_; + const bool visible_; + const base::string16 label_; + const gfx::Image icon_; + const ui::Accelerator accelerator_; +}; + +class TestMenuModelBuilder { + public: + TestMenuModelBuilder() = default; + ~TestMenuModelBuilder() = default; + + TestMenuModelBuilder SetType(ui::MenuModel::ItemType type) const { + TestMenuModelBuilder builder = *this; + builder.type_ = type; + return builder; + } + + TestMenuModelBuilder SetChecked(bool checked) const { + TestMenuModelBuilder builder = *this; + builder.checked_ = checked; + return builder; + } + + TestMenuModelBuilder SetEnabled(bool enabled) const { + TestMenuModelBuilder builder = *this; + builder.enabled_ = enabled; + return builder; + } + + TestMenuModelBuilder SetVisible(bool visible) const { + TestMenuModelBuilder builder = *this; + builder.visible_ = visible; + return builder; + } + + TestMenuModelBuilder SetLabel(const std::string& label) const { + TestMenuModelBuilder builder = *this; + builder.label_ = base::ASCIIToUTF16(label); + return builder; + } + + TestMenuModelBuilder SetIcon(const gfx::Image& icon) const { + TestMenuModelBuilder builder = *this; + builder.icon_ = icon; + return builder; + } + + TestMenuModelBuilder SetAccelerator( + const ui::Accelerator& accelerator) const { + TestMenuModelBuilder builder = *this; + builder.accelerator_ = accelerator; + return builder; + } + + std::unique_ptr<TestMenuModel> Build() const { + auto menu = std::make_unique<TestMenuModel>(checked_, enabled_, visible_, + label_, icon_, accelerator_); + switch (type_) { + case ui::MenuModel::TYPE_COMMAND: + menu->AddItem(0, label_); + break; + case ui::MenuModel::TYPE_CHECK: + menu->AddCheckItem(0, label_); + break; + case ui::MenuModel::TYPE_RADIO: + menu->AddRadioItem(0, label_, 0); + break; + case ui::MenuModel::TYPE_SEPARATOR: + menu->AddSeparator(ui::MenuSeparatorType::SPACING_SEPARATOR); + break; + case ui::MenuModel::TYPE_BUTTON_ITEM: + NOTIMPLEMENTED(); + break; + case ui::MenuModel::TYPE_SUBMENU: + menu->AddSubMenu(0, label_, nullptr); + break; + case ui::MenuModel::TYPE_ACTIONABLE_SUBMENU: + menu->AddActionableSubMenu(0, label_, nullptr); + break; + case ui::MenuModel::TYPE_HIGHLIGHTED: + menu->AddHighlightedItemWithIcon(0, label_, icon_.AsImageSkia()); + break; + } + return menu; + } + + private: + ui::MenuModel::ItemType type_ = ui::MenuModel::TYPE_COMMAND; + bool checked_ = false; + bool enabled_ = true; + bool visible_ = true; + base::string16 label_; + gfx::Image icon_; + ui::Accelerator accelerator_; +}; + +} // namespace + +TEST(MenuPropertyListTest, ComputePropertiesBasic) { + auto builder = TestMenuModelBuilder(); + auto menu = builder.Build(); + MenuItemProperties props; + EXPECT_EQ(menu->ComputeProperties(), props); + + // Same for TYPE_HIGHLIGHTED. + menu = builder.SetType(ui::MenuModel::TYPE_HIGHLIGHTED).Build(); + EXPECT_EQ(menu->ComputeProperties(), props); +} + +TEST(MenuPropertyListTest, ComputePropertiesCheck) { + auto menu = TestMenuModelBuilder().SetType(ui::MenuModel::TYPE_CHECK).Build(); + MenuItemProperties props; + props["toggle-type"] = MakeDbusVariant(DbusString("checkmark")); + props["toggle-state"] = MakeDbusVariant(DbusInt32(0)); + EXPECT_EQ(menu->ComputeProperties(), props); +} + +TEST(MenuPropertyListTest, ComputePropertiesRadio) { + auto menu = TestMenuModelBuilder().SetType(ui::MenuModel::TYPE_RADIO).Build(); + MenuItemProperties props; + props["toggle-type"] = MakeDbusVariant(DbusString("radio")); + props["toggle-state"] = MakeDbusVariant(DbusInt32(0)); + EXPECT_EQ(menu->ComputeProperties(), props); +} + +TEST(MenuPropertyListTest, ComputePropertiesCheckedState) { + auto builder = TestMenuModelBuilder().SetChecked(true); + + // Types other than radio and check should not have toggle-state set. + auto menu = builder.Build(); + MenuItemProperties props; + EXPECT_EQ(menu->ComputeProperties(), props); + + // Radio and check buttons should have the toggle-state set. + menu = builder.SetType(ui::MenuModel::TYPE_RADIO).Build(); + props["toggle-type"] = MakeDbusVariant(DbusString("radio")); + props["toggle-state"] = MakeDbusVariant(DbusInt32(1)); + EXPECT_EQ(menu->ComputeProperties(), props); + + menu = builder.SetType(ui::MenuModel::TYPE_CHECK).Build(); + props["toggle-type"] = MakeDbusVariant(DbusString("checkmark")); + EXPECT_EQ(menu->ComputeProperties(), props); +} + +TEST(MenuPropertyListTest, ComputePropertiesSeparator) { + auto menu = + TestMenuModelBuilder().SetType(ui::MenuModel::TYPE_SEPARATOR).Build(); + MenuItemProperties props; + props["type"] = MakeDbusVariant(DbusString("separator")); + EXPECT_EQ(menu->ComputeProperties(), props); +} + +TEST(MenuPropertyListTest, ComputePropertiesSubmenu) { + auto builder = TestMenuModelBuilder(); + auto menu = builder.SetType(ui::MenuModel::TYPE_SUBMENU).Build(); + MenuItemProperties props; + props["children-display"] = MakeDbusVariant(DbusString("submenu")); + EXPECT_EQ(menu->ComputeProperties(), props); + + // Same for ACTIONABLE_SUBMENU. + menu = builder.SetType(ui::MenuModel::TYPE_ACTIONABLE_SUBMENU).Build(); + EXPECT_EQ(menu->ComputeProperties(), props); +} + +TEST(MenuPropertyListTest, ComputePropertiesEnabledState) { + auto builder = TestMenuModelBuilder(); + + // Enabled. + auto menu = builder.SetEnabled(true).Build(); + MenuItemProperties props; + EXPECT_EQ(menu->ComputeProperties(), props); + + // Disabled. + menu = builder.SetEnabled(false).Build(); + props["enabled"] = MakeDbusVariant(DbusBoolean(false)); + EXPECT_EQ(menu->ComputeProperties(), props); +} + +TEST(MenuPropertyListTest, ComputePropertiesVisibleState) { + auto builder = TestMenuModelBuilder(); + + // Visible. + auto menu = builder.SetVisible(true).Build(); + MenuItemProperties props; + EXPECT_EQ(menu->ComputeProperties(), props); + + // Hidden. + menu = builder.SetVisible(false).Build(); + props["visible"] = MakeDbusVariant(DbusBoolean(false)); + EXPECT_EQ(menu->ComputeProperties(), props); +} + +TEST(MenuPropertyListTest, ComputePropertiesLabel) { + auto builder = TestMenuModelBuilder(); + + // No label. + auto menu = builder.SetLabel("").Build(); + MenuItemProperties props; + EXPECT_EQ(menu->ComputeProperties(), props); + + // Non-empty label. + menu = builder.SetLabel("label value").Build(); + props["label"] = MakeDbusVariant(DbusString("label value")); + EXPECT_EQ(menu->ComputeProperties(), props); +} + +TEST(MenuPropertyListTest, ComputePropertiesIcon) { + auto builder = TestMenuModelBuilder(); + + // No icon. + auto menu = builder.SetIcon(gfx::Image()).Build(); + MenuItemProperties props; + EXPECT_EQ(menu->ComputeProperties(), props); + + // Non-empty label. + SkBitmap bitmap; + bitmap.allocN32Pixels(10, 10); + bitmap.eraseARGB(255, 123, 123, 123); + gfx::ImageSkia image_skia; + image_skia.AddRepresentation(gfx::ImageSkiaRep(bitmap, 1.0f)); + gfx::Image icon(image_skia); + menu = builder.SetIcon(icon).Build(); + props["icon-data"] = MakeDbusVariant(DbusByteArray(icon.As1xPNGBytes())); + EXPECT_EQ(menu->ComputeProperties(), props); +} + +#if defined(USE_X11) +TEST(MenuPropertyListTest, ComputePropertiesAccelerator) { + auto builder = TestMenuModelBuilder(); + + // No accelerator. + auto menu = builder.SetAccelerator(ui::Accelerator()).Build(); + MenuItemProperties props; + EXPECT_EQ(menu->ComputeProperties(), props); + + // Set a key. + menu = builder.SetAccelerator(ui::Accelerator(ui::VKEY_A, 0)).Build(); + props["shortcut"] = + MakeDbusVariant(MakeDbusArray(MakeDbusArray(DbusString("a")))); + EXPECT_EQ(menu->ComputeProperties(), props); + + // Add modifiers. + menu = builder + .SetAccelerator(ui::Accelerator( + ui::VKEY_A, + ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN)) + .Build(); + props["shortcut"] = MakeDbusVariant( + MakeDbusArray(MakeDbusArray(DbusString("Control"), DbusString("Alt"), + DbusString("Shift"), DbusString("a")))); + EXPECT_EQ(menu->ComputeProperties(), props); +} +#endif + +TEST(MenuPropertyListTest, ComputePropertyChanges) { + MenuItemProperties old_props; + old_props["1"] = MakeDbusVariant(DbusInt32(1)); // Remains the same. + old_props["2"] = MakeDbusVariant(DbusInt32(2)); // Updates to -2. + old_props["3"] = MakeDbusVariant(DbusInt32(3)); // Removed. + + MenuItemProperties new_props; + new_props["1"] = MakeDbusVariant(DbusInt32(1)); + new_props["2"] = MakeDbusVariant(DbusInt32(-2)); + new_props["4"] = MakeDbusVariant(DbusInt32(4)); // Added. + + MenuPropertyList updated; + MenuPropertyList removed; + ComputeMenuPropertyChanges(old_props, new_props, &updated, &removed); + EXPECT_EQ(updated, (MenuPropertyList{"2", "4"})); + EXPECT_EQ(removed, (MenuPropertyList{"3"})); +} diff --git a/chromium/components/dbus/menu/types.cc b/chromium/components/dbus/menu/types.cc index 5694f2dc784..4af3e77c7a7 100644 --- a/chromium/components/dbus/menu/types.cc +++ b/chromium/components/dbus/menu/types.cc @@ -9,6 +9,15 @@ DbusType::~DbusType() = default; +bool DbusType::operator==(const DbusType& other) const { + if (GetSignatureDynamic() != other.GetSignatureDynamic()) + return false; + return IsEqual(other); +} +bool DbusType::operator!=(const DbusType& other) const { + return !(*this == other); +} + DbusBoolean::DbusBoolean(bool value) : value_(value) {} DbusBoolean::DbusBoolean(DbusBoolean&& other) = default; DbusBoolean::~DbusBoolean() = default; @@ -85,6 +94,11 @@ DbusVariant::operator bool() const { return !!value_; } +bool DbusVariant::IsEqual(const DbusType& other_type) const { + const DbusVariant* other = static_cast<const DbusVariant*>(&other_type); + return *value_ == *other->value_; +} + void DbusVariant::Write(dbus::MessageWriter* writer) const { dbus::MessageWriter variant_writer(nullptr); writer->OpenVariant(value_->GetSignatureDynamic(), &variant_writer); @@ -103,6 +117,12 @@ DbusByteArray::DbusByteArray(scoped_refptr<base::RefCountedMemory> value) DbusByteArray::DbusByteArray(DbusByteArray&& other) = default; DbusByteArray::~DbusByteArray() = default; +bool DbusByteArray::IsEqual(const DbusType& other_type) const { + const DbusByteArray* other = static_cast<const DbusByteArray*>(&other_type); + return value_->size() == other->value_->size() && + !memcmp(value_->front(), other->value_->front(), value_->size()); +} + void DbusByteArray::Write(dbus::MessageWriter* writer) const { writer->AppendArrayOfBytes(value_->front(), value_->size()); } diff --git a/chromium/components/dbus/menu/types.h b/chromium/components/dbus/menu/types.h index 3ba18d5d025..feb317737f9 100644 --- a/chromium/components/dbus/menu/types.h +++ b/chromium/components/dbus/menu/types.h @@ -65,6 +65,9 @@ class COMPONENT_EXPORT(DBUS) DbusType { public: virtual ~DbusType(); + bool operator==(const DbusType& other) const; + bool operator!=(const DbusType& other) const; + // Serializes this object to |writer|. virtual void Write(dbus::MessageWriter* writer) const = 0; @@ -75,6 +78,10 @@ class COMPONENT_EXPORT(DBUS) DbusType { // array is empty, then there would be no DbusType instance to get the // signature from. virtual std::string GetSignatureDynamic() const = 0; + + protected: + // This is only safe to call after verifying GetSignatureDynamic() matches. + virtual bool IsEqual(const DbusType& other_type) const = 0; }; template <typename T> @@ -82,8 +89,14 @@ class DbusTypeImpl : public DbusType { public: ~DbusTypeImpl() override {} - // DbusType: std::string GetSignatureDynamic() const override { return T::GetSignature(); } + + protected: + // DbusType: + bool IsEqual(const DbusType& other_type) const override { + const T* other = static_cast<const T*>(&other_type); + return static_cast<const T*>(this)->value_ == other->value_; + } }; class COMPONENT_EXPORT(DBUS) DbusBoolean : public DbusTypeImpl<DbusBoolean> { @@ -98,6 +111,8 @@ class COMPONENT_EXPORT(DBUS) DbusBoolean : public DbusTypeImpl<DbusBoolean> { static std::string GetSignature(); private: + friend class DbusTypeImpl<DbusBoolean>; + bool value_; }; @@ -113,6 +128,8 @@ class COMPONENT_EXPORT(DBUS) DbusInt32 : public DbusTypeImpl<DbusInt32> { static std::string GetSignature(); private: + friend class DbusTypeImpl<DbusInt32>; + int32_t value_; }; @@ -128,6 +145,8 @@ class COMPONENT_EXPORT(DBUS) DbusUint32 : public DbusTypeImpl<DbusUint32> { static std::string GetSignature(); private: + friend class DbusTypeImpl<DbusUint32>; + uint32_t value_; }; @@ -143,6 +162,8 @@ class COMPONENT_EXPORT(DBUS) DbusString : public DbusTypeImpl<DbusString> { static std::string GetSignature(); private: + friend class DbusTypeImpl<DbusString>; + std::string value_; }; @@ -159,6 +180,8 @@ class COMPONENT_EXPORT(DBUS) DbusObjectPath static std::string GetSignature(); private: + friend class DbusTypeImpl<DbusObjectPath>; + dbus::ObjectPath value_; }; @@ -171,14 +194,17 @@ class COMPONENT_EXPORT(DBUS) DbusVariant : public DbusTypeImpl<DbusVariant> { DbusVariant& operator=(DbusVariant&& other); - operator bool() const; + explicit operator bool() const; // DbusType: + bool IsEqual(const DbusType& other_type) const override; void Write(dbus::MessageWriter* writer) const override; static std::string GetSignature(); private: + friend class DbusTypeImpl<DbusVariant>; + std::unique_ptr<DbusType> value_; }; @@ -215,6 +241,8 @@ class COMPONENT_EXPORT(DBUS) DbusArray : public DbusTypeImpl<DbusArray<T>> { } private: + friend class DbusTypeImpl<DbusArray<T>>; + std::vector<T> value_; }; @@ -235,11 +263,14 @@ class COMPONENT_EXPORT(DBUS) DbusByteArray ~DbusByteArray() override; // DbusType: + bool IsEqual(const DbusType& other_type) const override; void Write(dbus::MessageWriter* writer) const override; static std::string GetSignature(); private: + friend class DbusTypeImpl<DbusByteArray>; + scoped_refptr<base::RefCountedMemory> value_; }; @@ -264,6 +295,8 @@ class COMPONENT_EXPORT(DBUS) DbusStruct } private: + friend class DbusTypeImpl<DbusStruct<Ts...>>; + std::tuple<Ts...> value_; }; @@ -276,7 +309,7 @@ template <typename K, typename V> class COMPONENT_EXPORT(DBUS) DbusDictEntry : public DbusTypeImpl<DbusDictEntry<K, V>> { public: - DbusDictEntry(K&& k, V&& v) : k_(std::move(k)), v_(std::move(v)) {} + DbusDictEntry(K&& k, V&& v) : value_{std::move(k), std::move(v)} {} DbusDictEntry(DbusDictEntry<K, V>&& other) = default; ~DbusDictEntry() override = default; @@ -284,8 +317,8 @@ class COMPONENT_EXPORT(DBUS) DbusDictEntry void Write(dbus::MessageWriter* writer) const override { dbus::MessageWriter dict_entry_writer(nullptr); writer->OpenDictEntry(&dict_entry_writer); - k_.Write(&dict_entry_writer); - v_.Write(&dict_entry_writer); + value_.first.Write(&dict_entry_writer); + value_.second.Write(&dict_entry_writer); writer->CloseContainer(&dict_entry_writer); } @@ -294,8 +327,9 @@ class COMPONENT_EXPORT(DBUS) DbusDictEntry } private: - K k_; - V v_; + friend class DbusTypeImpl<DbusDictEntry<K, V>>; + + std::pair<K, V> value_; }; template <typename K, typename V> diff --git a/chromium/components/dbus/menu/types_unittest.cc b/chromium/components/dbus/menu/types_unittest.cc new file mode 100644 index 00000000000..e6ca1457530 --- /dev/null +++ b/chromium/components/dbus/menu/types_unittest.cc @@ -0,0 +1,63 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/dbus/menu/types.h" + +#include "base/memory/ref_counted_memory.h" +#include "dbus/object_path.h" +#include "testing/gtest/include/gtest/gtest.h" + +TEST(DbusTypesTest, GetSignatureDynamic) { + // Basic signatures. + EXPECT_EQ("b", DbusBoolean(false).GetSignatureDynamic()); + EXPECT_EQ("i", DbusInt32(0).GetSignatureDynamic()); + EXPECT_EQ("u", DbusUint32(0).GetSignatureDynamic()); + EXPECT_EQ("s", DbusString("").GetSignatureDynamic()); + EXPECT_EQ("o", DbusObjectPath(dbus::ObjectPath("/")).GetSignatureDynamic()); + EXPECT_EQ("v", MakeDbusVariant(DbusInt32(0)).GetSignatureDynamic()); + EXPECT_EQ("ai", MakeDbusArray(DbusInt32(0)).GetSignatureDynamic()); + std::vector<unsigned char> buf{1, 2, 3}; + EXPECT_EQ("ay", DbusByteArray(base::RefCountedBytes::TakeVector(&buf)) + .GetSignatureDynamic()); + EXPECT_EQ("(ib)", + MakeDbusStruct(DbusInt32(0), DbusBoolean(0)).GetSignatureDynamic()); + EXPECT_EQ( + "{si}", + MakeDbusDictEntry(DbusString(""), DbusInt32(0)).GetSignatureDynamic()); + + // A more complex signature. This is the return type of + // com.canonical.dbusmenu.GetLayout. + EXPECT_EQ("a(ia{sv}av)", + MakeDbusArray(MakeDbusStruct(DbusInt32(0), + MakeDbusArray(MakeDbusDictEntry( + DbusString(""), + MakeDbusVariant(DbusInt32(0)))), + DbusArray<DbusVariant>())) + .GetSignatureDynamic()); +} + +TEST(DbusTypesTest, IsEqual) { + // Types that have different signatures should never be equal. + EXPECT_NE(DbusInt32(0), DbusBoolean(false)); + EXPECT_NE(DbusInt32(0), MakeDbusVariant(DbusInt32(0))); + + // Basic test. + EXPECT_EQ(DbusInt32(3), DbusInt32(3)); + EXPECT_NE(DbusInt32(3), DbusInt32(4)); + + // DbusVariant compares it's pointed-to value, not the pointers themselves. + EXPECT_EQ(MakeDbusVariant(DbusInt32(3)), MakeDbusVariant(DbusInt32(3))); + EXPECT_NE(MakeDbusVariant(DbusInt32(3)), MakeDbusVariant(DbusInt32(4))); + + // DbusByteArray does a deep comparison of its data, not a comparison on + // pointers. + std::vector<unsigned char> buf1{1, 2, 3}; + std::vector<unsigned char> buf2{1, 2, 3}; + EXPECT_EQ(DbusByteArray(base::RefCountedBytes::TakeVector(&buf1)), + DbusByteArray(base::RefCountedBytes::TakeVector(&buf2))); + buf1 = {1, 2, 3}; + buf2 = {3, 2, 1}; + EXPECT_NE(DbusByteArray(base::RefCountedBytes::TakeVector(&buf1)), + DbusByteArray(base::RefCountedBytes::TakeVector(&buf2))); +} |