/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// Keeps a map between pointerId and element that currently capturing pointer // with such pointerId. If pointerId is absent in this map then nobody is // capturing it. Additionally keep information about pending capturing content. static nsClassHashtable<nsUint32HashKey, PointerCaptureInfo>*
sPointerCaptureList;
// Keeps information about pointers such as pointerId, activeState, pointerType, // primaryState static nsClassHashtable<nsUint32HashKey, PointerInfo>* sActivePointersIds;
// Keeps track of which BrowserParent requested pointer capture for a pointer // id. static nsTHashMap<nsUint32HashKey, BrowserParent*>*
sPointerCaptureRemoteTargetTable = nullptr;
/* static */ void PointerEventHandler::InitializeStatics() {
MOZ_ASSERT(!sPointerCaptureList, "InitializeStatics called multiple times!");
sPointerCaptureList = new nsClassHashtable<nsUint32HashKey, PointerCaptureInfo>;
sActivePointersIds = new nsClassHashtable<nsUint32HashKey, PointerInfo>; if (XRE_IsParentProcess()) {
sPointerCaptureRemoteTargetTable = new nsTHashMap<nsUint32HashKey, BrowserParent*>;
}
}
/* static */ void PointerEventHandler::UpdateActivePointerState(WidgetMouseEvent* aEvent,
nsIContent* aTargetContent) { if (!aEvent) { return;
} switch (aEvent->mMessage) { case eMouseEnterIntoWidget: // In this case we have to know information about available mouse pointers
sActivePointersIds->InsertOrUpdate(
aEvent->pointerId,
MakeUnique<PointerInfo>(false, aEvent->mInputSource, true, false,
nullptr));
MaybeCacheSpoofedPointerID(aEvent->mInputSource, aEvent->pointerId); break; case ePointerDown: // In this case we switch pointer to active state if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) { // XXXedgar, test could possibly synthesize a mousedown event on a // coordinate outside the browser window and cause aTargetContent to be // nullptr, not sure if this also happens on real usage.
sActivePointersIds->InsertOrUpdate(
pointerEvent->pointerId,
MakeUnique<PointerInfo>( true, pointerEvent->mInputSource, pointerEvent->mIsPrimary,
pointerEvent->mFromTouchEvent,
aTargetContent ? aTargetContent->OwnerDoc() : nullptr));
MaybeCacheSpoofedPointerID(pointerEvent->mInputSource,
pointerEvent->pointerId);
} break; case ePointerCancel: // pointercancel means a pointer is unlikely to continue to produce // pointer events. In that case, we should turn off active state or remove // the pointer from active pointers. case ePointerUp: // In this case we remove information about pointer or turn off active // state if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) { if (pointerEvent->mInputSource !=
MouseEvent_Binding::MOZ_SOURCE_TOUCH) {
sActivePointersIds->InsertOrUpdate(
pointerEvent->pointerId,
MakeUnique<PointerInfo>(false, pointerEvent->mInputSource,
pointerEvent->mIsPrimary,
pointerEvent->mFromTouchEvent, nullptr));
} else {
sActivePointersIds->Remove(pointerEvent->pointerId);
}
} break; case eMouseExitFromWidget: // In this case we have to remove information about disappeared mouse // pointers
sActivePointersIds->Remove(aEvent->pointerId); break; default:
MOZ_ASSERT_UNREACHABLE("event has invalid type"); break;
}
}
// When fingerprinting resistance is enabled, we need to map other pointer // ids into the spoofed one. We don't have to do the mapping if the capture // info exists for the non-spoofed pointer id because of we won't allow // content to set pointer capture other than the spoofed one. Thus, it must be // from chrome if the capture info exists in this case. And we don't have to // do anything if the pointer id is the same as the spoofed one. if (nsContentUtils::ShouldResistFingerprinting("Efficiency Check",
RFPTarget::PointerId) &&
aEvent->pointerId != (uint32_t)GetSpoofedPointerIdForRFP() &&
!captureInfo) {
PointerCaptureInfo* spoofedCaptureInfo =
GetPointerCaptureInfo(GetSpoofedPointerIdForRFP());
// We need to check the target element's document should resist // fingerprinting. If not, we don't need to send a capture event // since the capture info of the original pointer id doesn't exist // in this case. if (!spoofedCaptureInfo || !spoofedCaptureInfo->mPendingElement ||
!spoofedCaptureInfo->mPendingElement->OwnerDoc()
->ShouldResistFingerprinting(RFPTarget::PointerEvents)) { return;
}
captureInfo = spoofedCaptureInfo;
}
if (!captureInfo ||
captureInfo->mPendingElement == captureInfo->mOverrideElement) { return;
}
// Update captureInfo before dispatching event since sPointerCaptureList may // be changed in the pointer event listener.
captureInfo->mOverrideElement = captureInfo->mPendingElement; if (captureInfo->Empty()) {
sPointerCaptureList->Remove(aEvent->pointerId);
}
if (overrideElement) {
DispatchGotOrLostPointerCaptureEvent(/* aIsGotCapture */ false, aEvent,
overrideElement);
} if (pendingElement) {
DispatchGotOrLostPointerCaptureEvent(/* aIsGotCapture */ true, aEvent,
pendingElement);
}
// If nobody captures the pointer and the pointer will not be removed, we need // to dispatch pointer boundary events if the pointer will keep hovering over // somewhere even after the pointer is up. // XXX Do we need to check whether there is new pending pointer capture // element? But if there is, what should we do? if (overrideElement && !pendingElement && aEvent->mWidget &&
aEvent->mMessage != ePointerCancel &&
(aEvent->mMessage != ePointerUp || aEvent->InputSourceSupportsHover())) {
aEvent->mSynthesizeMoveAfterDispatch = true;
}
}
// XXX If the pointer is already over a document in different process, we // cannot synthesize the pointermove/mousemove on the document since // dispatching events to the parent process is currently allowed only in // automation.
nsEventStatus eventStatus = nsEventStatus_eIgnore;
widget->DispatchEvent(&event, eventStatus);
}
/* static */ void PointerEventHandler::ImplicitlyCapturePointer(nsIFrame* aFrame,
WidgetEvent* aEvent) {
MOZ_ASSERT(aEvent->mMessage == ePointerDown); if (!aFrame || !IsPointerEventImplicitCaptureForTouchEnabled()) { return;
}
WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent();
NS_WARNING_ASSERTION(pointerEvent, "Call ImplicitlyCapturePointer with non-pointer event"); if (!pointerEvent->mFromTouchEvent) { // We only implicitly capture the pointer for touch device. return;
}
nsCOMPtr<nsIContent> target;
aFrame->GetContentForEvent(aEvent, getter_AddRefs(target)); while (target && !target->IsElement()) {
target = target->GetParent();
} if (NS_WARN_IF(!target)) { return;
}
RequestPointerCaptureById(pointerEvent->pointerId, target->AsElement());
}
/* static */
Element* PointerEventHandler::GetPointerCapturingElement(
WidgetGUIEvent* aEvent) { if ((aEvent->mClass != ePointerEventClass &&
aEvent->mClass != eMouseEventClass) ||
aEvent->mMessage == ePointerDown || aEvent->mMessage == eMouseDown) { // Pointer capture should only be applied to all pointer events and mouse // events except ePointerDown and eMouseDown; return nullptr;
}
// PointerEventHandler may synthesize ePointerMove event before releasing the // mouse capture (it's done by a default handler of eMouseUp) after handling // ePointerUp. Then, we need to dispatch pointer boundary events for the // element under the pointer to emulate a pointer move after a pointer // capture. Therefore, we need to ignore the capturing element if the event // dispatcher requests it. if (aEvent->ShouldIgnoreCapturingContent()) { return nullptr;
}
/* static */ void PointerEventHandler::ReleaseIfCaptureByDescendant(nsIContent* aContent) { // We should check that aChild does not contain pointer capturing elements. // If it does we should release the pointer capture for the elements. if (!sPointerCaptureList->IsEmpty()) { for (constauto& entry : *sPointerCaptureList) {
PointerCaptureInfo* data = entry.GetWeak(); if (data && data->mPendingElement &&
data->mPendingElement->IsInclusiveDescendantOf(aContent)) {
ReleasePointerCaptureById(entry.GetKey());
}
}
}
}
/* static */ void PointerEventHandler::PreHandlePointerEventsPreventDefault(
WidgetPointerEvent* aPointerEvent, WidgetGUIEvent* aMouseOrTouchEvent) { if (!aPointerEvent->mIsPrimary || aPointerEvent->mMessage == ePointerDown) { return;
}
PointerInfo* pointerInfo = nullptr; if (!sActivePointersIds->Get(aPointerEvent->pointerId, &pointerInfo) ||
!pointerInfo) { // The PointerInfo for active pointer should be added for normal cases. But // in some cases, we may receive mouse events before adding PointerInfo in // sActivePointersIds. (e.g. receive mousemove before // eMouseEnterIntoWidget). In these cases, we could ignore them because they // are not the events between a DefaultPrevented pointerdown and the // corresponding pointerup. return;
} if (!pointerInfo->mPreventMouseEventByContent) { return;
}
aMouseOrTouchEvent->PreventDefault(false);
aMouseOrTouchEvent->mFlags.mOnlyChromeDispatch = true; if (aPointerEvent->mMessage == ePointerUp) {
pointerInfo->mPreventMouseEventByContent = false;
}
}
/* static */ void PointerEventHandler::PostHandlePointerEventsPreventDefault(
WidgetPointerEvent* aPointerEvent, WidgetGUIEvent* aMouseOrTouchEvent) { if (!aPointerEvent->mIsPrimary || aPointerEvent->mMessage != ePointerDown ||
!aPointerEvent->DefaultPreventedByContent()) { return;
}
PointerInfo* pointerInfo = nullptr; if (!sActivePointersIds->Get(aPointerEvent->pointerId, &pointerInfo) ||
!pointerInfo) { // We already added the PointerInfo for active pointer when // PresShell::HandleEvent handling pointerdown event. #ifdef DEBUG
MOZ_CRASH("Got ePointerDown w/o active pointer info!!"); #endif// #ifdef DEBUG return;
} // PreventDefault only applied for active pointers. if (!pointerInfo->mActiveState) { return;
}
aMouseOrTouchEvent->PreventDefault(false);
aMouseOrTouchEvent->mFlags.mOnlyChromeDispatch = true;
pointerInfo->mPreventMouseEventByContent = true;
}
EventMessage pointerMessage = eVoidEvent; if (aMouseOrTouchEvent->mClass == eMouseEventClass) {
WidgetMouseEvent* mouseEvent = aMouseOrTouchEvent->AsMouseEvent(); // Don't dispatch pointer events caused by a mouse when simulating touch // devices in RDM.
Document* doc = aShell->GetDocument(); if (!doc) { return;
}
BrowsingContext* bc = doc->GetBrowsingContext(); if (bc && bc->TouchEventsOverride() == TouchEventsOverride::Enabled &&
bc->InRDMPane()) { return;
}
// 1. If it is not mouse then it is likely will come as touch event // 2. We don't synthesize pointer events for those events that are not // dispatched to DOM. if (!mouseEvent->convertToPointer ||
!aMouseOrTouchEvent->IsAllowedToDispatchDOMEvent()) { return;
}
pointerMessage = PointerEventHandler::ToPointerEventMessage(mouseEvent); if (pointerMessage == eVoidEvent) { return;
}
WidgetPointerEvent event(*mouseEvent);
InitPointerEventFromMouse(&event, mouseEvent, pointerMessage);
event.convertToPointer = mouseEvent->convertToPointer = false;
RefPtr<PresShell> shell(aShell); if (!aEventTargetFrame) {
shell = PresShell::GetShellForEventTarget(nullptr, aEventTargetContent); if (!shell) { return;
}
}
PreHandlePointerEventsPreventDefault(&event, aMouseOrTouchEvent); // Dispatch pointer event to the same target which is found by the // corresponding mouse event.
shell->HandleEventWithTarget(&event, aEventTargetFrame, aEventTargetContent,
aStatus, true, aMouseOrTouchEventTarget);
PostHandlePointerEventsPreventDefault(&event, aMouseOrTouchEvent); // If pointer capture is released, we need to synthesize eMouseMove to // dispatch mouse boundary events later.
mouseEvent->mSynthesizeMoveAfterDispatch |=
event.mSynthesizeMoveAfterDispatch;
} elseif (aMouseOrTouchEvent->mClass == eTouchEventClass) {
WidgetTouchEvent* touchEvent = aMouseOrTouchEvent->AsTouchEvent(); // loop over all touches and dispatch pointer events on each touch // copy the event
pointerMessage = PointerEventHandler::ToPointerEventMessage(touchEvent); if (pointerMessage == eVoidEvent) { return;
}
RefPtr<PresShell> shell(aShell); for (uint32_t i = 0; i < touchEvent->mTouches.Length(); ++i) {
Touch* touch = touchEvent->mTouches[i]; if (!TouchManager::ShouldConvertTouchToPointer(touch, touchEvent)) { continue;
}
InitPointerEventFromTouch(event, *touchEvent, *touch);
event.convertToPointer = touch->convertToPointer = false;
event.mCoalescedWidgetEvents = touch->mCoalescedWidgetEvents; if (aMouseOrTouchEvent->mMessage == eTouchStart) { // We already did hit test for touchstart in PresShell. We should // dispatch pointerdown to the same target as touchstart.
nsCOMPtr<nsIContent> content =
nsIContent::FromEventTargetOrNull(touch->mTarget); if (!content) { continue;
}
PreHandlePointerEventsPreventDefault(&event, aMouseOrTouchEvent);
shell->HandleEventWithTarget(&event, frame, content, aStatus, true,
aMouseOrTouchEventTarget);
PostHandlePointerEventsPreventDefault(&event, aMouseOrTouchEvent);
} else { // We didn't hit test for other touch events. Spec doesn't mention that // all pointer events should be dispatched to the same target as their // corresponding touch events. Call PresShell::HandleEvent so that we do // hit test for pointer events. // FIXME: If aDontRetargetEvents is true and the event is fired on // different document, we cannot track the pointer event target when // it's removed from the tree.
PreHandlePointerEventsPreventDefault(&event, aMouseOrTouchEvent);
shell->HandleEvent(aEventTargetFrame, &event, aDontRetargetEvents,
aStatus);
PostHandlePointerEventsPreventDefault(&event, aMouseOrTouchEvent);
}
}
}
}
/* static */ void PointerEventHandler::NotifyDestroyPresContext(
nsPresContext* aPresContext) { // Clean up pointer capture info for (auto iter = sPointerCaptureList->Iter(); !iter.Done(); iter.Next()) {
PointerCaptureInfo* data = iter.UserData();
MOZ_ASSERT(data, "how could we have a null PointerCaptureInfo here?"); if (data->mPendingElement &&
data->mPendingElement->GetPresContext(Element::eForComposedDoc) ==
aPresContext) {
data->mPendingElement = nullptr;
} if (data->mOverrideElement &&
data->mOverrideElement->GetPresContext(Element::eForComposedDoc) ==
aPresContext) {
data->mOverrideElement = nullptr;
} if (data->Empty()) {
iter.Remove();
}
} // Clean up active pointer info for (auto iter = sActivePointersIds->Iter(); !iter.Done(); iter.Next()) {
PointerInfo* data = iter.UserData();
MOZ_ASSERT(data, "how could we have a null PointerInfo here?"); if (data->mActiveDocument &&
data->mActiveDocument->GetPresContext() == aPresContext) {
iter.Remove();
}
}
}
bool PointerEventHandler::IsDragAndDropEnabled(WidgetMouseEvent& aEvent) { #ifdef XP_WIN if (StaticPrefs::dom_w3c_pointer_events_dispatch_by_pointer_messages()) { // WM_POINTER does not support drag and drop, see bug 1692277 return (aEvent.mInputSource != dom::MouseEvent_Binding::MOZ_SOURCE_PEN &&
aEvent.mReason != WidgetMouseEvent::eSynthesized); // bug 1692151
} #endif returntrue;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.