diff --git a/docs/best-practices/ui-views.md b/docs/best-practices/ui-views.md index e287e02016b..6551975faa8 100644 --- a/docs/best-practices/ui-views.md +++ b/docs/best-practices/ui-views.md @@ -55,3 +55,36 @@ void MyView::OnTabStripModelDestroyed(TabStripModel* model) { --- + + +## ✅ Prefer `views::AsViewClass` and `views::IsViewClass` Over `static_cast<>` for View Downcasting + +**Use `views::AsViewClass()` and `views::IsViewClass()` instead of `static_cast()` when downcasting `views::View` pointers.** + +Since Chromium disables RTTI, `dynamic_cast` is unavailable. `AsViewClass` and `IsViewClass` fill that role for the views hierarchy: they walk the metadata chain registered via `METADATA_HEADER` and catch — at compile time — target classes that have omitted `METADATA_HEADER` or left a metadata path unoverridden. At runtime, `AsViewClass` returns `nullptr` on a type mismatch, including for partially constructed views that have not yet installed their own class metadata. `static_cast` does none of this and silently produces a pointer whose dereference is undefined behaviour on a type mismatch. + +```cpp +// ❌ WRONG - no type check; UB on dereference if type assumption is wrong. +// static_cast also cannot detect a partially constructed object — +// e.g. when a Brave-specific subclass overrides a Chromium view and +// the object is accessed before the subclass metadata is installed, +// UBSan will not catch the invalid access. +auto* my_view = static_cast(some_view); +my_view->DoSomething(); + +// ✅ CORRECT - metadata chain walk returns nullptr for a partially constructed +// object or any type mismatch, making it safe under UBSan +auto* my_view = views::AsViewClass(some_view); +if (!my_view) { + return; +} +my_view->DoSomething(); + +// ✅ CORRECT - type predicate +if (views::IsViewClass(some_view)) { + // ... +} +``` + +--- +