| // 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 |