---
# System prepended metadata

title: UI Property
tags: [Chromium-General, elkurin-daily-note]

---

# UI Property
UI property is a concept used in Chromium to set a property value accessible from everywhere who has a owner of the property such as aura::Window or ui::View.  
This is very convenient to handle a many properties, features tied to an object.  
Let's see its mechanism.

## ClassProperty
[ClassProperty](https://source.chromium.org/chromium/chromium/src/+/main:ui/base/class_property.h;l=70;drc=f5bdc89c7395ed24f1b8d196a3bdd6232d5bf771) has following structure.

```cpp=
template<typename T>
struct ClassProperty {
  T default_value;
  const char* name;
  bool cascading = false;
  PropertyDeallocator deallocator;
};
```
The type of the value can be specified as a template parameter `T`. This `T` must be smaller than `int64_t` due to the restriction of the maximum property size as each object will carry many properties.  
Each property has a `default_value` and this is registered by default.  
`name` is usually the name of parameter we use such as `kImemrsiveIsActive`.   
`cascade` is used for the property that the accessor would like to search up an instance hierarchy for the first defined property, not only for the specified property. I seldomly see this in my experiece.  
`deallocator` is used when `T` is heap-allocated type, not like bool nor int.

## How to define property key
We can define a property key like the following:
```cpp=
// .h file
COMPONENT_EXPORT(CHROMEOS_UI_BASE)
extern const ui::ClassProperty<bool>* const kImmersiveIsActive;

// .cc file
DEFINE_UI_CLASS_PROPERTY_KEY(bool, kImmersiveIsActive, false)
```

The macro method `DEFINE_UI_CLASS_PROPERTY_KEY` is defined as follows:
```cpp=
#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, false,     \
                                                  nullptr};                  \
  const ::ui::ClassProperty<TYPE>* const NAME = &NAME##_Value;
```
By calling this macro, we construct a const param with type of `ui::ClassProperty<TYPE>` named `NAME`.  
ClassProperty type is described in the previous section.  
`DEFAULT` must be the same type as `TYPE`, if not it fails to match template.  

There is similar, but a bit different macros:
```cpp=
#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;
```
As you can see from the code, this is used for a property with a heap allocaed object as `TYPE` so that is must be freed by the owner. The class is an owner of this value, so the macro name is `DEFINED_OWNED_UI_CLSS_PROPERTY_KEY`.

## How to set/get/clear property
We have following methods to set, get or clear property.
```cpp=
template <typename T>
void SetProperty(const ClassProperty<T>* property, T value);

template <typename T>
T GetProperty(const ClassProperty<T>* property) const;

template <typename T>
void ClearProperty(const ClassProperty<T>* property);
```

The usage is quite simple.  
Here are the examples:
```cpp=
// Set
GetContentWindow()->SetProperty(chromeos::kImmersiveIsActive, enabled);

// Get
return window()->widget()->GetNativeWindow()->GetProperty(
  chromeos::kImmersiveIsActive);

// Clear
window->ClearProperty(aura::client::kModalKey);
```

## Implementation
Let's see the SetProperty impl:
```cpp=
template <typename T>
void PropertyHandler::SetProperty(const ClassProperty<T*>* property,
                                  const T& value) {
  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, std::make_unique<T>(value));
  }
}
```
The behavior depends on whether there is a value already set.  
If there is one, `old` exists, and its value is overriden by new `value`, and then trigger AfterPropertyChange.  
If not, it creates unique_ptr with `value` and trigger property settings pass, and eventually comes to [SetPropertyInternal](https://source.chromium.org/chromium/chromium/src/+/main:ui/base/class_property.cc;l=31;drc=f5bdc89c7395ed24f1b8d196a3bdd6232d5bf771) with relatively straight forwrd way which calls AfterPropertyChange as well.  
This [AfterPropertyChange](https://source.chromium.org/chromium/chromium/src/+/main:ui/base/class_property.h;l=142;drc=f5bdc89c7395ed24f1b8d196a3bdd6232d5bf771) is a virtual function and overriden by, for example, [aura::Window](https://source.chromium.org/chromium/chromium/src/+/main:ui/aura/window.cc;l=935;drc=f5bdc89c7395ed24f1b8d196a3bdd6232d5bf771).

```cpp=
void Window::AfterPropertyChange(const void* key, int64_t old_value) {
  for (WindowObserver& observer : observers_)
    observer.OnWindowPropertyChanged(this, key, old_value);
}
```
When the property is updated from somewhere, it triggers WindowObserver::OnWindowPropertyChanged so that propery change will be propagated to everywhere who is observing this specific window.
