blob: 6ed158cfff4ad5b816a6bebdc50e1d7fa4d23b8a [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_DOM_POPOVER_DATA_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_DOM_POPOVER_DATA_H_
#include "base/check_op.h"
#include "third_party/blink/renderer/core/dom/element.h"
#include "third_party/blink/renderer/core/dom/element_rare_data_field.h"
#include "third_party/blink/renderer/core/html/closewatcher/close_watcher.h"
#include "third_party/blink/renderer/core/html/forms/html_select_list_element.h"
#include "third_party/blink/renderer/core/html_element_type_helpers.h"
#include "third_party/blink/renderer/core/inspector/console_message.h"
#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
namespace blink {
enum class PopoverVisibilityState {
kHidden,
kShowing,
};
using PopoverHoverShowMap =
HeapHashMap<WeakMember<const HTMLFormControlElement>, TaskHandle>;
class PopoverData final : public GarbageCollected<PopoverData>,
public ElementRareDataField {
public:
PopoverData() = default;
PopoverData(const PopoverData&) = delete;
PopoverData& operator=(const PopoverData&) = delete;
PopoverVisibilityState visibilityState() const { return visibility_state_; }
void setVisibilityState(PopoverVisibilityState visibility_state) {
visibility_state_ = visibility_state;
}
PopoverValueType type() const { return type_; }
void setType(PopoverValueType type) {
type_ = type;
DCHECK_NE(type, PopoverValueType::kNone)
<< "Remove PopoverData rather than setting kNone type";
}
Element* invoker() const { return invoker_.Get(); }
void setInvoker(Element* element) { invoker_ = element; }
Element* previouslyFocusedElement() const {
return previously_focused_element_.Get();
}
void setPreviouslyFocusedElement(Element* element) {
previously_focused_element_ = element;
}
bool hasPendingToggleEventTask() const {
return pending_toggle_event_task_.IsActive();
}
void cancelPendingToggleEventTask() { pending_toggle_event_task_.Cancel(); }
void setPendingToggleEventTask(TaskHandle&& task) {
DCHECK(!pending_toggle_event_task_.IsActive());
pending_toggle_event_task_ = std::move(task);
}
bool pendingToggleEventStartedClosed() const {
DCHECK(hasPendingToggleEventTask());
return pending_toggle_event_started_closed_;
}
void setPendingToggleEventStartedClosed(bool was_closed) {
DCHECK(!hasPendingToggleEventTask());
pending_toggle_event_started_closed_ = was_closed;
}
class ScopedStartShowingOrHiding {
STACK_ALLOCATED();
public:
explicit ScopedStartShowingOrHiding(const Element& popover,
bool show_warning = true)
: popover_(popover),
was_set_(popover.GetPopoverData()->hiding_or_showing_this_popover_) {
if (was_set_ && show_warning) {
popover_.GetDocument().AddConsoleMessage(MakeGarbageCollected<
ConsoleMessage>(
mojom::blink::ConsoleMessageSource::kOther,
mojom::blink::ConsoleMessageLevel::kWarning,
"The `beforetoggle` event handler for a popover triggered another "
"popover to be shown or hidden. This is not recommended."));
} else {
popover_.GetPopoverData()->hiding_or_showing_this_popover_ = true;
}
}
~ScopedStartShowingOrHiding() {
if (!was_set_ && popover_.GetPopoverData()) {
popover_.GetPopoverData()->hiding_or_showing_this_popover_ = false;
}
}
explicit operator bool() const { return was_set_; }
private:
const Element& popover_;
bool was_set_;
};
PopoverHoverShowMap& hoverShowTasks() { return hover_show_tasks_; }
void setHoverHideTask(TaskHandle&& task) {
if (hover_hide_task_.IsActive()) {
hover_hide_task_.Cancel();
}
hover_hide_task_ = std::move(task);
}
HTMLSelectListElement* ownerSelectListElement() const {
return owner_select_list_element_.Get();
}
void setOwnerSelectListElement(HTMLSelectListElement* element) {
owner_select_list_element_ = element;
}
CloseWatcher* closeWatcher() { return close_watcher_.Get(); }
void setCloseWatcher(CloseWatcher* close_watcher) {
close_watcher_ = close_watcher;
}
void Trace(Visitor* visitor) const override {
visitor->Trace(invoker_);
visitor->Trace(previously_focused_element_);
visitor->Trace(hover_show_tasks_);
visitor->Trace(owner_select_list_element_);
visitor->Trace(close_watcher_);
ElementRareDataField::Trace(visitor);
}
private:
PopoverVisibilityState visibility_state_ = PopoverVisibilityState::kHidden;
PopoverValueType type_ = PopoverValueType::kNone;
WeakMember<Element> invoker_;
WeakMember<Element> previously_focused_element_;
// Any pending 'toggle' event waiting to be fired. Used for coalescing
// behavior so that only one such event is fired.
TaskHandle pending_toggle_event_task_;
bool pending_toggle_event_started_closed_;
// True when we're in the middle of trying to hide/show this popover.
bool hiding_or_showing_this_popover_;
// Map from elements with the 'popovertarget' attribute and
// `popovertargetaction=hover` to a task that will show the popover after a
// delay.
PopoverHoverShowMap hover_show_tasks_;
// A task that hides the popover after a delay.
TaskHandle hover_hide_task_;
WeakMember<HTMLSelectListElement> owner_select_list_element_;
Member<CloseWatcher> close_watcher_;
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_DOM_POPOVER_DATA_H_