summaryrefslogtreecommitdiff
path: root/chromium/components/dbus
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2020-01-20 13:40:20 +0100
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2020-01-22 12:41:23 +0000
commit7961cea6d1041e3e454dae6a1da660b453efd238 (patch)
treec0eeb4a9ff9ba32986289c1653d9608e53ccb444 /chromium/components/dbus
parentb7034d0803538058e5c9d904ef03cf5eab34f6ef (diff)
downloadqtwebengine-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.gn11
-rw-r--r--chromium/components/dbus/menu/DEPS2
-rw-r--r--chromium/components/dbus/menu/menu.cc251
-rw-r--r--chromium/components/dbus/menu/menu.h35
-rw-r--r--chromium/components/dbus/menu/menu_property_list.cc123
-rw-r--r--chromium/components/dbus/menu/menu_property_list.h35
-rw-r--r--chromium/components/dbus/menu/menu_property_list_unittest.cc361
-rw-r--r--chromium/components/dbus/menu/types.cc20
-rw-r--r--chromium/components/dbus/menu/types.h48
-rw-r--r--chromium/components/dbus/menu/types_unittest.cc63
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)));
+}