ui/views/input_protection/README.md
This directory contains the input protection system for the Views framework. The goal of this system is to prevent potentially unintended user interactions with sensitive UI elements. These are elements that perform actions with security, privacy, or system-state implications (e.g., "Allow" buttons on prompts, "Install" buttons for extensions, or "Confirm" buttons for purchases).
By protecting these elements, the system mitigates risks like clickjacking, rapid-fire clicking, and sudden UI appearances (e.g., a dialog popping up right under the user's cursor).
The system consists of a manager and one or more policies:
┌──────────────┐
│ View │
└──────┬───────┘
│
│ Owns
v
┌─────────────────────────────┐
│InputEventActivationProtector│
└──────────────┬──────────────┘
│
│ Delegates to
v
┌──────────────────────────┼──────────────────────────┐
│ │ │
v v v
┌──────────────────────┐ ┌──────────────────────┐ ┌──────────────────────┐
│DefaultInputProtection│ │WindowActivationInput │ │OcclusionAwareInput │
│Policy │ │ProtectionPolicy │ │ProtectionPolicy │
└──────────────────────┘ └──────────────────────┘ └──────────────────────┘
InputEventActivationProtectorThis is the main manager class. It is typically owned by a View that requires
protection (for example, DialogClientView or BubbleFrameView).
IsPossiblyUnintendedInteraction(event, target_view) to check if
an input event should be blocked.InputProtectionPolicy objects and delegates the check
to them. If any policy recommends blocking, the event is blocked.InputProtectionPolicyThe abstract base class for all protection rules. Subclasses must implement:
IsPossiblyUnintendedInteraction(event, target_view, protector): Evaluates if
the event should be blocked.Subclasses can optionally override lifecycle methods to manage their internal state:
OnProtectionStarted(): Called when the protected target becomes visible.OnProtectionStopped(): Called when the protected target is hidden or
destroyed.OnProtectionReset(): Called when a UI change (e.g., layout or window
stationarity change) requires restarting the cooldown.DefaultInputProtectionPolicy:
WindowActivationInputProtectionPolicy:
OcclusionAwareInputProtectionPolicy:
OccludedWidgetInputProtector).To protect a view, add an InputEventActivationProtector member to your view
class.
If you instantiate the protector using the default constructor, it automatically
installs the DefaultInputProtectionPolicy (which handles show cooldown and
click-spam protection).
You can then add additional policies if needed:
// Instantiates with DefaultInputProtectionPolicy installed automatically.
input_protector_ = std::make_unique<InputEventActivationProtector>();
// Add an additional policy (both will be active).
input_protector_->AddPolicy(
std::make_unique<WindowActivationInputProtectionPolicy>(widget));
If you want to use a custom policy instead of the default one (e.g., in tests or for specialized UIs), use the parameterized constructor. This constructor installs only the passed policy and does not install the default policy.
// Instantiates with ONLY the window activation policy.
// DefaultInputProtectionPolicy is NOT installed.
input_protector_ = std::make_unique<InputEventActivationProtector>(
std::make_unique<WindowActivationInputProtectionPolicy>(widget));
// You can still add more policies later if needed:
input_protector_->AddPolicy(std::make_unique<MyCustomPolicy>());
Before handling a sensitive event (e.g., a button click), query the protector:
void MyView::OnButtonPressed(const ui::Event& event) {
if (input_protector_.IsPossiblyUnintendedInteraction(event, this)) {
return; // Block the event
}
// Handle the event...
}
To create a new protection policy:
InputProtectionPolicy.IsPossiblyUnintendedInteraction to define your blocking logic.OnProtectionStarted, OnProtectionStopped, and/or OnProtectionReset to
manage your state (e.g., updating timestamps).views::WidgetObserver) and manage the observation lifecycle.InputEventActivationProtector using
AddPolicy.Example template:
class MyCustomPolicy : public InputProtectionPolicy {
public:
MyCustomPolicy() = default;
~MyCustomPolicy() override = default;
// InputProtectionPolicy:
bool IsPossiblyUnintendedInteraction(
const ui::Event& event,
const View* target_view,
const InputEventActivationProtector& protector) override {
// Return true if the event should be blocked based on your custom logic.
return SomeConditionIsMet(event);
}
void OnProtectionStarted() override {
// Initialize state when target becomes visible.
}
};