diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-15 03:34:50 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-15 03:34:50 +0000 |
commit | def92d1b8e9d373e2f6f27c366d578d97d8960c6 (patch) | |
tree | 2ef34b9ad8bb9a9220e05d60352558b15f513894 /dom/events | |
parent | Adding debian version 125.0.3-1. (diff) | |
download | firefox-def92d1b8e9d373e2f6f27c366d578d97d8960c6.tar.xz firefox-def92d1b8e9d373e2f6f27c366d578d97d8960c6.zip |
Merging upstream version 126.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/events')
26 files changed, 523 insertions, 597 deletions
diff --git a/dom/events/Clipboard.cpp b/dom/events/Clipboard.cpp index b163bc816f..b797f93961 100644 --- a/dom/events/Clipboard.cpp +++ b/dom/events/Clipboard.cpp @@ -132,6 +132,11 @@ class ClipboardGetCallbackForRead final : public ClipboardGetCallback { } RefPtr<Promise> p(std::move(mPromise)); + if (entries.IsEmpty()) { + p->MaybeResolve(nsTArray<RefPtr<ClipboardItem>>{}); + return NS_OK; + } + // We currently only support one clipboard item. p->MaybeResolve( AutoTArray<RefPtr<ClipboardItem>, 1>{MakeRefPtr<ClipboardItem>( diff --git a/dom/events/EventDispatcher.cpp b/dom/events/EventDispatcher.cpp index dd3de015b4..61e5adf6f1 100644 --- a/dom/events/EventDispatcher.cpp +++ b/dom/events/EventDispatcher.cpp @@ -47,6 +47,7 @@ #include "mozilla/dom/SimpleGestureEvent.h" #include "mozilla/dom/ScriptSettings.h" #include "mozilla/dom/StorageEvent.h" +#include "mozilla/dom/TextEvent.h" #include "mozilla/dom/TimeEvent.h" #include "mozilla/dom/TouchEvent.h" #include "mozilla/dom/TransitionEvent.h" @@ -1141,9 +1142,8 @@ nsresult EventDispatcher::Dispatch(EventTarget* aTarget, if (!postVisitor.mDOMEvent) { // This is tiny bit slow, but happens only once per event. // Similar code also in EventListenerManager. - nsCOMPtr<EventTarget> et = aEvent->mOriginalTarget; - RefPtr<Event> event = - EventDispatcher::CreateEvent(et, aPresContext, aEvent, u""_ns); + RefPtr<Event> event = EventDispatcher::CreateEvent( + aEvent->mOriginalTarget, aPresContext, aEvent, u""_ns); event.swap(postVisitor.mDOMEvent); } nsAutoString typeStr; @@ -1151,12 +1151,11 @@ nsresult EventDispatcher::Dispatch(EventTarget* aTarget, AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING( "EventDispatcher::Dispatch", OTHER, typeStr); - nsCOMPtr<nsIDocShell> docShell; - docShell = nsContentUtils::GetDocShellForEventTarget(aEvent->mTarget); MarkerInnerWindowId innerWindowId; - if (nsCOMPtr<nsPIDOMWindowInner> inner = - do_QueryInterface(aEvent->mTarget->GetOwnerGlobal())) { - innerWindowId = MarkerInnerWindowId{inner->WindowID()}; + if (nsIGlobalObject* global = aEvent->mTarget->GetOwnerGlobal()) { + if (nsPIDOMWindowInner* inner = global->GetAsInnerWindow()) { + innerWindowId = MarkerInnerWindowId{inner->WindowID()}; + } } struct DOMEventMarker { @@ -1206,7 +1205,7 @@ nsresult EventDispatcher::Dispatch(EventTarget* aTarget, auto startTime = TimeStamp::Now(); profiler_add_marker("DOMEvent", geckoprofiler::category::DOM, - {MarkerTiming::IntervalStart(), + {MarkerTiming::IntervalStart(startTime), MarkerInnerWindowId(innerWindowId)}, DOMEventMarker{}, typeStr, target, startTime, aEvent->mTimeStamp); @@ -1406,6 +1405,9 @@ nsresult EventDispatcher::DispatchDOMEvent(EventTarget* aTarget, case eEditorInputEventClass: return NS_NewDOMInputEvent(aOwner, aPresContext, aEvent->AsEditorInputEvent()); + case eLegacyTextEventClass: + return NS_NewDOMTextEvent(aOwner, aPresContext, + aEvent->AsLegacyTextEvent()); case eDragEventClass: return NS_NewDOMDragEvent(aOwner, aPresContext, aEvent->AsDragEvent()); case eClipboardEventClass: @@ -1450,10 +1452,15 @@ nsresult EventDispatcher::DispatchDOMEvent(EventTarget* aTarget, if (aEventType.LowerCaseEqualsLiteral("keyboardevent")) { return NS_NewDOMKeyboardEvent(aOwner, aPresContext, nullptr); } - if (aEventType.LowerCaseEqualsLiteral("compositionevent") || - aEventType.LowerCaseEqualsLiteral("textevent")) { + if (aEventType.LowerCaseEqualsLiteral("compositionevent")) { return NS_NewDOMCompositionEvent(aOwner, aPresContext, nullptr); } + if (aEventType.LowerCaseEqualsLiteral("textevent")) { + if (!StaticPrefs::dom_events_textevent_enabled()) { + return NS_NewDOMCompositionEvent(aOwner, aPresContext, nullptr); + } + return NS_NewDOMTextEvent(aOwner, aPresContext, nullptr); + } if (aEventType.LowerCaseEqualsLiteral("mutationevent") || aEventType.LowerCaseEqualsLiteral("mutationevents")) { return NS_NewDOMMutationEvent(aOwner, aPresContext, nullptr); diff --git a/dom/events/EventListenerManager.cpp b/dom/events/EventListenerManager.cpp index 269ccd9de7..72d4eb1b30 100644 --- a/dom/events/EventListenerManager.cpp +++ b/dom/events/EventListenerManager.cpp @@ -455,27 +455,6 @@ void EventListenerManager::AddEventListenerInternal( window->SetHasFormSelectEventListeners(); } break; - case eMarqueeStart: - if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) { - if (Document* doc = window->GetExtantDoc()) { - doc->SetUseCounter(eUseCounter_custom_onstart); - } - } - break; - case eMarqueeBounce: - if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) { - if (Document* doc = window->GetExtantDoc()) { - doc->SetUseCounter(eUseCounter_custom_onbounce); - } - } - break; - case eMarqueeFinish: - if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) { - if (Document* doc = window->GetExtantDoc()) { - doc->SetUseCounter(eUseCounter_custom_onfinish); - } - } - break; case eScrollPortOverflow: if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) { if (Document* doc = window->GetExtantDoc()) { @@ -608,18 +587,6 @@ void EventListenerManager::AddEventListenerInternal( nsPrintfCString("resolvedEventMessage=%s", ToChar(resolvedEventMessage)) .get()); - NS_ASSERTION(aTypeAtom != nsGkAtoms::onstart, - nsPrintfCString("resolvedEventMessage=%s", - ToChar(resolvedEventMessage)) - .get()); - NS_ASSERTION(aTypeAtom != nsGkAtoms::onbounce, - nsPrintfCString("resolvedEventMessage=%s", - ToChar(resolvedEventMessage)) - .get()); - NS_ASSERTION(aTypeAtom != nsGkAtoms::onfinish, - nsPrintfCString("resolvedEventMessage=%s", - ToChar(resolvedEventMessage)) - .get()); NS_ASSERTION(aTypeAtom != nsGkAtoms::onoverflow, nsPrintfCString("resolvedEventMessage=%s", ToChar(resolvedEventMessage)) @@ -1417,10 +1384,10 @@ already_AddRefed<nsPIDOMWindowInner> EventListenerManager::WindowFromListener( if (global) { innerWindow = global->GetAsInnerWindow(); // Can be nullptr } - } else { + } else if (mTarget) { // This ensures `window.event` can be set properly for // nsWindowRoot to handle KeyPress event. - if (aListener && aTypeAtom == nsGkAtoms::onkeypress && mTarget && + if (aListener && aTypeAtom == nsGkAtoms::onkeypress && mTarget->IsRootWindow()) { nsPIWindowRoot* root = mTarget->AsWindowRoot(); if (nsPIDOMWindowOuter* outerWindow = root->GetWindow()) { @@ -1431,7 +1398,9 @@ already_AddRefed<nsPIDOMWindowInner> EventListenerManager::WindowFromListener( // listener->mListener.GetXPCOMCallback(). // In most cases, it would be the same as for // the target, so let's do that. - innerWindow = GetInnerWindowForTarget(); // Can be nullptr + if (nsIGlobalObject* global = mTarget->GetOwnerGlobal()) { + innerWindow = global->GetAsInnerWindow(); + } } } } diff --git a/dom/events/EventNameList.h b/dom/events/EventNameList.h index 92c76d000e..185f75f2bc 100644 --- a/dom/events/EventNameList.h +++ b/dom/events/EventNameList.h @@ -150,7 +150,6 @@ EVENT(abort, eImageAbort, EventNameType_All, eBasicEventClass) EVENT(beforetoggle, eBeforeToggle, EventNameType_HTMLXUL, eBasicEventClass) -EVENT(bounce, eMarqueeBounce, EventNameType_HTMLMarqueeOnly, eBasicEventClass) EVENT(cancel, eCancel, EventNameType_HTMLXUL, eBasicEventClass) EVENT(canplay, eCanPlay, EventNameType_HTML, eBasicEventClass) EVENT(canplaythrough, eCanPlayThrough, EventNameType_HTML, eBasicEventClass) @@ -179,7 +178,6 @@ EVENT(drop, eDrop, EventNameType_HTMLXUL, eDragEventClass) EVENT(durationchange, eDurationChange, EventNameType_HTML, eBasicEventClass) EVENT(emptied, eEmptied, EventNameType_HTML, eBasicEventClass) EVENT(ended, eEnded, EventNameType_HTML, eBasicEventClass) -EVENT(finish, eMarqueeFinish, EventNameType_HTMLMarqueeOnly, eBasicEventClass) EVENT(formdata, eFormData, EventNameType_HTML, eBasicEventClass) EVENT(fullscreenchange, eFullscreenChange, EventNameType_HTML, eBasicEventClass) EVENT(fullscreenerror, eFullscreenError, EventNameType_HTML, eBasicEventClass) @@ -227,6 +225,8 @@ EVENT(gotpointercapture, ePointerGotCapture, EventNameType_All, EVENT(lostpointercapture, ePointerLostCapture, EventNameType_All, ePointerEventClass) EVENT(selectstart, eSelectStart, EventNameType_HTMLXUL, eBasicEventClass) +NON_IDL_EVENT(textInput, eLegacyTextInput, EventNameType_None, + eLegacyTextEventClass) EVENT(contextlost, eContextLost, EventNameType_HTML, eBasicEventClass) EVENT(contextrestored, eContextRestored, EventNameType_HTML, eBasicEventClass) @@ -246,7 +246,6 @@ EVENT(seeking, eSeeking, EventNameType_HTML, eBasicEventClass) EVENT(select, eFormSelect, EventNameType_HTMLXUL, eBasicEventClass) EVENT(slotchange, eSlotChange, EventNameType_All, eBasicEventClass) EVENT(stalled, eStalled, EventNameType_HTML, eBasicEventClass) -EVENT(start, eMarqueeStart, EventNameType_HTMLMarqueeOnly, eBasicEventClass) EVENT(submit, eFormSubmit, EventNameType_HTMLXUL, eBasicEventClass) EVENT(suspend, eSuspend, EventNameType_HTML, eBasicEventClass) EVENT(timeupdate, eTimeUpdate, EventNameType_HTML, eBasicEventClass) diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp index 0ae756211b..3c24cdb30a 100644 --- a/dom/events/EventStateManager.cpp +++ b/dom/events/EventStateManager.cpp @@ -227,6 +227,9 @@ static nsINode* GetCommonAncestorForMouseUp( return parent; } +LazyLogModule sMouseBoundaryLog("MouseBoundaryEvents"); +LazyLogModule sPointerBoundaryLog("PointerBoundaryEvents"); + /******************************************************************/ /* mozilla::UITimerCallback */ /******************************************************************/ @@ -264,8 +267,8 @@ UITimerCallback::Notify(nsITimer* aTimer) { if (XRE_IsParentProcess()) { hal::BatteryInformation batteryInfo; hal::GetCurrentBatteryInformation(&batteryInfo); - glean::power_battery::percentage_when_user_active.AccumulateSamples( - {uint64_t(batteryInfo.level() * 100)}); + glean::power_battery::percentage_when_user_active.AccumulateSingleSample( + uint64_t(batteryInfo.level() * 100)); } } mPreviousCount = gMouseOrKeyboardEventCounter; @@ -282,10 +285,6 @@ UITimerCallback::GetName(nsACString& aName) { /* mozilla::OverOutElementsWrapper */ /******************************************************************/ -OverOutElementsWrapper::OverOutElementsWrapper() : mLastOverFrame(nullptr) {} - -OverOutElementsWrapper::~OverOutElementsWrapper() = default; - NS_IMPL_CYCLE_COLLECTION(OverOutElementsWrapper, mDeepestEnterEventTarget, mDispatchingOverEventTarget, mDispatchingOutOrDeepestLeaveEventTarget) @@ -296,6 +295,116 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(OverOutElementsWrapper) NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_END +void OverOutElementsWrapper::ContentRemoved(nsIContent& aContent) { + if (!mDeepestEnterEventTarget) { + return; + } + + if (!nsContentUtils::ContentIsFlattenedTreeDescendantOf( + mDeepestEnterEventTarget, &aContent)) { + return; + } + + LogModule* const logModule = mType == BoundaryEventType::Mouse + ? sMouseBoundaryLog + : sPointerBoundaryLog; + + if (!StaticPrefs:: + dom_events_mouse_pointer_boundary_keep_enter_targets_after_over_target_removed()) { + MOZ_LOG(logModule, LogLevel::Info, + ("The last \"over\" event target (%p) is removed", + mDeepestEnterEventTarget.get())); + mDeepestEnterEventTarget = nullptr; + return; + } + + if (mDispatchingOverEventTarget && + (mDeepestEnterEventTarget == mDispatchingOverEventTarget || + nsContentUtils::ContentIsFlattenedTreeDescendantOf( + mDispatchingOverEventTarget, &aContent))) { + if (mDispatchingOverEventTarget == + mDispatchingOutOrDeepestLeaveEventTarget) { + MOZ_LOG(logModule, LogLevel::Info, + ("The dispatching \"%s\" event target (%p) is removed", + mDeepestEnterEventTargetIsOverEventTarget ? "out" : "leave", + mDispatchingOutOrDeepestLeaveEventTarget.get())); + mDispatchingOutOrDeepestLeaveEventTarget = nullptr; + } + MOZ_LOG(logModule, LogLevel::Info, + ("The dispatching \"over\" event target (%p) is removed", + mDispatchingOverEventTarget.get())); + mDispatchingOverEventTarget = nullptr; + } + if (mDispatchingOutOrDeepestLeaveEventTarget && + (mDeepestEnterEventTarget == mDispatchingOutOrDeepestLeaveEventTarget || + nsContentUtils::ContentIsFlattenedTreeDescendantOf( + mDispatchingOutOrDeepestLeaveEventTarget, &aContent))) { + MOZ_LOG(logModule, LogLevel::Info, + ("The dispatching \"%s\" event target (%p) is removed", + mDeepestEnterEventTargetIsOverEventTarget ? "out" : "leave", + mDispatchingOutOrDeepestLeaveEventTarget.get())); + mDispatchingOutOrDeepestLeaveEventTarget = nullptr; + } + MOZ_LOG(logModule, LogLevel::Info, + ("The last \"%s\" event target (%p) is removed and now the last " + "deepest enter target becomes %s(%p)", + mDeepestEnterEventTargetIsOverEventTarget ? "over" : "enter", + mDeepestEnterEventTarget.get(), + aContent.GetFlattenedTreeParent() + ? ToString(*aContent.GetFlattenedTreeParent()).c_str() + : "nullptr", + aContent.GetFlattenedTreeParent())); + mDeepestEnterEventTarget = aContent.GetFlattenedTreeParent(); + mDeepestEnterEventTargetIsOverEventTarget = false; +} + +void OverOutElementsWrapper::DidDispatchOverAndEnterEvent( + nsIContent* aOriginalOverTargetInComposedDoc) { + mDispatchingOverEventTarget = nullptr; + + // Pointer Events define that once the `pointerover` event target is removed + // from the tree, `pointerout` should not be fired on that and the closest + // connected ancestor at the target removal should be kept as the deepest + // `pointerleave` target. Therefore, we don't need the special handling for + // `pointerout` event target if the last `pointerover` target is temporarily + // removed from the tree. + if (mType == OverOutElementsWrapper::BoundaryEventType::Pointer) { + return; + } + + // Assume that the caller checks whether aOriginalOverTarget is in the + // original document. If we don't enable the strict mouse/pointer event + // boundary event dispatching by the pref (see below), + // mDeepestEnterEventTarget is set to nullptr when the last "over" target is + // removed. Therefore, we cannot check whether aOriginalOverTarget is in the + // original document here. + if (!aOriginalOverTargetInComposedDoc) { + return; + } + MOZ_ASSERT_IF(mDeepestEnterEventTarget, + mDeepestEnterEventTarget->GetComposedDoc() == + aOriginalOverTargetInComposedDoc->GetComposedDoc()); + // If the "mouseover" event target is removed temporarily while we're + // dispatching "mouseover" and "mouseenter" events and the target gets back + // under the deepest enter event target, we should restore the "mouseover" + // target. + if ((!StaticPrefs:: + dom_events_mouse_pointer_boundary_keep_enter_targets_after_over_target_removed() && + !mDeepestEnterEventTarget) || + (!mDeepestEnterEventTargetIsOverEventTarget && mDeepestEnterEventTarget && + nsContentUtils::ContentIsFlattenedTreeDescendantOf( + aOriginalOverTargetInComposedDoc, mDeepestEnterEventTarget))) { + mDeepestEnterEventTarget = aOriginalOverTargetInComposedDoc; + mDeepestEnterEventTargetIsOverEventTarget = true; + LogModule* const logModule = mType == BoundaryEventType::Mouse + ? sMouseBoundaryLog + : sPointerBoundaryLog; + MOZ_LOG(logModule, LogLevel::Info, + ("The \"over\" event target (%p) is restored", + mDeepestEnterEventTarget.get())); + } +} + /******************************************************************/ /* mozilla::EventStateManager */ /******************************************************************/ @@ -2169,8 +2278,8 @@ void EventStateManager::MaybeFirePointerCancel(WidgetInputEvent* aEvent) { WidgetPointerEvent event(aTouchEvent->IsTrusted(), ePointerCancel, aTouchEvent->mWidget); - PointerEventHandler::InitPointerEventFromTouch( - event, *aTouchEvent, *aTouchEvent->mTouches[0], true); + PointerEventHandler::InitPointerEventFromTouch(event, *aTouchEvent, + *aTouchEvent->mTouches[0]); event.convertToPointer = false; presShell->HandleEventWithTarget(&event, targetFrame, content, &status); @@ -3453,6 +3562,13 @@ void EventStateManager::PostHandleKeyboardEvent( } } +static bool NeedsActiveContentChange(const WidgetMouseEvent* aMouseEvent) { + // If the mouse event is a synthesized mouse event due to a touch, do + // not set/clear the activation state. Element activation is handled by APZ. + return !aMouseEvent || + aMouseEvent->mInputSource != MouseEvent_Binding::MOZ_SOURCE_TOUCH; +} + nsresult EventStateManager::PostHandleEvent(nsPresContext* aPresContext, WidgetEvent* aEvent, nsIFrame* aTargetFrame, @@ -3696,7 +3812,9 @@ nsresult EventStateManager::PostHandleEvent(nsPresContext* aPresContext, } // XXX Why do we always set this is active? Active window may be changed // by a mousedown event listener. - SetActiveManager(this, activeContent); + if (NeedsActiveContentChange(mouseEvent)) { + SetActiveManager(this, activeContent); + } } break; case ePointerCancel: case ePointerUp: { @@ -3724,10 +3842,7 @@ nsresult EventStateManager::PostHandleEvent(nsPresContext* aPresContext, PresShell::ReleaseCapturingContent(); WidgetMouseEvent* mouseUpEvent = aEvent->AsMouseEvent(); - // If the mouseup event is a synthesized mouse event due to a touch, do - // not clear the activation state. Element activation is handled by APZ. - if (!mouseUpEvent || mouseUpEvent->mInputSource != - dom::MouseEvent_Binding::MOZ_SOURCE_TOUCH) { + if (NeedsActiveContentChange(mouseUpEvent)) { ClearGlobalActiveContent(this); } if (mouseUpEvent && EventCausesClickEvents(*mouseUpEvent)) { @@ -4803,6 +4918,10 @@ class EnterLeaveDispatcher { void EventStateManager::NotifyMouseOut(WidgetMouseEvent* aMouseEvent, nsIContent* aMovingInto) { + const bool isPointer = aMouseEvent->mClass == ePointerEventClass; + LogModule* const logModule = + isPointer ? sPointerBoundaryLog : sMouseBoundaryLog; + RefPtr<OverOutElementsWrapper> wrapper = GetWrapperByEventID(aMouseEvent); // If there is no deepest "leave" event target, that means the last "over" @@ -4816,6 +4935,11 @@ void EventStateManager::NotifyMouseOut(WidgetMouseEvent* aMouseEvent, return; } + MOZ_LOG(logModule, LogLevel::Info, + ("NotifyMouseOut: the source event is %s (IsReal()=%s)", + ToChar(aMouseEvent->mMessage), + aMouseEvent->IsReal() ? "true" : "false")); + // XXX If a content node is a container of remove content, it should be // replaced with them and its children should not be visible. Therefore, // if the deepest "enter" target is not the last "over" target, i.e., the @@ -4829,6 +4953,10 @@ void EventStateManager::NotifyMouseOut(WidgetMouseEvent* aMouseEvent, if (RefPtr<nsPresContext> presContext = docshell->GetPresContext()) { EventStateManager* kidESM = presContext->EventStateManager(); // Not moving into any element in this subdocument + MOZ_LOG(logModule, LogLevel::Info, + ("Notifying child EventStateManager (%p) of \"out\" " + "event...", + kidESM)); kidESM->NotifyMouseOut(aMouseEvent, nullptr); } } @@ -4846,7 +4974,6 @@ void EventStateManager::NotifyMouseOut(WidgetMouseEvent* aMouseEvent, // hover state itself, and we have optimizations for hover switching between // two nearby elements both deep in the DOM tree that would be defeated by // switching the hover state to null here. - bool isPointer = aMouseEvent->mClass == ePointerEventClass; if (!aMovingInto && !isPointer) { // Unset :hover SetContentState(nullptr, ElementState::HOVER); @@ -4859,12 +4986,27 @@ void EventStateManager::NotifyMouseOut(WidgetMouseEvent* aMouseEvent, // "out" events hould be fired only when the deepest "leave" event target // is the last "over" event target. if (nsCOMPtr<nsIContent> outEventTarget = wrapper->GetOutEventTarget()) { + MOZ_LOG(logModule, LogLevel::Info, + ("Dispatching %s event to %s (%p)", + isPointer ? "ePointerOut" : "eMouseOut", + outEventTarget ? ToString(*outEventTarget).c_str() : "nullptr", + outEventTarget.get())); DispatchMouseOrPointerEvent(aMouseEvent, isPointer ? ePointerOut : eMouseOut, outEventTarget, aMovingInto); } + + MOZ_LOG(logModule, LogLevel::Info, + ("Dispatching %s event to %s (%p) and its ancestors", + isPointer ? "ePointerLeave" : "eMouseLeave", + wrapper->GetDeepestLeaveEventTarget() + ? ToString(*wrapper->GetDeepestLeaveEventTarget()).c_str() + : "nullptr", + wrapper->GetDeepestLeaveEventTarget())); leaveDispatcher.Dispatch(); + MOZ_LOG(logModule, LogLevel::Info, + ("Dispatched \"out\" and/or \"leave\" events")); wrapper->DidDispatchOutAndOrLeaveEvent(); } @@ -4884,6 +5026,10 @@ void EventStateManager::NotifyMouseOver(WidgetMouseEvent* aMouseEvent, nsIContent* aContent) { NS_ASSERTION(aContent, "Mouse must be over something"); + const bool isPointer = aMouseEvent->mClass == ePointerEventClass; + LogModule* const logModule = + isPointer ? sPointerBoundaryLog : sMouseBoundaryLog; + RefPtr<OverOutElementsWrapper> wrapper = GetWrapperByEventID(aMouseEvent); // If we have next "out" event target and it's the new "over" target, we don't @@ -4897,6 +5043,11 @@ void EventStateManager::NotifyMouseOver(WidgetMouseEvent* aMouseEvent, return; } + MOZ_LOG(logModule, LogLevel::Info, + ("NotifyMouseOver: the source event is %s (IsReal()=%s)", + ToChar(aMouseEvent->mMessage), + aMouseEvent->IsReal() ? "true" : "false")); + // Check to see if we're a subdocument and if so update the parent // document's ESM state to indicate that the mouse is over the // content associated with our subdocument. @@ -4906,6 +5057,10 @@ void EventStateManager::NotifyMouseOver(WidgetMouseEvent* aMouseEvent, if (PresShell* parentPresShell = parentDoc->GetPresShell()) { RefPtr<EventStateManager> parentESM = parentPresShell->GetPresContext()->EventStateManager(); + MOZ_LOG(logModule, LogLevel::Info, + ("Notifying parent EventStateManager (%p) of \"over\" " + "event...", + parentESM.get())); parentESM->NotifyMouseOver(aMouseEvent, docContent); } } @@ -4922,8 +5077,6 @@ void EventStateManager::NotifyMouseOver(WidgetMouseEvent* aMouseEvent, nsCOMPtr<nsIContent> deepestLeaveEventTarget = wrapper->GetDeepestLeaveEventTarget(); - bool isPointer = aMouseEvent->mClass == ePointerEventClass; - EnterLeaveDispatcher enterDispatcher(this, aContent, deepestLeaveEventTarget, aMouseEvent, isPointer ? ePointerEnter : eMouseEnter); @@ -4939,12 +5092,26 @@ void EventStateManager::NotifyMouseOver(WidgetMouseEvent* aMouseEvent, // Fire mouseover // XXX If aContent has already been removed from the DOM tree, what should we // do? At least, dispatching `mouseover` on it is odd. + MOZ_LOG(logModule, LogLevel::Info, + ("Dispatching %s event to %s (%p)", + isPointer ? "ePointerOver" : "eMoustOver", + aContent ? ToString(*aContent).c_str() : "nullptr", aContent)); wrapper->mLastOverFrame = DispatchMouseOrPointerEvent( aMouseEvent, isPointer ? ePointerOver : eMouseOver, aContent, deepestLeaveEventTarget); + + MOZ_LOG(logModule, LogLevel::Info, + ("Dispatching %s event to %s (%p) and its ancestors", + isPointer ? "ePointerEnter" : "eMouseEnter", + aContent ? ToString(*aContent).c_str() : "nullptr", aContent)); enterDispatcher.Dispatch(); - wrapper->DidDispatchOverAndEnterEvent(); + MOZ_LOG(logModule, LogLevel::Info, + ("Dispatched \"over\" and \"enter\" events (the original \"over\" " + "event target was in the document %p, and now in %p)", + aContent->GetComposedDoc(), mDocument.get())); + wrapper->DidDispatchOverAndEnterEvent( + aContent->GetComposedDoc() == mDocument ? aContent : nullptr); } // Returns the center point of the window's client area. This is @@ -5135,11 +5302,13 @@ OverOutElementsWrapper* EventStateManager::GetWrapperByEventID( if (!pointer) { MOZ_ASSERT(aEvent->AsMouseEvent() != nullptr); if (!mMouseEnterLeaveHelper) { - mMouseEnterLeaveHelper = new OverOutElementsWrapper(); + mMouseEnterLeaveHelper = new OverOutElementsWrapper( + OverOutElementsWrapper::BoundaryEventType::Mouse); } return mMouseEnterLeaveHelper; } - return mPointersEnterLeaveHelper.GetOrInsertNew(pointer->pointerId); + return mPointersEnterLeaveHelper.GetOrInsertNew( + pointer->pointerId, OverOutElementsWrapper::BoundaryEventType::Pointer); } /* static */ @@ -6895,40 +7064,4 @@ bool EventStateManager::WheelPrefs::IsOverOnePageScrollAllowedY( MIN_MULTIPLIER_VALUE_ALLOWING_OVER_ONE_PAGE_SCROLL; } -void OverOutElementsWrapper::ContentRemoved(nsIContent& aContent) { - if (!mDeepestEnterEventTarget) { - return; - } - - if (!nsContentUtils::ContentIsFlattenedTreeDescendantOf( - mDeepestEnterEventTarget, &aContent)) { - return; - } - - if (!StaticPrefs:: - dom_events_mouse_pointer_boundary_keep_enter_targets_after_over_target_removed()) { - mDeepestEnterEventTarget = nullptr; - return; - } - - if (mDispatchingOverEventTarget && - (mDeepestEnterEventTarget == mDispatchingOverEventTarget || - nsContentUtils::ContentIsFlattenedTreeDescendantOf( - mDispatchingOverEventTarget, &aContent))) { - if (mDispatchingOverEventTarget == - mDispatchingOutOrDeepestLeaveEventTarget) { - mDispatchingOutOrDeepestLeaveEventTarget = nullptr; - } - mDispatchingOverEventTarget = nullptr; - } - if (mDispatchingOutOrDeepestLeaveEventTarget && - (mDeepestEnterEventTarget == mDispatchingOutOrDeepestLeaveEventTarget || - nsContentUtils::ContentIsFlattenedTreeDescendantOf( - mDispatchingOutOrDeepestLeaveEventTarget, &aContent))) { - mDispatchingOutOrDeepestLeaveEventTarget = nullptr; - } - mDeepestEnterEventTarget = aContent.GetFlattenedTreeParent(); - mDeepestEnterEventTargetIsOverEventTarget = false; -} - } // namespace mozilla diff --git a/dom/events/EventStateManager.h b/dom/events/EventStateManager.h index b3bb3b5170..a4e709d507 100644 --- a/dom/events/EventStateManager.h +++ b/dom/events/EventStateManager.h @@ -56,10 +56,11 @@ class RemoteDragStartData; } // namespace dom class OverOutElementsWrapper final : public nsISupports { - ~OverOutElementsWrapper(); + ~OverOutElementsWrapper() = default; public: - OverOutElementsWrapper(); + enum class BoundaryEventType : bool { Mouse, Pointer }; + explicit OverOutElementsWrapper(BoundaryEventType aType) : mType(aType) {} NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_CLASS(OverOutElementsWrapper) @@ -72,7 +73,8 @@ class OverOutElementsWrapper final : public nsISupports { mDispatchingOverEventTarget = aOverEventTarget; mDeepestEnterEventTargetIsOverEventTarget = true; } - void DidDispatchOverAndEnterEvent() { mDispatchingOverEventTarget = nullptr; } + void DidDispatchOverAndEnterEvent( + nsIContent* aOriginalOverTargetInComposedDoc); [[nodiscard]] bool IsDispatchingOverEventOn( nsIContent* aOverEventTarget) const { MOZ_ASSERT(aOverEventTarget); @@ -135,6 +137,8 @@ class OverOutElementsWrapper final : public nsISupports { // the DOM tree, this is set to nullptr. nsCOMPtr<nsIContent> mDispatchingOutOrDeepestLeaveEventTarget; + const BoundaryEventType mType; + // Once the last "over" element is removed from the tree, this is set // to false. Then, mDeepestEnterEventTarget may be an ancestor of the // "over" element which should be the deepest target of next "leave" diff --git a/dom/events/InvokeEvent.cpp b/dom/events/InvokeEvent.cpp index 4b7dc1e6da..3312f8138d 100644 --- a/dom/events/InvokeEvent.cpp +++ b/dom/events/InvokeEvent.cpp @@ -47,9 +47,13 @@ already_AddRefed<InvokeEvent> InvokeEvent::Constructor( Element* InvokeEvent::GetInvoker() { EventTarget* currentTarget = GetCurrentTarget(); if (currentTarget) { + nsINode* currentTargetNode = currentTarget->GetAsNode(); + if (!currentTargetNode) { + return nullptr; + } nsINode* retargeted = nsContentUtils::Retarget( - static_cast<nsINode*>(mInvoker), currentTarget->GetAsNode()); - return retargeted->AsElement(); + static_cast<nsINode*>(mInvoker), currentTargetNode); + return retargeted ? retargeted->AsElement() : nullptr; } MOZ_ASSERT(!mEvent->mFlags.mIsBeingDispatched); return mInvoker; diff --git a/dom/events/PointerEventHandler.cpp b/dom/events/PointerEventHandler.cpp index ef6f2b12c0..f631b6e494 100644 --- a/dom/events/PointerEventHandler.cpp +++ b/dom/events/PointerEventHandler.cpp @@ -15,6 +15,7 @@ #include "mozilla/dom/BrowserChild.h" #include "mozilla/dom/BrowserParent.h" #include "mozilla/dom/Document.h" +#include "mozilla/dom/DocumentInlines.h" #include "mozilla/dom/MouseEventBinding.h" namespace mozilla { @@ -80,7 +81,8 @@ void PointerEventHandler::UpdateActivePointerState(WidgetMouseEvent* aEvent, // In this case we have to know information about available mouse pointers sActivePointersIds->InsertOrUpdate( aEvent->pointerId, - MakeUnique<PointerInfo>(false, aEvent->mInputSource, true, nullptr)); + MakeUnique<PointerInfo>(false, aEvent->mInputSource, true, false, + nullptr)); MaybeCacheSpoofedPointerID(aEvent->mInputSource, aEvent->pointerId); break; @@ -94,6 +96,7 @@ void PointerEventHandler::UpdateActivePointerState(WidgetMouseEvent* aEvent, pointerEvent->pointerId, MakeUnique<PointerInfo>( true, pointerEvent->mInputSource, pointerEvent->mIsPrimary, + pointerEvent->mFromTouchEvent, aTargetContent ? aTargetContent->OwnerDoc() : nullptr)); MaybeCacheSpoofedPointerID(pointerEvent->mInputSource, pointerEvent->pointerId); @@ -112,7 +115,8 @@ void PointerEventHandler::UpdateActivePointerState(WidgetMouseEvent* aEvent, sActivePointersIds->InsertOrUpdate( pointerEvent->pointerId, MakeUnique<PointerInfo>(false, pointerEvent->mInputSource, - pointerEvent->mIsPrimary, nullptr)); + pointerEvent->mIsPrimary, + pointerEvent->mFromTouchEvent, nullptr)); } else { sActivePointersIds->Remove(pointerEvent->pointerId); } @@ -314,7 +318,7 @@ void PointerEventHandler::ProcessPointerCaptureForTouch( continue; } WidgetPointerEvent event(aEvent->IsTrusted(), eVoidEvent, aEvent->mWidget); - InitPointerEventFromTouch(event, *aEvent, *touch, i == 0); + InitPointerEventFromTouch(event, *aEvent, *touch); CheckPointerCaptureState(&event); } } @@ -461,11 +465,13 @@ Element* PointerEventHandler::GetPointerCapturingElement( 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. - for (const auto& entry : *sPointerCaptureList) { - PointerCaptureInfo* data = entry.GetWeak(); - if (data && data->mPendingElement && - data->mPendingElement->IsInclusiveDescendantOf(aContent)) { - ReleasePointerCaptureById(entry.GetKey()); + if (!sPointerCaptureList->IsEmpty()) { + for (const auto& entry : *sPointerCaptureList) { + PointerCaptureInfo* data = entry.GetWeak(); + if (data && data->mPendingElement && + data->mPendingElement->IsInclusiveDescendantOf(aContent)) { + ReleasePointerCaptureById(entry.GetKey()); + } } } } @@ -546,7 +552,7 @@ void PointerEventHandler::InitPointerEventFromMouse( /* static */ void PointerEventHandler::InitPointerEventFromTouch( WidgetPointerEvent& aPointerEvent, const WidgetTouchEvent& aTouchEvent, - const mozilla::dom::Touch& aTouch, bool aIsPrimary) { + const mozilla::dom::Touch& aTouch) { // Use mButton/mButtons only when mButton got a value (from pen input) int16_t button = aTouchEvent.mMessage == eTouchMove ? MouseButton::eNotPressed : aTouchEvent.mButton != MouseButton::eNotPressed @@ -558,7 +564,10 @@ void PointerEventHandler::InitPointerEventFromTouch( ? aTouchEvent.mButtons : MouseButtonsFlag::ePrimaryFlag; - aPointerEvent.mIsPrimary = aIsPrimary; + // Only the first touch would be the primary pointer. + aPointerEvent.mIsPrimary = aTouchEvent.mMessage == eTouchStart + ? !HasActiveTouchPointer() + : GetPointerPrimaryState(aTouch.Identifier()); aPointerEvent.pointerId = aTouch.Identifier(); aPointerEvent.mRefPoint = aTouch.mRefPoint; aPointerEvent.mModifiers = aTouchEvent.mModifiers; @@ -679,7 +688,7 @@ void PointerEventHandler::DispatchPointerFromMouseOrTouch( WidgetPointerEvent event(touchEvent->IsTrusted(), pointerMessage, touchEvent->mWidget); - InitPointerEventFromTouch(event, *touchEvent, *touch, i == 0); + InitPointerEventFromTouch(event, *touchEvent, *touch); event.convertToPointer = touch->convertToPointer = false; event.mCoalescedWidgetEvents = touch->mCoalescedWidgetEvents; if (aMouseOrTouchEvent->mMessage == eTouchStart) { @@ -739,6 +748,15 @@ void PointerEventHandler::NotifyDestroyPresContext( 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) { @@ -771,6 +789,16 @@ bool PointerEventHandler::GetPointerPrimaryState(uint32_t aPointerId) { } /* static */ +bool PointerEventHandler::HasActiveTouchPointer() { + for (auto iter = sActivePointersIds->ConstIter(); !iter.Done(); iter.Next()) { + if (iter.Data()->mFromTouchEvent) { + return true; + } + } + return false; +} + +/* static */ void PointerEventHandler::DispatchGotOrLostPointerCaptureEvent( bool aIsGotCapture, const WidgetPointerEvent* aPointerEvent, Element* aCaptureTarget) { diff --git a/dom/events/PointerEventHandler.h b/dom/events/PointerEventHandler.h index 0211bfe0d8..87b41f9d94 100644 --- a/dom/events/PointerEventHandler.h +++ b/dom/events/PointerEventHandler.h @@ -50,13 +50,16 @@ class PointerInfo final { uint16_t mPointerType; bool mActiveState; bool mPrimaryState; + bool mFromTouchEvent; bool mPreventMouseEventByContent; WeakPtr<dom::Document> mActiveDocument; explicit PointerInfo(bool aActiveState, uint16_t aPointerType, - bool aPrimaryState, dom::Document* aActiveDocument) + bool aPrimaryState, bool aFromTouchEvent, + dom::Document* aActiveDocument) : mPointerType(aPointerType), mActiveState(aActiveState), mPrimaryState(aPrimaryState), + mFromTouchEvent(aFromTouchEvent), mPreventMouseEventByContent(false), mActiveDocument(aActiveDocument) {} }; @@ -208,8 +211,7 @@ class PointerEventHandler final { static void InitPointerEventFromTouch(WidgetPointerEvent& aPointerEvent, const WidgetTouchEvent& aTouchEvent, - const mozilla::dom::Touch& aTouch, - bool aIsPrimary); + const mozilla::dom::Touch& aTouch); static bool ShouldGeneratePointerEventFromMouse(WidgetGUIEvent* aEvent) { return aEvent->mMessage == eMouseDown || aEvent->mMessage == eMouseUp || @@ -250,6 +252,10 @@ class PointerEventHandler final { // event with pointerId static bool GetPointerPrimaryState(uint32_t aPointerId); + // HasActiveTouchPointer returns true if there is active pointer event that is + // generated from touch event. + static bool HasActiveTouchPointer(); + MOZ_CAN_RUN_SCRIPT static void DispatchGotOrLostPointerCaptureEvent( bool aIsGotCapture, const WidgetPointerEvent* aPointerEvent, diff --git a/dom/events/TextEvent.cpp b/dom/events/TextEvent.cpp new file mode 100644 index 0000000000..95ed15f8e2 --- /dev/null +++ b/dom/events/TextEvent.cpp @@ -0,0 +1,68 @@ +/* -*- 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/. */ + +#include "mozilla/BasePrincipal.h" // for nsIPrincipal::IsSystemPrincipal() +#include "mozilla/EventForwards.h" +#include "mozilla/TextEvents.h" +#include "mozilla/dom/DataTransfer.h" +#include "mozilla/dom/TextEvent.h" +#include "nsGlobalWindowInner.h" +#include "nsIPrincipal.h" +#include "nsPresContext.h" + +namespace mozilla::dom { + +TextEvent::TextEvent(EventTarget* aOwner, nsPresContext* aPresContext, + InternalLegacyTextEvent* aEvent) + : UIEvent(aOwner, aPresContext, + aEvent + ? aEvent + : new InternalLegacyTextEvent(false, eVoidEvent, nullptr)) { + NS_ASSERTION(mEvent->mClass == eLegacyTextEventClass, "event type mismatch"); + mEventIsInternal = !aEvent; +} + +void TextEvent::InitTextEvent(const nsAString& typeArg, bool canBubbleArg, + bool cancelableArg, nsGlobalWindowInner* viewArg, + const nsAString& dataArg) { + if (NS_WARN_IF(mEvent->mFlags.mIsBeingDispatched)) { + return; + } + + UIEvent::InitUIEvent(typeArg, canBubbleArg, cancelableArg, viewArg, 0); + + static_cast<InternalLegacyTextEvent*>(mEvent)->mData = dataArg; +} + +void TextEvent::GetData(nsAString& aData, + nsIPrincipal& aSubjectPrincipal) const { + InternalLegacyTextEvent* textEvent = mEvent->AsLegacyTextEvent(); + MOZ_ASSERT(textEvent); + if (mEvent->IsTrusted() && !aSubjectPrincipal.IsSystemPrincipal() && + !StaticPrefs::dom_event_clipboardevents_enabled() && + ExposesClipboardDataOrDataTransfer(textEvent->mInputType)) { + aData.Truncate(); + return; + } + if (!textEvent->mDataTransfer) { + aData = textEvent->mData; + return; + } + textEvent->mDataTransfer->GetData(u"text/plain"_ns, aData, aSubjectPrincipal, + IgnoreErrors()); +} + +} // namespace mozilla::dom + +using namespace mozilla; +using namespace mozilla::dom; + +already_AddRefed<TextEvent> NS_NewDOMTextEvent( + EventTarget* aOwner, nsPresContext* aPresContext, + InternalLegacyTextEvent* aEvent) { + RefPtr<TextEvent> it = new TextEvent(aOwner, aPresContext, aEvent); + return it.forget(); +} diff --git a/dom/events/TextEvent.h b/dom/events/TextEvent.h new file mode 100644 index 0000000000..0546a24eed --- /dev/null +++ b/dom/events/TextEvent.h @@ -0,0 +1,46 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_TextEvent_h +#define mozilla_dom_TextEvent_h + +#include "mozilla/dom/UIEvent.h" + +#include "mozilla/dom/TextEventBinding.h" +#include "mozilla/EventForwards.h" + +class nsIPrincipal; + +namespace mozilla::dom { + +class TextEvent : public UIEvent { + public: + TextEvent(EventTarget* aOwner, nsPresContext* aPresContext, + InternalLegacyTextEvent* aEvent); + + NS_INLINE_DECL_REFCOUNTING_INHERITED(TextEvent, UIEvent) + + virtual JSObject* WrapObjectInternal( + JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override { + return TextEvent_Binding::Wrap(aCx, this, aGivenProto); + } + + void GetData(nsAString& aData, nsIPrincipal& aSubjectPrincipal) const; + void InitTextEvent(const nsAString& typeArg, bool canBubbleArg, + bool cancelableArg, nsGlobalWindowInner* viewArg, + const nsAString& dataArg); + + protected: + ~TextEvent() = default; +}; + +} // namespace mozilla::dom + +already_AddRefed<mozilla::dom::TextEvent> NS_NewDOMTextEvent( + mozilla::dom::EventTarget* aOwner, nsPresContext* aPresContext, + mozilla::InternalLegacyTextEvent* aEvent); + +#endif // mozilla_dom_InputEvent_h_ diff --git a/dom/events/moz.build b/dom/events/moz.build index 62e1f7d57b..405bdff895 100644 --- a/dom/events/moz.build +++ b/dom/events/moz.build @@ -94,6 +94,7 @@ EXPORTS.mozilla.dom += [ "SimpleGestureEvent.h", "StorageEvent.h", "TextClause.h", + "TextEvent.h", "Touch.h", "TouchEvent.h", "TransitionEvent.h", @@ -154,6 +155,7 @@ UNIFIED_SOURCES += [ "StorageEvent.cpp", "TextClause.cpp", "TextComposition.cpp", + "TextEvent.cpp", "Touch.cpp", "TouchEvent.cpp", "TransitionEvent.cpp", diff --git a/dom/events/test/clipboard/mochitest.toml b/dom/events/test/clipboard/mochitest.toml index 7829915267..98cbd3ddb5 100644 --- a/dom/events/test/clipboard/mochitest.toml +++ b/dom/events/test/clipboard/mochitest.toml @@ -1,5 +1,12 @@ [DEFAULT] +["test_async_clipboard_datatransfer.html"] +scheme = "https" +skip-if = [ + "headless", # headless doesn't support custom type + "os == 'android'", # android doesn't support custom type +] + ["test_paste_image.html"] skip-if = [ "headless", # Bug 1405869 diff --git a/dom/events/test/clipboard/test_async_clipboard.xhtml b/dom/events/test/clipboard/test_async_clipboard.xhtml index ec54809077..8a882fd24d 100644 --- a/dom/events/test/clipboard/test_async_clipboard.xhtml +++ b/dom/events/test/clipboard/test_async_clipboard.xhtml @@ -74,15 +74,7 @@ }); const items = await navigator.clipboard.read(); - - // Bug 1756955: at least on Ubuntu 20.04, clearing the clipboard leads to - // one item with no types. - if (!items.length || - (items.length == 1 && !items[0].types.length)) { - ok(true, "read() read the right thing from empty clipboard"); - } else { - ok(false, "read() read the wrong thing from empty clipboard"); - } + ok(!items.length, "read() read the right thing from empty clipboard"); } async function testNoContentsReadText() { diff --git a/dom/events/test/clipboard/test_async_clipboard_datatransfer.html b/dom/events/test/clipboard/test_async_clipboard_datatransfer.html new file mode 100644 index 0000000000..ab4151f4f5 --- /dev/null +++ b/dom/events/test/clipboard/test_async_clipboard_datatransfer.html @@ -0,0 +1,89 @@ +<html> +<head> +<title>Test for bug 1756955</title> +<link rel="stylesheet" href="/tests/SimpleTest/test.css"> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<script src="/tests/SimpleTest/EventUtils.js"></script> +<script> + +const kIsMac = navigator.platform.indexOf("Mac") > -1; + +async function copyByDataTransfer(aItems) { + const copyPromise = new Promise(resolve => { + document.addEventListener("copy", (e) => { + e.preventDefault(); + for (const [key, value] of Object.entries(aItems)) { + e.clipboardData.setData(key, value); + } + resolve(); + }, { once: true }); + }); + synthesizeKey( + "c", + kIsMac ? { accelKey: true } : { ctrlKey: true } + ); + await copyPromise; +} + +async function paste(aCallback) { + const pastePromise = new Promise(resolve => { + document.addEventListener("paste", (e) => { + resolve(aCallback(e.clipboardData)); + }, { once: true }); + }); + synthesizeKey( + "v", + kIsMac ? { accelKey: true } : { ctrlKey: true } + ); + await pastePromise; +} + +add_setup(async function () { + await SpecialPowers.pushPrefEnv({ + set: [ + ["dom.events.asyncClipboard.readText", true], + ["dom.events.asyncClipboard.clipboardItem", true], + ], + }); +}); + +add_task(async function test_mandatory_type() { + const items = { + "text/plain": "X" + Math.random(), + "custom/foo": "X" + Math.random(), + }; + await copyByDataTransfer(items); + await paste(async (clipboardData) => { + for (const [key, value] of Object.entries(items)) { + is(clipboardData.getData(key), value, `Check ${key} type`); + } + + let clipboardItems = await navigator.clipboard.read(); + is(clipboardItems.length, 1, "Should only one clipboardItem"); + is(clipboardItems[0].types.length, 1, "Should only one type"); + is(await clipboardItems[0].getType("text/plain").then(blob => blob.text()), + items["text/plain"], + "Check text/plain type in clipbordItem"); + }); +}); + +add_task(async function test_no_mandatory_type() { + const items = { + "custom/foo": "X" + Math.random(), + }; + await copyByDataTransfer(items); + await paste(async (clipboardData) => { + for (const [key, value] of Object.entries(items)) { + is(clipboardData.getData(key), value, `Check ${key} type`); + } + + let clipboardItems = await navigator.clipboard.read(); + is(clipboardItems.length, 0, "Should only have no clipboardItem"); + }); +}); + +</script> +</head> +<body> +</body> +</html> diff --git a/dom/events/test/mochitest.toml b/dom/events/test/mochitest.toml index 53675a8f49..19a082b1e3 100644 --- a/dom/events/test/mochitest.toml +++ b/dom/events/test/mochitest.toml @@ -448,8 +448,6 @@ skip-if = [ ["test_legacy_touch_api.html"] -["test_marquee_events.html"] - ["test_messageEvent.html"] ["test_messageEvent_init.html"] diff --git a/dom/events/test/pointerevents/mochitest.toml b/dom/events/test/pointerevents/mochitest.toml index 340704f94e..c22e1bdf94 100644 --- a/dom/events/test/pointerevents/mochitest.toml +++ b/dom/events/test/pointerevents/mochitest.toml @@ -126,17 +126,6 @@ skip-if = [ "http2", ] -["test_wpt_pointerevent_change-touch-action-onpointerdown_touch-manual.html"] -support-files = ["wpt/pointerevent_change-touch-action-onpointerdown_touch-manual.html"] -disabled = "disabled" - -["test_wpt_pointerevent_constructor.html"] -support-files = ["wpt/pointerevent_constructor.html"] -skip-if = [ - "http3", - "http2", -] - ["test_wpt_pointerevent_drag_interaction-manual.html"] support-files = ["wpt/html/pointerevent_drag_interaction-manual.html"] skip-if = [ @@ -158,10 +147,6 @@ skip-if = [ support-files = ["wpt/pointerevent_multiple_primary_pointers_boundary_events-manual.html"] disabled = "should be investigated" -["test_wpt_pointerevent_pointerId_scope-manual.html"] -support-files = ["wpt/resources/pointerevent_pointerId_scope-iframe.html"] -disabled = "should be investigated" - ["test_wpt_pointerevent_pointercancel_touch-manual.html"] support-files = ["wpt/pointerevent_pointercancel_touch-manual.html"] skip-if = [ diff --git a/dom/events/test/pointerevents/test_wpt_pointerevent_change-touch-action-onpointerdown_touch-manual.html b/dom/events/test/pointerevents/test_wpt_pointerevent_change-touch-action-onpointerdown_touch-manual.html deleted file mode 100644 index f95b16c850..0000000000 --- a/dom/events/test/pointerevents/test_wpt_pointerevent_change-touch-action-onpointerdown_touch-manual.html +++ /dev/null @@ -1,39 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- -https://bugzilla.mozilla.org/show_bug.cgi?id=1000870 ---> - <head> - <meta charset="utf-8"> - <title>Test for Bug 1000870</title> - <meta name="author" content="Maksim Lebedev" /> - <script src="/tests/SimpleTest/SimpleTest.js"></script> - <script src="/tests/SimpleTest/EventUtils.js"></script> - <script type="text/javascript" src="mochitest_support_external.js"></script> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> - <script type="text/javascript"> - SimpleTest.waitForExplicitFinish(); - function startTest() { - runTestInNewWindow("pointerevent_change-touch-action-onpointerdown_touch-manual.html"); - } - function executeTest(int_win) { - const WM_VSCROLL = 0x0115; - sendTouchEvent(int_win, "target0", "touchstart"); - sendTouchEvent(int_win, "target0", "touchmove"); - sendTouchEvent(int_win, "target0", "touchend"); - - // NOTE: This testcase is about that modifying touch-action during a - // pointerdown callback "should not" affect the gesture detection of the - // touch session started by the pointerdown. That is, a scroll should - // still fired by gesture detection, instead of launching by our own. - var utils = _getDOMWindowUtils(int_win); - var target0 = int_win.document.getElementById("target0"); - utils.sendNativeMouseScrollEvent(target0.getBoundingClientRect().left + 5, - target0.getBoundingClientRect().top + 5, - WM_VSCROLL, 10, 10, 0, 0, 0, target0); - } - </script> - </head> - <body> - </body> -</html> diff --git a/dom/events/test/pointerevents/test_wpt_pointerevent_constructor.html b/dom/events/test/pointerevents/test_wpt_pointerevent_constructor.html deleted file mode 100644 index 058e32a967..0000000000 --- a/dom/events/test/pointerevents/test_wpt_pointerevent_constructor.html +++ /dev/null @@ -1,26 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- -https://bugzilla.mozilla.org/show_bug.cgi?id=1000870 ---> - <head> - <meta charset="utf-8"> - <title>Test for Bug 1000870</title> - <meta name="author" content="Maksim Lebedev" /> - <script src="/tests/SimpleTest/SimpleTest.js"></script> - <script src="/tests/SimpleTest/EventUtils.js"></script> - <script type="text/javascript" src="mochitest_support_external.js"></script> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> - <script type="text/javascript"> - SimpleTest.waitForExplicitFinish(); - function startTest() { - runTestInNewWindow("wpt/pointerevent_constructor.html"); - } - function executeTest(int_win) { - // Function should be, but can be empty - } - </script> - </head> - <body> - </body> -</html> diff --git a/dom/events/test/pointerevents/test_wpt_pointerevent_pointerId_scope-manual.html b/dom/events/test/pointerevents/test_wpt_pointerevent_pointerId_scope-manual.html deleted file mode 100644 index f52bf7fc20..0000000000 --- a/dom/events/test/pointerevents/test_wpt_pointerevent_pointerId_scope-manual.html +++ /dev/null @@ -1,27 +0,0 @@ -<!DOCTYPE HTML> -<html> -<!-- -https://bugzilla.mozilla.org/show_bug.cgi?id=1000870 ---> - <head> - <meta charset="utf-8"> - <title>Test for Bug 1000870</title> - <meta name="author" content="Maksim Lebedev" /> - <script src="/tests/SimpleTest/SimpleTest.js"></script> - <script src="/tests/SimpleTest/EventUtils.js"></script> - <script type="text/javascript" src="mochitest_support_external.js"></script> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> - <script type="text/javascript"> - SimpleTest.waitForExplicitFinish(); - function startTest() { - runTestInNewWindow("wpt/pointerevent_pointerId_scope-manual.html"); - } - function executeTest(int_win) { - sendTouchEvent(int_win, "target0", "touchstart"); - sendTouchEvent(int_win, "target0", "touchend"); - } - </script> - </head> - <body> - </body> -</html> diff --git a/dom/events/test/pointerevents/wpt/pointerevent_change-touch-action-onpointerdown_touch-manual.html b/dom/events/test/pointerevents/wpt/pointerevent_change-touch-action-onpointerdown_touch-manual.html deleted file mode 100644 index 04d56cb7a5..0000000000 --- a/dom/events/test/pointerevents/wpt/pointerevent_change-touch-action-onpointerdown_touch-manual.html +++ /dev/null @@ -1,135 +0,0 @@ -<!doctype html> -<html> - <head> - <title>Change touch-action on pointerdown</title> - <meta name="viewport" content="width=device-width"> - <link rel="stylesheet" type="text/css" href="pointerevent_styles.css"> - <script src="/resources/testharness.js"></script> - <script src="/resources/testharnessreport.js"></script> - <script src="pointerevent_support.js"></script> - <style> - #target0 { - background: black; - width: 700px; - height: 430px; - color: white; - overflow-y: auto; - overflow-x: auto; - white-space: nowrap; - } - </style> - </head> - <body onload="run()"> - <h1>Pointer Events touch-action attribute support</h1> - <h4>Test Description: Press and hold your touch. Try to scroll text in any direction. - Then release your touch and try to scroll again. Expected: no panning. - </h4> - <p>Note: this test is for touch-devices only</p> - <div id="target0" style="touch-action: auto;"> - <p> - Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem - nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat. - Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit - lobortis nisl ut aliquip ex ea commodo consequat. - </p> - <p>Lorem ipsum dolor sit amet...</p> - <p>Lorem ipsum dolor sit amet...</p> - <p>Lorem ipsum dolor sit amet...</p> - <p>Lorem ipsum dolor sit amet...</p> - <p>Lorem ipsum dolor sit amet...</p> - <p>Lorem ipsum dolor sit amet...</p> - <p>Lorem ipsum dolor sit amet...</p> - <p> - Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem - nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat. - Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit - lobortis nisl ut aliquip ex ea commodo consequat. - </p> - <p>Lorem ipsum dolor sit amet...</p> - <p>Lorem ipsum dolor sit amet...</p> - <p>Lorem ipsum dolor sit amet...</p> - <p>Lorem ipsum dolor sit amet...</p> - <p>Lorem ipsum dolor sit amet...</p> - <p>Lorem ipsum dolor sit amet...</p> - <p>Lorem ipsum dolor sit amet...</p> - <p> - Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem - nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat. - Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit - lobortis nisl ut aliquip ex ea commodo consequat. - </p> - <p>Lorem ipsum dolor sit amet...</p> - <p>Lorem ipsum dolor sit amet...</p> - <p>Lorem ipsum dolor sit amet...</p> - <p>Lorem ipsum dolor sit amet...</p> - <p>Lorem ipsum dolor sit amet...</p> - <p>Lorem ipsum dolor sit amet...</p> - <p>Lorem ipsum dolor sit amet...</p> - <p> - Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem - nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat. - Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit - lobortis nisl ut aliquip ex ea commodo consequat. - </p> - <p>Lorem ipsum dolor sit amet...</p> - <p>Lorem ipsum dolor sit amet...</p> - <p>Lorem ipsum dolor sit amet...</p> - <p>Lorem ipsum dolor sit amet...</p> - <p>Lorem ipsum dolor sit amet...</p> - <p>Lorem ipsum dolor sit amet...</p> - <p>Lorem ipsum dolor sit amet...</p> - </div> - <script type='text/javascript'> - var detected_pointertypes = {}; - - var styleIsChanged = false; - var scrollIsReceived = false; - var firstTouchCompleted = false; - var countToPass = 50; - var xScr0, yScr0, xScr1, yScr1; - - setup({ explicit_done: true }); - add_completion_callback(showPointerTypes); - - function run() { - var target0 = document.getElementById("target0"); - - on_event(target0, 'scroll', function(event) { - if(!scrollIsReceived && firstTouchCompleted) { - test(function() { - failOnScroll(); - }, "scroll was received while shouldn't"); - scrollIsReceived = true; - } - done(); - }); - - on_event(target0, 'pointerdown', function(event) { - detected_pointertypes[event.pointerType] = true; - if(!styleIsChanged) { - var before = document.getElementById('target0').style.touchAction; - - document.getElementById('target0').style.touchAction = 'none'; - - var after = document.getElementById('target0').style.touchAction; - - test(function() { - assert_true(before != after, "touch-action was changed"); - }, "touch-action was changed"); - - styleIsChanged = true; - } - }); - - on_event(target0, 'pointerup', function(event) { - firstTouchCompleted = true; - }); - } - </script> - <h1>touch-action: auto to none</h1> - <div id="complete-notice"> - <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p> - </div> - <div id="log"></div> - </body> -</html>
\ No newline at end of file diff --git a/dom/events/test/pointerevents/wpt/pointerevent_constructor.html b/dom/events/test/pointerevents/wpt/pointerevent_constructor.html deleted file mode 100644 index b2a779d1f7..0000000000 --- a/dom/events/test/pointerevents/wpt/pointerevent_constructor.html +++ /dev/null @@ -1,106 +0,0 @@ -<!doctype html> -<html> - <head> - <title>PointerEvent: Constructor test</title> - <meta name="viewport" content="width=device-width"> - <link rel="stylesheet" type="text/css" href="pointerevent_styles.css"> - <script src="/resources/testharness.js"></script> - <script src="/resources/testharnessreport.js"></script> - <!-- Additional helper script for common checks across event types --> - <script type="text/javascript" src="pointerevent_support.js"></script> - </head> - <body> - <h1>PointerEvent: Dispatch custom event</h1> - <h4>Test Description: This test checks if PointerEvent constructor works properly using synthetic pointerover and pointerout events. For valid results, this test must be run without generating real (trusted) pointerover or pointerout events on the black rectangle below.</h4> - <div id="target0"></div> - <script> - var detected_pointertypes = {}; - add_completion_callback(showPointerTypes); - - async_test(function() { - var target0 = document.getElementById("target0"); - // set values for non-default constructor - var testBubbles = true; - var testCancelable = true; - var testPointerId = 42; - var testPointerType = 'pen'; - var testClientX = 300; - var testClientY = 500; - var testWidth = 3; - var testHeight = 5; - var testTiltX = -45; - var testTiltY = 30; - var testButton = 0; - var testButtons = 1; - var testPressure = 0.4; - var testIsPrimary = true; - - on_event(target0, "pointerover", this.step_func(function(event) { - detected_pointertypes[ event.pointerType ] = true; - generate_tests(assert_equals, [ - ["custom bubbles", event.bubbles, testBubbles], - ["custom cancelable", event.cancelable, testCancelable], - ["custom pointerId", event.pointerId, testPointerId], - ["custom pointerType", event.pointerType, testPointerType], - ["custom button", event.button, testButton], - ["custom buttons", event.buttons, testButtons], - ["custom width", event.width, testWidth], - ["custom height", event.height, testHeight], - ["custom clientX", event.clientX, testClientX], - ["custom clientY", event.clientY, testClientY], - ["custom tiltX", event.tiltX, testTiltX], - ["custom tiltY", event.tiltY, testTiltY], - ["custom isPrimary", event.isPrimary, testIsPrimary] - ]); - test(function() { - assert_approx_equals(event.pressure, testPressure, 0.00000001, "custom pressure: "); - }, "custom pressure: "); - })); - - on_event(target0, "pointerout", this.step_func(function(event) { - generate_tests(assert_equals, [ - ["default pointerId", event.pointerId, 0], - ["default pointerType", event.pointerType, ""], - ["default width", event.width, 1], - ["default height", event.height, 1], - ["default tiltX", event.tiltX, 0], - ["default tiltY", event.tiltY, 0], - ["default pressure", event.pressure, 0], - ["default isPrimary", event.isPrimary, false] - ]); - })); - - on_event(window, "load", this.step_func_done(function() { - assert_not_equals(window.PointerEvent, undefined); - - var pointerEventCustom = new PointerEvent("pointerover", - {bubbles: testBubbles, - cancelable: testCancelable, - pointerId: testPointerId, - pointerType: testPointerType, - width: testWidth, - height: testHeight, - clientX: testClientX, - clientY: testClientY, - tiltX: testTiltX, - tiltY: testTiltY, - button: testButton, - buttons: testButtons, - pressure: testPressure, - isPrimary: testIsPrimary - }); - // A PointerEvent created with a PointerEvent constructor must have all its attributes set to the corresponding values provided to the constructor. - // For attributes where values are not provided to the constructor, the corresponding default values must be used. - // TA: 12.1 - target0.dispatchEvent(pointerEventCustom); - var pointerEventDefault = new PointerEvent("pointerout"); - target0.dispatchEvent(pointerEventDefault); - }, "PointerEvent constructor")); - }) - </script> - <div id="complete-notice"> - <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p> - </div> - <div id="log"></div> - </body> -</html> diff --git a/dom/events/test/pointerevents/wpt/pointerevent_pointerId_scope-manual.html b/dom/events/test/pointerevents/wpt/pointerevent_pointerId_scope-manual.html deleted file mode 100644 index 3640cb6f6b..0000000000 --- a/dom/events/test/pointerevents/wpt/pointerevent_pointerId_scope-manual.html +++ /dev/null @@ -1,82 +0,0 @@ -<!doctype html> -<html> - <!-- -Test cases for Pointer Events v1 spec -This document references Test Assertions (abbrev TA below) written by Cathy Chan -http://www.w3.org/wiki/PointerEvents/TestAssertions ---> - <head> - <title>Pointer Events pointerdown tests</title> - <meta name="viewport" content="width=device-width"> - <link rel="stylesheet" type="text/css" href="pointerevent_styles.css"> - <script src="/resources/testharness.js"></script> - <script src="/resources/testharnessreport.js"></script> - <!-- Additional helper script for common checks across event types --> - <script type="text/javascript" src="pointerevent_support.js"></script> - <script> - var detected_pointertypes = {}; - var test_pointerEvent = async_test("pointerId of an active pointer is the same across iframes"); - // showPointerTypes is defined in pointerevent_support.js - // Requirements: the callback function will reference the test_pointerEvent object and - // will fail unless the async_test is created with the var name "test_pointerEvent". - add_completion_callback(showPointerTypes); - var detected_pointertypes = {}; - - function run() { - var target0 = document.getElementById("target0"); - var pointerover_pointerId = null; - var pointerover_pointerType = null; - - var eventList = ['pointerenter', 'pointerover', 'pointermove', 'pointerout', 'pointerleave']; - var receivedEvents = {}; - var receivedEventsInnerFrame = {}; - - - function checkPointerId(event, inner) { - detected_pointertypes[event.pointerType] = true; - var eventName = (inner ? "inner frame " : "" ) + event.type; - test_pointerEvent.step(function() { - assert_equals(event.pointerId, pointerover_pointerId, "PointerId of " + eventName + " is not correct"); - assert_equals(event.pointerType, pointerover_pointerType, "PointerType of " + eventName + " is not correct"); - }, eventName + ".pointerId were the same as first pointerover"); - } - - on_event(window, "message", function(event) { - var pe_event = JSON.parse(event.data); - receivedEventsInnerFrame[pe_event.type] = 1; - checkPointerId(pe_event, true); - if (Object.keys(receivedEvents).length == eventList.length && Object.keys(receivedEventsInnerFrame).length == eventList.length) - test_pointerEvent.done(); - }); - - eventList.forEach(function(eventName) { - on_event(target0, eventName, function (event) { - if (pointerover_pointerId === null && event.type == 'pointerover') { - pointerover_pointerId = event.pointerId; - pointerover_pointerType = event.pointerType; - } else { - checkPointerId(event, false); - } - receivedEvents[event.type] = 1; - }); - }); - } - </script> - </head> - <body onload="run()"> - <h1>Pointer Events pointerdown tests</h1> - Complete the following actions: - <ol> - <li>Start with your pointing device outside of black box, then move it into black box. If using touch just press in black box and don't release. - <li>Move your pointing device into purple box (without leaving the digitizer range if you are using hover supported pen or without releasing touch if using touch). Then move it out of the purple box. - </ol> - <div id="target0" class="touchActionNone"> - </div> - <iframe src="resources/pointerevent_pointerId_scope-iframe.html" id="innerframe"></iframe> - <div id="complete-notice"> - <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p> - <p>Refresh the page to run the tests again with a different pointer type.</p> - </div> - <div id="log"></div> - </body> -</html> diff --git a/dom/events/test/test_all_synthetic_events.html b/dom/events/test/test_all_synthetic_events.html index d8de1a9148..3a00227f1b 100644 --- a/dom/events/test/test_all_synthetic_events.html +++ b/dom/events/test/test_all_synthetic_events.html @@ -376,6 +376,13 @@ const kEventConstructors = { return new TCPServerSocketEvent(aName, aProps); }, }, + TextEvent : { create (aName, aProps) { + var e = document.createEvent("textevent"); + e.initTextEvent("textInput", aProps.bubbles, aProps.cancelable, + aProps.view, aProps.data); + return e; + }, + }, TimeEvent: { create: null // Cannot create untrusted event from JS }, @@ -477,7 +484,8 @@ function test() { SimpleTest.waitForExplicitFinish(); SpecialPowers.pushPrefEnv( {"set": [["dom.w3c_touch_events.legacy_apis.enabled", true], - ["layout.css.content-visibility.enabled", true]]}, + ["layout.css.content-visibility.enabled", true], + ["dom.events.textevent.enabled", true]]}, function() { test(); SimpleTest.finish(); diff --git a/dom/events/test/test_eventctors.html b/dom/events/test/test_eventctors.html index 01ae59493e..26b9d647bf 100644 --- a/dom/events/test/test_eventctors.html +++ b/dom/events/test/test_eventctors.html @@ -924,6 +924,28 @@ is(e.dataTransfer, null, "InputEvent.dataTransfer should be null in default"); is(e.inputType, "", "InputEvent.inputType should be empty string in default"); is(e.isComposing, false, "InputEvent.isComposing should be false in default"); +// TextEvent +if (SpecialPowers.getBoolPref("dom.events.textevent.enabled")) { + try { + e = new TextEvent(); + ok(false, "TextEvent should not have constructor"); + } catch (exp) { + ok(true, "TextEvent does not have a constructor"); + } + try { + e = new TextEvent("foo"); + ok(false, "TextEvent should not have constructor"); + } catch (exp) { + ok(true, "TextEvent does not have a constructor taking a event type"); + } + try { + e = new TextEvent("foo", {}); + ok(false, "TextEvent should not have constructor"); + } catch (exp) { + ok(true, "TextEvent does not have a constructor taking event type and a dictionary"); + } +} + </script> </pre> </body> diff --git a/dom/events/test/test_marquee_events.html b/dom/events/test/test_marquee_events.html deleted file mode 100644 index 22d0eafdf1..0000000000 --- a/dom/events/test/test_marquee_events.html +++ /dev/null @@ -1,31 +0,0 @@ -<html> -<head> - <meta charset="utf-8"> - <title>Test for bug 1425874</title> - <link rel="stylesheet" href="/tests/SimpleTest/test.css"> - <script src="/tests/SimpleTest/SimpleTest.js"></script> - <script src="/tests/SimpleTest/EventUtils.js"></script> -</head> -<body> - <script> - var wasEventCalled; - function callEventWithAttributeHandler(element, evt) { - wasEventCalled = false; - let el = document.createElement(element); - el.setAttribute(`on${evt}`, "wasEventCalled = true"); - el.dispatchEvent(new Event(evt)); - return wasEventCalled; - } - - info("Make sure the EventNameType_HTMLMarqueeOnly events only compile for marquee"); - - ok(!callEventWithAttributeHandler("div", "bounce"), "no onbounce for div"); - ok(!callEventWithAttributeHandler("div", "finish"), "no onfinish for div"); - ok(!callEventWithAttributeHandler("div", "start"), "no onstart for div"); - - ok(callEventWithAttributeHandler("marquee", "bounce"), "onbounce for marquee"); - ok(callEventWithAttributeHandler("marquee", "finish"), "onfinish for marquee"); - ok(callEventWithAttributeHandler("marquee", "start"), "onstart for marquee"); - </script> -</body> -</html> |