blob: 7b2b2b4c243b74d53c47da2b45082e94033041a6 [file] [log] [blame]
// Copyright 2017 The Cobalt Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "cobalt/dom/pointer_state.h"
#include <algorithm>
#include "base/trace_event/trace_event.h"
#include "cobalt/dom/mouse_event.h"
#include "cobalt/dom/pointer_event.h"
#include "cobalt/dom/wheel_event.h"
#include "cobalt/script/wrappable.h"
namespace cobalt {
namespace dom {
void PointerState::QueuePointerEvent(const scoped_refptr<Event>& event) {
TRACE_EVENT1("cobalt::dom", "PointerState::QueuePointerEvent()", "event",
event->type().c_str());
// Only accept this for event types that are MouseEvents or known derivatives.
SB_DCHECK(CanQueueEvent(event));
// Queue the event to be handled on the next layout.
pointer_events_.push(event);
}
namespace {
int32_t GetPointerIdFromEvent(const scoped_refptr<Event>& event) {
if (event->GetWrappableType() == base::GetTypeId<PointerEvent>()) {
const PointerEvent* const pointer_event =
base::polymorphic_downcast<const PointerEvent* const>(event.get());
return pointer_event->pointer_id();
}
return 0;
}
} // namespace
scoped_refptr<Event> PointerState::GetNextQueuedPointerEvent() {
scoped_refptr<Event> event;
if (pointer_events_.empty()) {
return event;
}
// Ignore pointer move events when they are succeeded by additional pointer
// move events with the same pointerId.
scoped_refptr<Event> front_event(pointer_events_.front());
bool next_event_is_move_event =
front_event->type() == base::Tokens::pointermove() ||
front_event->type() == base::Tokens::mousemove();
int32_t next_event_pointer_id = GetPointerIdFromEvent(front_event);
int32_t current_event_pointer_id;
bool current_event_is_move_event;
do {
current_event_pointer_id = next_event_pointer_id;
current_event_is_move_event = next_event_is_move_event;
event = pointer_events_.front();
pointer_events_.pop();
if (pointer_events_.empty() || !current_event_is_move_event) {
break;
}
next_event_pointer_id = GetPointerIdFromEvent(event);
if (next_event_pointer_id != current_event_pointer_id) {
break;
}
front_event = pointer_events_.front();
next_event_is_move_event =
(front_event->type() == base::Tokens::pointermove() ||
front_event->type() == base::Tokens::mousemove());
} while (next_event_is_move_event);
return event;
}
void PointerState::SetPendingPointerCaptureTargetOverride(int32_t pointer_id,
Element* element) {
pending_target_override_[pointer_id] = base::AsWeakPtr(element);
}
void PointerState::ClearPendingPointerCaptureTargetOverride(
int32_t pointer_id) {
pending_target_override_.erase(pointer_id);
}
scoped_refptr<HTMLElement> PointerState::GetPointerCaptureOverrideElement(
int32_t pointer_id, PointerEventInit* event_init) {
const scoped_refptr<Window>& view = event_init->view();
scoped_refptr<Element> target_override_element;
// Algorithm for Process Pending Pointer Capture.
// https://www.w3.org/TR/2015/REC-pointerevents-20150224/#process-pending-pointer-capture
auto pending_override = pending_target_override_.find(pointer_id);
auto override = target_override_.find(pointer_id);
if (pending_override != pending_target_override_.end() &&
pending_override->second &&
pending_override->second->node_document() != view->document()) {
// When the pointer capture target override is removed from its
// ownerDocument's tree, clear the pending pointer capture target override
// and pointer capture target override nodes and fire a PointerEvent named
// lostpointercapture at the document.
pending_target_override_.erase(pending_override);
pending_override = pending_target_override_.end();
}
// 1. If the pointer capture target override for this pointer is set and is
// not equal to the pending pointer capture target override, then fire a
// pointer event named lostpointercapture at the pointer capture target
// override node.
if (override != target_override_.end() && override->second &&
(pending_override == pending_target_override_.end() ||
pending_override->second != override->second)) {
override->second->DispatchEvent(
new PointerEvent(base::Tokens::lostpointercapture(), Event::kBubbles,
Event::kNotCancelable, view, *event_init));
}
// 2. If the pending pointer capture target override for this pointer is set
// and is not equal to the pointer capture target override, then fire a
// pointer event named gotpointercapture at the pending pointer capture
// target override.
if (pending_override != pending_target_override_.end() &&
pending_override->second &&
(override == target_override_.end() ||
pending_override->second != override->second)) {
pending_override->second->DispatchEvent(
new PointerEvent(base::Tokens::gotpointercapture(), Event::kBubbles,
Event::kNotCancelable, view, *event_init));
}
// 3. Set the pointer capture target override to the pending pointer capture
// target override, if set. Otherwise, clear the pointer capture target
// override.
if (pending_override != pending_target_override_.end() &&
pending_override->second) {
target_override_[pending_override->first] = pending_override->second;
target_override_element = pending_override->second.get();
} else if (override != target_override_.end()) {
target_override_.erase(override);
}
scoped_refptr<HTMLElement> html_element;
if (target_override_element) {
html_element = target_override_element->AsHTMLElement();
}
return html_element;
}
void PointerState::SetPointerCapture(int32_t pointer_id, Element* element,
script::ExceptionState* exception_state) {
// Algorithm for Setting Pointer Capture
// https://www.w3.org/TR/2015/REC-pointerevents-20150224/#setting-pointer-capture
// 1. If the pointerId provided as the method's argument does not match any of
// the active pointers, then throw a DOMException with the name
// InvalidPointerId.
if (active_pointers_.find(pointer_id) == active_pointers_.end()) {
DOMException::Raise(dom::DOMException::kInvalidPointerIdErr,
exception_state);
return;
}
// 2. If the Element on which this method is invoked does not participate in
// its ownerDocument's tree, throw an exception with the name
// InvalidStateError.
if (!element || !element->owner_document()) {
DOMException::Raise(dom::DOMException::kInvalidStateErr, exception_state);
return;
}
// 3. If the pointer is not in the active buttons state, then terminate these
// steps.
if (pointers_with_active_buttons_.find(pointer_id) ==
pointers_with_active_buttons_.end()) {
return;
}
// 4. For the specified pointerId, set the pending pointer capture target
// override to the Element on which this method was invoked.
SetPendingPointerCaptureTargetOverride(pointer_id, element);
}
void PointerState::ReleasePointerCapture(
int32_t pointer_id, Element* element,
script::ExceptionState* exception_state) {
// Algorithm for Releasing Pointer Capture
// https://www.w3.org/TR/2015/REC-pointerevents-20150224/#releasing-pointer-capture
// 1. If the pointerId provided as the method's argument does not match any of
// the active pointers and these steps are not being invoked as a result of
// the implicit release of pointer capture, then throw a DOMException with the
// name InvalidPointerId.
if (active_pointers_.find(pointer_id) == active_pointers_.end()) {
DOMException::Raise(dom::DOMException::kInvalidPointerIdErr,
exception_state);
return;
}
// 2. If pointer capture is not currently set for the specified pointer, then
// terminate these steps.
auto pending_override = pending_target_override_.find(pointer_id);
if (pending_override == pending_target_override_.end()) {
return;
}
// 3. If the pointer capture target override for the specified pointerId is
// not the Element on which this method was invoked, then terminate these
// steps.
if (pending_override->second.get() != element) {
return;
}
// 4. For the specified pointerId, clear the pending pointer capture target
// override, if set.
ClearPendingPointerCaptureTargetOverride(pointer_id);
}
bool PointerState::HasPointerCapture(int32_t pointer_id, Element* element) {
auto pending_override = pending_target_override_.find(pointer_id);
if (pending_override != pending_target_override_.end()) {
return pending_override->second.get() == element;
}
return false;
}
void PointerState::SetActiveButtonsState(int32_t pointer_id, uint16_t buttons) {
if (buttons) {
pointers_with_active_buttons_.insert(pointer_id);
} else {
pointers_with_active_buttons_.erase(pointer_id);
}
}
void PointerState::SetActive(int32_t pointer_id) {
active_pointers_.insert(pointer_id);
}
void PointerState::ClearActive(int32_t pointer_id) {
active_pointers_.erase(pointer_id);
}
void PointerState::ClearForShutdown() {
{
decltype(pointer_events_) empty_queue;
std::swap(pointer_events_, empty_queue);
}
target_override_.clear();
pending_target_override_.clear();
active_pointers_.clear();
pointers_with_active_buttons_.clear();
}
// static
bool PointerState::CanQueueEvent(const scoped_refptr<Event>& event) {
return event->GetWrappableType() == base::GetTypeId<PointerEvent>() ||
event->GetWrappableType() == base::GetTypeId<MouseEvent>() ||
event->GetWrappableType() == base::GetTypeId<WheelEvent>();
}
} // namespace dom
} // namespace cobalt