blob: 619fadfbb2e45b0ee93d4cef411fe5b2adb3b419 [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 <utility>
#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<web::Event>& event) {
TRACE_EVENT1("cobalt::dom", "PointerState::QueuePointerEvent()", "event",
TRACE_STR_COPY(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<web::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<web::Event> PointerState::GetNextQueuedPointerEvent() {
scoped_refptr<web::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<web::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(), web::Event::kBubbles,
web::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(), web::Event::kBubbles,
web::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 web::DOMException with the name
// InvalidPointerId.
if (active_pointers_.find(pointer_id) == active_pointers_.end()) {
web::DOMException::Raise(web::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()) {
web::DOMException::Raise(web::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 web::DOMException
// with the name InvalidPointerId.
if (active_pointers_.find(pointer_id) == active_pointers_.end()) {
web::DOMException::Raise(web::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();
}
void PointerState::SetClientCoordinates(int32_t pointer_id,
math::Vector2dF position) {
client_coordinates_[pointer_id] = position;
}
base::Optional<math::Vector2dF> PointerState::GetClientCoordinates(
int32_t pointer_id) {
auto client_coordinate = client_coordinates_.find(pointer_id);
if (client_coordinate != client_coordinates_.end()) {
return client_coordinate->second;
}
base::Optional<math::Vector2dF> ret;
return ret;
}
void PointerState::ClearClientCoordinates(int32_t pointer_id) {
client_coordinates_.erase(pointer_id);
}
void PointerState::SetClientTimeStamp(int32_t pointer_id, uint64 time_stamp) {
client_time_stamps_[pointer_id] = time_stamp;
}
base::Optional<uint64> PointerState::GetClientTimeStamp(int32_t pointer_id) {
auto time_stamp = client_time_stamps_.find(pointer_id);
if (time_stamp != client_time_stamps_.end()) {
return time_stamp->second;
}
base::Optional<uint64> ret;
return ret;
}
void PointerState::ClearTimeStamp(int32_t pointer_id) {
client_time_stamps_.erase(pointer_id);
}
void PointerState::SetWasCancelled(int32_t pointer_id) {
client_cancellations_.insert(pointer_id);
}
bool PointerState::GetWasCancelled(int32_t pointer_id) {
auto client_cancellation = client_cancellations_.find(pointer_id);
return client_cancellation != client_cancellations_.end();
}
void PointerState::ClearWasCancelled(int32_t pointer_id) {
client_cancellations_.erase(pointer_id);
}
// static
bool PointerState::CanQueueEvent(const scoped_refptr<web::Event>& event) {
return event->GetWrappableType() == base::GetTypeId<PointerEvent>() ||
event->GetWrappableType() == base::GetTypeId<MouseEvent>() ||
event->GetWrappableType() == base::GetTypeId<WheelEvent>();
}
} // namespace dom
} // namespace cobalt