diff options
Diffstat (limited to 'dom/events')
22 files changed, 636 insertions, 225 deletions
diff --git a/dom/events/Clipboard.cpp b/dom/events/Clipboard.cpp index 560002dd68..b163bc816f 100644 --- a/dom/events/Clipboard.cpp +++ b/dom/events/Clipboard.cpp @@ -29,6 +29,7 @@ #include "nsArrayUtils.h" #include "nsComponentManagerUtils.h" #include "nsContentUtils.h" +#include "nsGlobalWindowInner.h" #include "nsIClipboard.h" #include "nsIInputStream.h" #include "nsIParserUtils.h" @@ -86,6 +87,16 @@ class ClipboardGetCallback : public nsIAsyncClipboardGetCallback { RefPtr<Promise> mPromise; }; +static nsTArray<nsCString> MandatoryDataTypesAsCStrings() { + // Mandatory data types defined in + // https://w3c.github.io/clipboard-apis/#mandatory-data-types-x. The types + // should be in the same order as kNonPlainTextExternalFormats in + // DataTransfer. + return nsTArray<nsCString>{nsLiteralCString(kHTMLMime), + nsLiteralCString(kTextMime), + nsLiteralCString(kPNGImageMime)}; +} + class ClipboardGetCallbackForRead final : public ClipboardGetCallback { public: explicit ClipboardGetCallbackForRead(nsIGlobalObject* aGlobal, @@ -109,11 +120,15 @@ class ClipboardGetCallbackForRead final : public ClipboardGetCallback { } AutoTArray<RefPtr<ClipboardItem::ItemEntry>, 3> entries; - for (const auto& format : flavorList) { - auto entry = MakeRefPtr<ClipboardItem::ItemEntry>( - mGlobal, NS_ConvertUTF8toUTF16(format)); - entry->LoadDataFromSystemClipboard(aAsyncGetClipboardData); - entries.AppendElement(std::move(entry)); + // We might reuse the request from DataTransfer created for paste event, + // which could contain more types that are not in the mandatory list. + for (const auto& format : MandatoryDataTypesAsCStrings()) { + if (flavorList.Contains(format)) { + auto entry = MakeRefPtr<ClipboardItem::ItemEntry>( + mGlobal, NS_ConvertUTF8toUTF16(format)); + entry->LoadDataFromSystemClipboard(aAsyncGetClipboardData); + entries.AppendElement(std::move(entry)); + } } RefPtr<Promise> p(std::move(mPromise)); @@ -214,6 +229,36 @@ NS_IMPL_ISUPPORTS(ClipboardGetCallbackForReadText, nsIAsyncClipboardGetCallback, } // namespace +void Clipboard::RequestRead(Promise& aPromise, const ReadRequestType& aType, + nsPIDOMWindowInner& aOwner, + nsIPrincipal& aSubjectPrincipal, + nsIAsyncGetClipboardData& aRequest) { +#ifdef DEBUG + bool isValid = false; + MOZ_ASSERT(NS_SUCCEEDED(aRequest.GetValid(&isValid)) && isValid); +#endif + + RefPtr<ClipboardGetCallback> callback; + switch (aType) { + case ReadRequestType::eRead: { + callback = + MakeRefPtr<ClipboardGetCallbackForRead>(aOwner.AsGlobal(), &aPromise); + break; + } + case ReadRequestType::eReadText: { + callback = MakeRefPtr<ClipboardGetCallbackForReadText>(&aPromise); + break; + } + default: { + MOZ_ASSERT_UNREACHABLE("Unknown read type"); + return; + } + } + + MOZ_ASSERT(callback); + callback->OnSuccess(&aRequest); +} + void Clipboard::RequestRead(Promise* aPromise, ReadRequestType aType, nsPIDOMWindowInner* aOwner, nsIPrincipal& aPrincipal) { @@ -239,19 +284,14 @@ void Clipboard::RequestRead(Promise* aPromise, ReadRequestType aType, callback = MakeRefPtr<ClipboardGetCallbackForRead>(global, std::move(p)); rv = clipboardService->AsyncGetData( - // Mandatory data types defined in - // https://w3c.github.io/clipboard-apis/#mandatory-data-types-x - AutoTArray<nsCString, 3>{nsDependentCString(kHTMLMime), - nsDependentCString(kTextMime), - nsDependentCString(kPNGImageMime)}, - nsIClipboard::kGlobalClipboard, owner->GetWindowContext(), - &aPrincipal, callback); + MandatoryDataTypesAsCStrings(), nsIClipboard::kGlobalClipboard, + owner->GetWindowContext(), &aPrincipal, callback); break; } case ReadRequestType::eReadText: { callback = MakeRefPtr<ClipboardGetCallbackForReadText>(std::move(p)); rv = clipboardService->AsyncGetData( - AutoTArray<nsCString, 1>{nsDependentCString(kTextMime)}, + AutoTArray<nsCString, 1>{nsLiteralCString(kTextMime)}, nsIClipboard::kGlobalClipboard, owner->GetWindowContext(), &aPrincipal, callback); break; @@ -288,6 +328,24 @@ already_AddRefed<Promise> Clipboard::ReadHelper(nsIPrincipal& aSubjectPrincipal, return p.forget(); } + // If a "paste" clipboard event is actively being processed, we're + // intentionally skipping permission/user-activation checks and giving the + // webpage access to the clipboard. + if (RefPtr<DataTransfer> dataTransfer = + nsGlobalWindowInner::Cast(owner)->GetCurrentPasteDataTransfer()) { + // If there is valid nsIAsyncGetClipboardData, use it directly. + if (nsCOMPtr<nsIAsyncGetClipboardData> asyncGetClipboardData = + dataTransfer->GetAsyncGetClipboardData()) { + bool isValid = false; + asyncGetClipboardData->GetValid(&isValid); + if (isValid) { + RequestRead(*p, aType, *owner, aSubjectPrincipal, + *asyncGetClipboardData); + return p.forget(); + } + } + } + if (IsTestingPrefEnabledOrHasReadPermission(aSubjectPrincipal)) { MOZ_LOG(GetClipboardLog(), LogLevel::Debug, ("%s: testing pref enabled or has read permission", __FUNCTION__)); diff --git a/dom/events/Clipboard.h b/dom/events/Clipboard.h index 2df4df1ec5..a9952e6052 100644 --- a/dom/events/Clipboard.h +++ b/dom/events/Clipboard.h @@ -13,7 +13,8 @@ #include "mozilla/Logging.h" #include "mozilla/RefPtr.h" #include "mozilla/UniquePtr.h" -#include "mozilla/dom/DataTransfer.h" + +class nsIAsyncGetClipboardData; namespace mozilla::dom { @@ -75,6 +76,10 @@ class Clipboard : public DOMEventTargetHelper { void RequestRead(Promise* aPromise, ReadRequestType aType, nsPIDOMWindowInner* aOwner, nsIPrincipal& aPrincipal); + + void RequestRead(Promise& aPromise, const ReadRequestType& aType, + nsPIDOMWindowInner& aOwner, nsIPrincipal& aSubjectPrincipal, + nsIAsyncGetClipboardData& aRequest); }; } // namespace mozilla::dom diff --git a/dom/events/DataTransfer.cpp b/dom/events/DataTransfer.cpp index ad8c7059da..ba56575749 100644 --- a/dom/events/DataTransfer.cpp +++ b/dom/events/DataTransfer.cpp @@ -622,52 +622,64 @@ already_AddRefed<DataTransfer> DataTransfer::MozCloneForEvent( } // The order of the types matters. `kFileMime` needs to be one of the first two -// types. -static const char* kNonPlainTextExternalFormats[] = { - kCustomTypesMime, kFileMime, kHTMLMime, kRTFMime, kURLMime, - kURLDataMime, kTextMime, kPNGImageMime, kPDFJSMime}; - -/* static */ -void DataTransfer::GetExternalClipboardFormats(const int32_t& aWhichClipboard, - const bool& aPlainTextOnly, - nsTArray<nsCString>* aResult) { - MOZ_ASSERT(aResult); - +// types. And the order should be the same as the types order defined in +// MandatoryDataTypesAsCStrings() for Clipboard API. +static const nsCString kNonPlainTextExternalFormats[] = { + nsLiteralCString(kCustomTypesMime), nsLiteralCString(kFileMime), + nsLiteralCString(kHTMLMime), nsLiteralCString(kRTFMime), + nsLiteralCString(kURLMime), nsLiteralCString(kURLDataMime), + nsLiteralCString(kTextMime), nsLiteralCString(kPNGImageMime), + nsLiteralCString(kPDFJSMime)}; + +void DataTransfer::GetExternalClipboardFormats(const bool& aPlainTextOnly, + nsTArray<nsCString>& aResult) { // NOTE: When you change this method, you may need to change // GetExternalTransferableFormats() too since those methods should // work similarly. + MOZ_ASSERT(!mAsyncGetClipboardData); + + RefPtr<WindowContext> wc = GetWindowContext(); + if (NS_WARN_IF(!wc)) { + MOZ_ASSERT_UNREACHABLE( + "How could this DataTransfer be created with a non-window global?"); + return; + } + nsCOMPtr<nsIClipboard> clipboard = do_GetService("@mozilla.org/widget/clipboard;1"); - if (!clipboard || aWhichClipboard < 0) { + if (!clipboard || mClipboardType < 0) { return; } + nsresult rv = NS_ERROR_FAILURE; + nsCOMPtr<nsIAsyncGetClipboardData> asyncGetClipboardData; if (aPlainTextOnly) { - bool hasType; - AutoTArray<nsCString, 1> textMime = {nsDependentCString(kTextMime)}; - nsresult rv = - clipboard->HasDataMatchingFlavors(textMime, aWhichClipboard, &hasType); - NS_SUCCEEDED(rv); - if (hasType) { - aResult->AppendElement(kTextMime); - } + rv = clipboard->GetDataSnapshotSync( + AutoTArray<nsCString, 1>{nsLiteralCString(kTextMime)}, mClipboardType, + wc, getter_AddRefs(asyncGetClipboardData)); + } else { + AutoTArray<nsCString, ArrayLength(kNonPlainTextExternalFormats)> formats; + formats.AppendElements(Span<const nsCString>(kNonPlainTextExternalFormats)); + rv = clipboard->GetDataSnapshotSync(formats, mClipboardType, wc, + getter_AddRefs(asyncGetClipboardData)); + } + + if (NS_FAILED(rv) || !asyncGetClipboardData) { return; } - // If not plain text only, then instead check all the other types - for (uint32_t f = 0; f < mozilla::ArrayLength(kNonPlainTextExternalFormats); - ++f) { - bool hasType; - AutoTArray<nsCString, 1> format = { - nsDependentCString(kNonPlainTextExternalFormats[f])}; - nsresult rv = - clipboard->HasDataMatchingFlavors(format, aWhichClipboard, &hasType); - NS_SUCCEEDED(rv); - if (hasType) { - aResult->AppendElement(kNonPlainTextExternalFormats[f]); + // Order is important for DataTransfer; ensure the returned list items follow + // the sequence specified in kNonPlainTextExternalFormats. + AutoTArray<nsCString, ArrayLength(kNonPlainTextExternalFormats)> flavors; + asyncGetClipboardData->GetFlavorList(flavors); + for (const auto& format : kNonPlainTextExternalFormats) { + if (flavors.Contains(format)) { + aResult.AppendElement(format); } } + + mAsyncGetClipboardData = asyncGetClipboardData; } /* static */ @@ -695,10 +707,10 @@ void DataTransfer::GetExternalTransferableFormats( } // If not plain text only, then instead check all the other types - for (const char* format : kNonPlainTextExternalFormats) { - auto index = flavors.IndexOf(nsCString(format)); + for (const auto& format : kNonPlainTextExternalFormats) { + auto index = flavors.IndexOf(format); if (index != flavors.NoIndex) { - aResult->AppendElement(nsCString(format)); + aResult->AppendElement(format); } } } @@ -1192,7 +1204,10 @@ void DataTransfer::Disconnect() { } } -void DataTransfer::ClearAll() { mItems->ClearAllItems(); } +void DataTransfer::ClearAll() { + mItems->ClearAllItems(); + mAsyncGetClipboardData = nullptr; +} uint32_t DataTransfer::MozItemCount() const { return mItems->MozItemCount(); } @@ -1260,6 +1275,24 @@ already_AddRefed<nsIGlobalObject> DataTransfer::GetGlobal() const { return global.forget(); } +already_AddRefed<WindowContext> DataTransfer::GetWindowContext() const { + nsCOMPtr<nsIGlobalObject> global = GetGlobal(); + if (!global) { + return nullptr; + } + + const auto* innerWindow = global->GetAsInnerWindow(); + if (!innerWindow) { + return nullptr; + } + + return do_AddRef(innerWindow->GetWindowContext()); +} + +nsIAsyncGetClipboardData* DataTransfer::GetAsyncGetClipboardData() const { + return mAsyncGetClipboardData; +} + nsresult DataTransfer::CacheExternalData(const char* aFormat, uint32_t aIndex, nsIPrincipal* aPrincipal, bool aHidden) { @@ -1357,20 +1390,13 @@ void DataTransfer::CacheExternalClipboardFormats(bool aPlainTextOnly) { "caching clipboard data for invalid event"); nsCOMPtr<nsIPrincipal> sysPrincipal = nsContentUtils::GetSystemPrincipal(); - nsTArray<nsCString> typesArray; - - if (XRE_IsContentProcess()) { - ContentChild::GetSingleton()->SendGetExternalClipboardFormats( - mClipboardType, aPlainTextOnly, &typesArray); - } else { - GetExternalClipboardFormats(mClipboardType, aPlainTextOnly, &typesArray); - } - + GetExternalClipboardFormats(aPlainTextOnly, typesArray); if (aPlainTextOnly) { // The only thing that will be in types is kTextMime MOZ_ASSERT(typesArray.IsEmpty() || typesArray.Length() == 1); if (typesArray.Length() == 1) { + MOZ_ASSERT(typesArray.Contains(kTextMime)); CacheExternalData(kTextMime, 0, sysPrincipal, false); } return; diff --git a/dom/events/DataTransfer.h b/dom/events/DataTransfer.h index ea0368216a..7ff2bff54b 100644 --- a/dom/events/DataTransfer.h +++ b/dom/events/DataTransfer.h @@ -23,6 +23,7 @@ #include "mozilla/dom/DataTransferItemList.h" #include "mozilla/dom/File.h" +class nsIAsyncGetClipboardData; class nsINode; class nsITransferable; class nsILoadContext; @@ -386,14 +387,6 @@ class DataTransfer final : public nsISupports, public nsWrapperCache { } bool MozShowFailAnimation() const { return mShowFailAnimation; } - // Retrieve a list of clipboard formats supported - // - // If kFileMime is supported, then it will be placed either at - // index 0 or at index 1 in aResult - static void GetExternalClipboardFormats(const int32_t& aWhichClipboard, - const bool& aPlainTextOnly, - nsTArray<nsCString>* aResult); - // Retrieve a list of supporting formats in aTransferable. // // If kFileMime is supported, then it will be placed either at @@ -428,7 +421,18 @@ class DataTransfer final : public nsISupports, public nsWrapperCache { already_AddRefed<nsIGlobalObject> GetGlobal() const; + already_AddRefed<WindowContext> GetWindowContext() const; + + nsIAsyncGetClipboardData* GetAsyncGetClipboardData() const; + protected: + // Retrieve a list of clipboard formats supported + // + // If kFileMime is supported, then it will be placed either at + // index 0 or at index 1 in aResult + void GetExternalClipboardFormats(const bool& aPlainTextOnly, + nsTArray<nsCString>& aResult); + // caches text and uri-list data formats that exist in the drag service or // clipboard for retrieval later. nsresult CacheExternalData(const char* aFormat, uint32_t aIndex, @@ -506,6 +510,11 @@ class DataTransfer final : public nsISupports, public nsWrapperCache { // drag and drop. int32_t mClipboardType; + // The nsIAsyncGetClipboardData that is used for getting clipboard formats. + // XXXedgar we should get the actual data from this in the future, see bug + // 1879401. + nsCOMPtr<nsIAsyncGetClipboardData> mAsyncGetClipboardData; + // The items contained with the DataTransfer RefPtr<DataTransferItemList> mItems; diff --git a/dom/events/EventListenerService.cpp b/dom/events/EventListenerService.cpp index 7e4919d938..3398f0dd02 100644 --- a/dom/events/EventListenerService.cpp +++ b/dom/events/EventListenerService.cpp @@ -220,21 +220,6 @@ EventListenerService::GetListenerInfoFor( } NS_IMETHODIMP -EventListenerService::GetEventTargetChainFor( - EventTarget* aEventTarget, bool aComposed, - nsTArray<RefPtr<EventTarget>>& aOutArray) { - NS_ENSURE_ARG(aEventTarget); - WidgetEvent event(true, eVoidEvent); - event.SetComposed(aComposed); - nsTArray<EventTarget*> targets; - nsresult rv = EventDispatcher::Dispatch(aEventTarget, nullptr, &event, - nullptr, nullptr, nullptr, &targets); - NS_ENSURE_SUCCESS(rv, rv); - aOutArray.AppendElements(targets); - return NS_OK; -} - -NS_IMETHODIMP EventListenerService::HasListenersFor(EventTarget* aEventTarget, const nsAString& aType, bool* aRetVal) { NS_ENSURE_TRUE(aEventTarget, NS_ERROR_UNEXPECTED); @@ -258,54 +243,6 @@ static already_AddRefed<EventListener> ToEventListener( } NS_IMETHODIMP -EventListenerService::AddSystemEventListener(EventTarget* aTarget, - const nsAString& aType, - JS::Handle<JS::Value> aListener, - bool aUseCapture, JSContext* aCx) { - MOZ_ASSERT(aTarget, "Missing target"); - - NS_ENSURE_TRUE(aTarget, NS_ERROR_UNEXPECTED); - - RefPtr<EventListener> listener = ToEventListener(aCx, aListener); - if (!listener) { - return NS_ERROR_UNEXPECTED; - } - - EventListenerManager* manager = aTarget->GetOrCreateListenerManager(); - NS_ENSURE_STATE(manager); - - EventListenerFlags flags = aUseCapture ? TrustedEventsAtSystemGroupCapture() - : TrustedEventsAtSystemGroupBubble(); - manager->AddEventListenerByType(listener, aType, flags); - return NS_OK; -} - -NS_IMETHODIMP -EventListenerService::RemoveSystemEventListener(EventTarget* aTarget, - const nsAString& aType, - JS::Handle<JS::Value> aListener, - bool aUseCapture, - JSContext* aCx) { - MOZ_ASSERT(aTarget, "Missing target"); - - NS_ENSURE_TRUE(aTarget, NS_ERROR_UNEXPECTED); - - RefPtr<EventListener> listener = ToEventListener(aCx, aListener); - if (!listener) { - return NS_ERROR_UNEXPECTED; - } - - EventListenerManager* manager = aTarget->GetExistingListenerManager(); - if (manager) { - EventListenerFlags flags = aUseCapture ? TrustedEventsAtSystemGroupCapture() - : TrustedEventsAtSystemGroupBubble(); - manager->RemoveEventListenerByType(listener, aType, flags); - } - - return NS_OK; -} - -NS_IMETHODIMP EventListenerService::AddListenerForAllEvents( EventTarget* aTarget, JS::Handle<JS::Value> aListener, bool aUseCapture, bool aWantsUntrusted, bool aSystemEventGroup, JSContext* aCx) { diff --git a/dom/events/EventNameList.h b/dom/events/EventNameList.h index 6c9119d5f9..92c76d000e 100644 --- a/dom/events/EventNameList.h +++ b/dom/events/EventNameList.h @@ -228,6 +228,9 @@ EVENT(lostpointercapture, ePointerLostCapture, EventNameType_All, ePointerEventClass) EVENT(selectstart, eSelectStart, EventNameType_HTMLXUL, eBasicEventClass) +EVENT(contextlost, eContextLost, EventNameType_HTML, eBasicEventClass) +EVENT(contextrestored, eContextRestored, EventNameType_HTML, eBasicEventClass) + // Not supported yet; probably never because "wheel" is a better idea. // EVENT(mousewheel) EVENT(pause, ePause, EventNameType_HTML, eBasicEventClass) diff --git a/dom/events/PointerEventHandler.cpp b/dom/events/PointerEventHandler.cpp index 6c537bfb67..ef6f2b12c0 100644 --- a/dom/events/PointerEventHandler.cpp +++ b/dom/events/PointerEventHandler.cpp @@ -610,15 +610,16 @@ EventMessage PointerEventHandler::ToPointerEventMessage( /* static */ void PointerEventHandler::DispatchPointerFromMouseOrTouch( - PresShell* aShell, nsIFrame* aFrame, nsIContent* aContent, - WidgetGUIEvent* aEvent, bool aDontRetargetEvents, nsEventStatus* aStatus, - nsIContent** aTargetContent) { - MOZ_ASSERT(aFrame || aContent); - MOZ_ASSERT(aEvent); + PresShell* aShell, nsIFrame* aEventTargetFrame, + nsIContent* aEventTargetContent, WidgetGUIEvent* aMouseOrTouchEvent, + bool aDontRetargetEvents, nsEventStatus* aStatus, + nsIContent** aMouseOrTouchEventTarget /* = nullptr */) { + MOZ_ASSERT(aEventTargetFrame || aEventTargetContent); + MOZ_ASSERT(aMouseOrTouchEvent); EventMessage pointerMessage = eVoidEvent; - if (aEvent->mClass == eMouseEventClass) { - WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent(); + 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(); @@ -636,7 +637,7 @@ void PointerEventHandler::DispatchPointerFromMouseOrTouch( // 2. We don't synthesize pointer events for those events that are not // dispatched to DOM. if (!mouseEvent->convertToPointer || - !aEvent->IsAllowedToDispatchDOMEvent()) { + !aMouseOrTouchEvent->IsAllowedToDispatchDOMEvent()) { return; } @@ -648,20 +649,20 @@ void PointerEventHandler::DispatchPointerFromMouseOrTouch( InitPointerEventFromMouse(&event, mouseEvent, pointerMessage); event.convertToPointer = mouseEvent->convertToPointer = false; RefPtr<PresShell> shell(aShell); - if (!aFrame) { - shell = PresShell::GetShellForEventTarget(nullptr, aContent); + if (!aEventTargetFrame) { + shell = PresShell::GetShellForEventTarget(nullptr, aEventTargetContent); if (!shell) { return; } } - PreHandlePointerEventsPreventDefault(&event, aEvent); + PreHandlePointerEventsPreventDefault(&event, aMouseOrTouchEvent); // Dispatch pointer event to the same target which is found by the // corresponding mouse event. - shell->HandleEventWithTarget(&event, aFrame, aContent, aStatus, true, - aTargetContent); - PostHandlePointerEventsPreventDefault(&event, aEvent); - } else if (aEvent->mClass == eTouchEventClass) { - WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent(); + shell->HandleEventWithTarget(&event, aEventTargetFrame, aEventTargetContent, + aStatus, true, aMouseOrTouchEventTarget); + PostHandlePointerEventsPreventDefault(&event, aMouseOrTouchEvent); + } else if (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); @@ -681,7 +682,7 @@ void PointerEventHandler::DispatchPointerFromMouseOrTouch( InitPointerEventFromTouch(event, *touchEvent, *touch, i == 0); event.convertToPointer = touch->convertToPointer = false; event.mCoalescedWidgetEvents = touch->mCoalescedWidgetEvents; - if (aEvent->mMessage == eTouchStart) { + 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 = @@ -696,18 +697,22 @@ void PointerEventHandler::DispatchPointerFromMouseOrTouch( continue; } - PreHandlePointerEventsPreventDefault(&event, aEvent); + PreHandlePointerEventsPreventDefault(&event, aMouseOrTouchEvent); shell->HandleEventWithTarget(&event, frame, content, aStatus, true, - nullptr); - PostHandlePointerEventsPreventDefault(&event, aEvent); + 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. - PreHandlePointerEventsPreventDefault(&event, aEvent); - shell->HandleEvent(aFrame, &event, aDontRetargetEvents, aStatus); - PostHandlePointerEventsPreventDefault(&event, aEvent); + // 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); } } } diff --git a/dom/events/PointerEventHandler.h b/dom/events/PointerEventHandler.h index d1a5b31b5a..0211bfe0d8 100644 --- a/dom/events/PointerEventHandler.h +++ b/dom/events/PointerEventHandler.h @@ -168,11 +168,39 @@ class PointerEventHandler final { static void PostHandlePointerEventsPreventDefault( WidgetPointerEvent* aPointerEvent, WidgetGUIEvent* aMouseOrTouchEvent); - MOZ_CAN_RUN_SCRIPT - static void DispatchPointerFromMouseOrTouch( - PresShell* aShell, nsIFrame* aFrame, nsIContent* aContent, - WidgetGUIEvent* aEvent, bool aDontRetargetEvents, nsEventStatus* aStatus, - nsIContent** aTargetContent); + /** + * Dispatch a pointer event for aMouseOrTouchEvent to aEventTargetContent. + * + * @param aShell The PresShell which is handling the event. + * @param aEventTargetFrame The frame for aEventTargetContent. + * @param aEventTargetContent The event target node. + * @param aMouseOrTouchEvent A mouse or touch event. + * @param aDontRetargetEvents If true, this won't dispatch event with + * different PresShell from aShell. Otherwise, + * pointer events may be fired on different + * document if and only if aMouseOrTOuchEvent is a + * touch event except eTouchStart. + * @param aState [out] The result of the pointer event. + * @param aMouseOrTouchEventTarget + * [out] The event target for the following mouse + * or touch event. If aEventTargetContent has not + * been removed from the tree, this is always set + * to it. If aEventTargetContent is removed from + * the tree and aMouseOrTouchEvent is a mouse + * event, this is set to inclusive ancestor of + * aEventTargetContent which is still connected. + * If aEventTargetContent is removed from the tree + * and aMouseOrTouchEvent is a touch event, this is + * set to aEventTargetContent because touch event + * should be dispatched even on disconnected node. + * FIXME: If the event is a touch event but the + * message is not eTouchStart, this won't be set. + */ + MOZ_CAN_RUN_SCRIPT static void DispatchPointerFromMouseOrTouch( + PresShell* aShell, nsIFrame* aEventTargetFrame, + nsIContent* aEventTargetContent, WidgetGUIEvent* aMouseOrTouchEvent, + bool aDontRetargetEvents, nsEventStatus* aStatus, + nsIContent** aMouseOrTouchEventTarget = nullptr); static void InitPointerEventFromMouse(WidgetPointerEvent* aPointerEvent, WidgetMouseEvent* aMouseEvent, diff --git a/dom/events/TouchEvent.cpp b/dom/events/TouchEvent.cpp index 24068703be..6e50c81db8 100644 --- a/dom/events/TouchEvent.cpp +++ b/dom/events/TouchEvent.cpp @@ -233,7 +233,7 @@ bool TouchEvent::PrefEnabled(nsIDocShell* aDocShell) { // The touch screen data seems to be inaccurate in the parent process, // and we really need the crash annotation in child processes. if (firstTime && !XRE_IsParentProcess()) { - CrashReporter::AnnotateCrashReport( + CrashReporter::RecordAnnotationBool( CrashReporter::Annotation::HasDeviceTouchScreen, enabled); firstTime = false; } diff --git a/dom/events/nsIEventListenerService.idl b/dom/events/nsIEventListenerService.idl index ead3cc4af1..b8f36e5d0e 100644 --- a/dom/events/nsIEventListenerService.idl +++ b/dom/events/nsIEventListenerService.idl @@ -75,41 +75,11 @@ interface nsIEventListenerService : nsISupports Array<nsIEventListenerInfo> getListenerInfoFor(in EventTarget aEventTarget); /** - * Returns an array of event targets. - * aEventTarget will be at index 0. - * The objects are the ones that would be used as DOMEvent.currentTarget while - * dispatching an event to aEventTarget - * @note Some events, especially 'load', may actually have a shorter - * event target chain than what this methods returns. - */ - [can_run_script] - Array<EventTarget> getEventTargetChainFor(in EventTarget aEventTarget, - in boolean composed); - - /** * Returns true if a event target has any listener for the given type. */ boolean hasListenersFor(in EventTarget aEventTarget, in AString aType); - /** - * Add a system-group eventlistener to a event target. - */ - [implicit_jscontext] - void addSystemEventListener(in EventTarget target, - in AString type, - in jsval listener, - in boolean useCapture); - - /** - * Remove a system-group eventlistener from a event target. - */ - [implicit_jscontext] - void removeSystemEventListener(in EventTarget target, - in AString type, - in jsval listener, - in boolean useCapture); - [implicit_jscontext] void addListenerForAllEvents(in EventTarget target, in jsval listener, diff --git a/dom/events/test/clipboard/browser_navigator_clipboard_contextmenu_suppression.js b/dom/events/test/clipboard/browser_navigator_clipboard_contextmenu_suppression.js index f504e499c9..97066cd2eb 100644 --- a/dom/events/test/clipboard/browser_navigator_clipboard_contextmenu_suppression.js +++ b/dom/events/test/clipboard/browser_navigator_clipboard_contextmenu_suppression.js @@ -238,16 +238,10 @@ add_task(async function test_context_menu_suppression_image() { await pasteButtonIsShown; info("Test read from same-origin frame before paste contextmenu is closed"); - const clipboarCacheEnabled = SpecialPowers.getBoolPref( - "widget.clipboard.use-cached-data.enabled", - false - ); // If the cached data is used, it uses type order in cached transferable. SimpleTest.isDeeply( await readTypes(browser.browsingContext.children[0]), - clipboarCacheEnabled - ? ["text/plain", "text/html", "image/png"] - : ["text/html", "text/plain", "image/png"], + ["text/html", "text/plain", "image/png"], "read from same-origin should just be resolved without showing paste contextmenu shown" ); @@ -262,3 +256,158 @@ add_task(async function test_context_menu_suppression_image() { ); }); }); + +function testPasteContextMenuSuppressionPasteEvent( + aTriggerPasteFun, + aSuppress, + aMsg +) { + add_task(async function test_context_menu_suppression_paste_event() { + await BrowserTestUtils.withNewTab( + kContentFileUrl, + async function (browser) { + info(`Write data by in cross-origin frame`); + const clipboardText = "X" + Math.random(); + await SpecialPowers.spawn( + browser.browsingContext.children[1], + [clipboardText], + async text => { + content.document.notifyUserGestureActivation(); + return content.eval(`navigator.clipboard.writeText("${text}");`); + } + ); + + info("Test read should show contextmenu"); + let pasteButtonIsShown = waitForPasteContextMenu(); + let readTextRequest = readText(browser); + await pasteButtonIsShown; + + info("Click paste button, request should be resolved"); + await promiseClickPasteButton(); + is(await readTextRequest, clipboardText, "Request should be resolved"); + + info("Test read in paste event handler"); + readTextRequest = SpecialPowers.spawn(browser, [], async () => { + content.document.notifyUserGestureActivation(); + return content.eval(` + (() => { + return new Promise(resolve => { + document.addEventListener("paste", function(e) { + e.preventDefault(); + resolve(navigator.clipboard.readText()); + }, { once: true }); + }); + })(); + `); + }); + + if (aSuppress) { + let listener = function (e) { + if (e.target.getAttribute("id") == kPasteMenuPopupId) { + ok(!aSuppress, "paste contextmenu should not be shown"); + } + }; + document.addEventListener("popupshown", listener); + info(`Trigger paste event by ${aMsg}`); + // trigger paste event + await aTriggerPasteFun(browser); + is( + await readTextRequest, + clipboardText, + "Request should be resolved" + ); + document.removeEventListener("popupshown", listener); + } else { + let pasteButtonIsShown = waitForPasteContextMenu(); + info( + `Trigger paste event by ${aMsg}, read should still show contextmenu` + ); + // trigger paste event + await aTriggerPasteFun(browser); + await pasteButtonIsShown; + + info("Click paste button, request should be resolved"); + await promiseClickPasteButton(); + is( + await readTextRequest, + clipboardText, + "Request should be resolved" + ); + } + + info("Test read should still show contextmenu"); + pasteButtonIsShown = waitForPasteContextMenu(); + readTextRequest = readText(browser); + await pasteButtonIsShown; + + info("Click paste button, request should be resolved"); + await promiseClickPasteButton(); + is(await readTextRequest, clipboardText, "Request should be resolved"); + } + ); + }); +} + +// If platform supports selection clipboard, the middle click paste the content +// from selection clipboard instead, in such case, we don't suppress the +// contextmenu when access global clipboard via async clipboard API. +if ( + !Services.clipboard.isClipboardTypeSupported( + Services.clipboard.kSelectionClipboard + ) +) { + testPasteContextMenuSuppressionPasteEvent( + async browser => { + await SpecialPowers.pushPrefEnv({ + set: [["middlemouse.paste", true]], + }); + + await SpecialPowers.spawn(browser, [], async () => { + EventUtils.synthesizeMouse( + content.document.documentElement, + 1, + 1, + { button: 1 }, + content.window + ); + }); + }, + true, + "middle click" + ); +} + +testPasteContextMenuSuppressionPasteEvent( + async browser => { + await EventUtils.synthesizeAndWaitKey( + "v", + kIsMac ? { accelKey: true } : { ctrlKey: true } + ); + }, + true, + "keyboard shortcut" +); + +testPasteContextMenuSuppressionPasteEvent( + async browser => { + await SpecialPowers.spawn(browser, [], async () => { + return SpecialPowers.doCommand(content.window, "cmd_paste"); + }); + }, + true, + "paste command" +); + +testPasteContextMenuSuppressionPasteEvent( + async browser => { + await SpecialPowers.spawn(browser, [], async () => { + let div = content.document.createElement("div"); + div.setAttribute("contenteditable", "true"); + content.document.documentElement.appendChild(div); + div.focus(); + return SpecialPowers.doCommand(content.window, "cmd_pasteNoFormatting"); + }); + }, + false, + "pasteNoFormatting command" +); diff --git a/dom/events/test/mochitest.toml b/dom/events/test/mochitest.toml index b6b9e58368..53675a8f49 100644 --- a/dom/events/test/mochitest.toml +++ b/dom/events/test/mochitest.toml @@ -472,6 +472,9 @@ skip-if = [ "http2", ] +["test_mouse_events_after_touchend.html"] +skip-if = ["os == 'mac'"] # Bug 1881864 + ["test_mouse_over_at_removing_down_target.html"] ["test_moving_and_expanding_selection_per_page.html"] diff --git a/dom/events/test/pointerevents/mochitest_support_external.js b/dom/events/test/pointerevents/mochitest_support_external.js index 7f22166fdd..7a758a8c5f 100644 --- a/dom/events/test/pointerevents/mochitest_support_external.js +++ b/dom/events/test/pointerevents/mochitest_support_external.js @@ -2,6 +2,8 @@ // to tests on auto MochiTest system with minimum changes. // Author: Maksim Lebedev <alessarik@gmail.com> +/* eslint-disable mozilla/no-comparison-or-assignment-inside-ok */ + // Function allows to prepare our tests after load document addEventListener( "load", diff --git a/dom/events/test/pointerevents/test_bug1315862.html b/dom/events/test/pointerevents/test_bug1315862.html index 92d61a518f..657947c156 100644 --- a/dom/events/test/pointerevents/test_bug1315862.html +++ b/dom/events/test/pointerevents/test_bug1315862.html @@ -36,10 +36,10 @@ function runTests() { let target = iframe.contentDocument.body.firstChild;
allPointerEvents.forEach((event, idx, arr) => {
- SpecialPowers.addSystemEventListener(target, event, () => {
+ SpecialPowers.wrap(target).addEventListener(event, () => {
ok(false, "Shouldn't dispatch " + event + " in the system group");
receivePointerEvents = true;
- });
+ }, { mozSystemGroup: true });
});
target.addEventListener("pointerdown", (e) => {
target.setPointerCapture(e.pointerId);
diff --git a/dom/events/test/test_accesskey.html b/dom/events/test/test_accesskey.html index cdfff54a28..4d382f2270 100644 --- a/dom/events/test/test_accesskey.html +++ b/dom/events/test/test_accesskey.html @@ -143,7 +143,7 @@ add_task(async function modifyAccessKey() { add_task(async function file_picker() { const file = document.getElementById("file"); const MockFilePicker = SpecialPowers.MockFilePicker; - MockFilePicker.init(window); + MockFilePicker.init(SpecialPowers.wrap(window).browsingContext); MockFilePicker.returnValue = MockFilePicker.returnCancel; let clicked = false; diff --git a/dom/events/test/test_bug226361.xhtml b/dom/events/test/test_bug226361.xhtml index 143a485757..c2c19ee2a7 100644 --- a/dom/events/test/test_bug226361.xhtml +++ b/dom/events/test/test_bug226361.xhtml @@ -20,7 +20,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=226361 </p> <div id="content" style="display: none"> - + </div> <pre id="test"> <script type="application/javascript"> @@ -43,8 +43,11 @@ function tab_to(id) { } function tab_iframe() { - doc = document; - tab_to('iframe'); + let canTabMoveFocusToRootElement = !SpecialPowers.getBoolPref("dom.disable_tab_focus_to_root_element"); + if (canTabMoveFocusToRootElement) { + doc = document; + tab_to('iframe'); + } // inside iframe doc = document.getElementById('iframe').contentDocument diff --git a/dom/events/test/test_bug238987.html b/dom/events/test/test_bug238987.html index b2712a25d0..34f870eeac 100644 --- a/dom/events/test/test_bug238987.html +++ b/dom/events/test/test_bug238987.html @@ -22,7 +22,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=238987 var shouldStop = false; var activateShift = false; - var expectedResult = "i1,i2,i3,i4,i5,i6,i7,i8,number,i9,i10,i11,i12"; + var expectedResult = "i1,i2,i3,i4,i5,i7,i8,number,i9,i10,i11,i12"; var forwardFocusArray = expectedResult.split(","); var backwardFocusArray = expectedResult.split(","); var forwardBlurArray = expectedResult.split(","); diff --git a/dom/events/test/test_bug336682.js b/dom/events/test/test_bug336682.js index e673f1eb99..11bd7d46bb 100644 --- a/dom/events/test/test_bug336682.js +++ b/dom/events/test/test_bug336682.js @@ -4,6 +4,8 @@ * Any copyright is dedicated to the Public Domain. * http://creativecommons.org/licenses/publicdomain/ */ +/* eslint-disable mozilla/no-comparison-or-assignment-inside-ok */ + var gState = 0; /** * After all the on/offline handlers run, diff --git a/dom/events/test/test_bug448602.html b/dom/events/test/test_bug448602.html index 18b4cb2d2f..452a361a52 100644 --- a/dom/events/test/test_bug448602.html +++ b/dom/events/test/test_bug448602.html @@ -174,28 +174,7 @@ function runTests() { is(SpecialPowers.unwrap(infos[0].listenerObject), l, "Should have the right listener object (4)"); - // Event target chain tests l3 = document.getElementById("testlevel3"); - var textnode = l3.firstChild; - var chain = els.getEventTargetChainFor(textnode, true); - ok(chain.length > 3, "Too short event target chain."); - ok(SpecialPowers.compare(chain[0], textnode), "Wrong chain item (1)"); - ok(SpecialPowers.compare(chain[1], l3), "Wrong chain item (2)"); - ok(SpecialPowers.compare(chain[2], l2), "Wrong chain item (3)"); - ok(SpecialPowers.compare(chain[3], root), "Wrong chain item (4)"); - - var hasDocumentInChain = false; - var hasWindowInChain = false; - for (var i = 0; i < chain.length; ++i) { - if (SpecialPowers.compare(chain[i], document)) { - hasDocumentInChain = true; - } else if (SpecialPowers.compare(chain[i], window)) { - hasWindowInChain = true; - } - } - - ok(hasDocumentInChain, "Should have document in event target chain!"); - ok(hasWindowInChain, "Should have window in event target chain!"); try { els.getListenerInfoFor(null); diff --git a/dom/events/test/test_mouse_events_after_touchend.html b/dom/events/test/test_mouse_events_after_touchend.html new file mode 100644 index 0000000000..146c37e489 --- /dev/null +++ b/dom/events/test/test_mouse_events_after_touchend.html @@ -0,0 +1,232 @@ +<!doctype html> +<html> +<head> +<meta charset="utf-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<title>Tests for mouse events after touchend</title> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<script src="/tests/SimpleTest/EventUtils.js"></script> +<script src="/tests/SimpleTest/paint_listener.js"></script> +<script src="/tests/gfx/layers/apz/test/mochitest/apz_test_utils.js"></script> +<script src="/tests/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js"></script> +<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +<style> +#parent, #child { + width: 300px; + height: 64px; + padding: 16px; +} +#parent { + background-color: black; +} +#child { + background-color: gray; +} +</style> +<script> +"use strict"; + +SimpleTest.waitForExplicitFinish(); +SimpleTest.requestFlakyTimeout("Required for waiting to prevent double tap at second tap"); +SimpleTest.waitForFocus(async () => { + function stringifyEvent(event) { + return `{ type: ${event.type}, target: ${ + event.target.id || event.target.nodeName + }${ + event.detail !== undefined ? `, detail: ${event.detail}` : "" + }${ + event.button !== undefined ? `, button: ${event.button}` : "" + }${ + event.buttons !== undefined ? `, buttons: ${event.buttons}` : "" + } }`; + } + function stringifyEvents(arrayOfEvents) { + if (!arrayOfEvents.length) { + return "[]"; + } + let ret = ""; + for (const event of arrayOfEvents) { + if (ret === "") { + ret = "[ "; + } else { + ret += ", "; + } + ret += stringifyEvent(event); + } + return ret + " ]"; + } + + let events = []; + for (const type of ["mousemove", + "mousedown", + "mouseup", + "click", + "dblclick", + "contextmenu", + "touchend"]) { + if (type == "touchend") { + addEventListener(type, event => { + info(`Received: ${stringifyEvent(event)}`); + events.push({type, target: event.target}); + }, {capture: true}); + } else { + addEventListener(type, event => { + info(`Received: ${stringifyEvent(event)}`); + events.push({ + type: event.type, + target: event.target, + detail: event.detail, + button: event.button, + buttons: event.buttons, + }); + }, {capture: true}); + } + } + + function shiftEventsBefore(arrayOfEvents, aType) { + const index = arrayOfEvents.findIndex(event => event.type == aType); + if (index <= 0) { + return []; + } + let ret = []; + for (let i = 0; i < index; i++) { + ret.push(arrayOfEvents.shift()); + } + return ret; + } + + const parent = document.getElementById("parent"); + const child = document.getElementById("child"); + + function promiseEvent(aType) { + return new Promise(resolve => + addEventListener(aType, resolve, {once: true}) + ); + } + + async function promiseFlushingAPZGestureState() { + await promiseApzFlushedRepaints(); + // Wait for a while to avoid that the next tap will be treated as 2nd tap of + // a double tap. + return new Promise( + resolve => setTimeout( + resolve, + // NOTE: x1.0 is not enough to avoid intermittent failures. + SpecialPowers.getIntPref("apz.max_tap_time") * 1.2 + ) + ); + } + + await waitUntilApzStable(); + for (const prefValue of [true, false]) { + await SpecialPowers.pushPrefEnv({ + set: [ + ["test.events.async.enabled", prefValue], + ["ui.click_hold_context_menus.delay", 15000], // disable long tap + ] + }); + const desc = `(test.events.async.enabled=${prefValue})`; + + await (async function test_single_tap() { + await promiseFlushingAPZGestureState(); + info("test_single_tap: testing..."); + events = []; + const waitForClick = promiseEvent("click"); + synthesizeTouch(child, 5, 5); + await waitForClick; + is( + stringifyEvents(events), + stringifyEvents([ + { type: "touchend", target: child }, + { type: "mousemove", target: child, detail: 0, button: 0, buttons: 0 }, + { type: "mousedown", target: child, detail: 1, button: 0, buttons: 1 }, + { type: "mouseup", target: child, detail: 1, button: 0, buttons: 0 }, + { type: "click", target: child, detail: 1, button: 0, buttons: 0 }, + ]), + `Single tap should cause a click ${desc}` + ); + })(); + + await (async function test_single_tap_with_consuming_touchstart() { + await promiseFlushingAPZGestureState(); + info("test_single_tap_with_consuming_touchstart: testing..."); + events = []; + const waitForTouchEnd = promiseEvent("touchend"); + child.addEventListener("touchstart", event => { + event.preventDefault(); + }, {once: true}); + synthesizeTouch(child, 5, 5); + await waitForTouchEnd; + const result = stringifyEvents(events); + const expected = stringifyEvents([{ type: "touchend", target: child }]); + // If testing this with APZ, the result is really unstable. Let's allow to + // fail for now. + (prefValue && result != expected ? todo_is : is)( + result, + expected, + `Single tap should not cause mouse events if touchstart is consumed ${desc}` + ); + })(); + + + await (async function test_single_tap_with_consuming_touchend() { + await promiseFlushingAPZGestureState(); + info("test_single_tap_with_consuming_touchend: testing..."); + events = []; + const waitForTouchEnd = promiseEvent("touchend"); + child.addEventListener("touchend", event => { + event.preventDefault(); + }, {once: true}); + synthesizeTouch(child, 5, 5); + await waitForTouchEnd; + is( + stringifyEvents(shiftEventsBefore(events)), + stringifyEvents([]), + `test_single_tap_with_consuming_touchstart() shouldn't cause mouse events after touchend` + ) + is( + stringifyEvents(events), + stringifyEvents([ + { type: "touchend", target: child }, + ]), + `Single tap should not cause mouse events if touchend is consumed ${desc}` + ); + })(); + + await (async function test_multi_touch() { + await promiseFlushingAPZGestureState(); + events = []; + info("test_multi_touch: testing..."); + const waitForTouchEnd = new Promise(resolve => { + let count = 0; + function onTouchEnd(event) { + if (++count == 2) { + removeEventListener("touchend", onTouchEnd, {capture: true}); + requestAnimationFrame(() => requestAnimationFrame(resolve)); + } + } + addEventListener("touchend", onTouchEnd, {capture: true}); + }); + synthesizeTouch(child, [5, 25], 5); + await waitForTouchEnd; + is( + stringifyEvents(shiftEventsBefore(events)), + stringifyEvents([]), + `test_single_tap_with_consuming_touchend() shouldn't cause mouse events after touchend` + ) + is( + stringifyEvents(events), + stringifyEvents([ + { type: "touchend", target: child }, + { type: "touchend", target: child }, + ]), + `Multiple touch should not cause mouse events ${desc}` + ); + })(); + } + SimpleTest.finish(); +}); +</script> +</head> +<body><div id="parent"><div id="child"></div></div></body> +</html> diff --git a/dom/events/test/test_use_split_keypress_event_model_on_old_Confluence.html b/dom/events/test/test_use_split_keypress_event_model_on_old_Confluence.html index 3898584b0a..43d8081606 100644 --- a/dom/events/test/test_use_split_keypress_event_model_on_old_Confluence.html +++ b/dom/events/test/test_use_split_keypress_event_model_on_old_Confluence.html @@ -39,7 +39,7 @@ SimpleTest.waitForExplicitFinish(); SimpleTest.waitForFocus(async function doTests() { let iframe = document.getElementById("iframe"); let waitForCheckKeyPressEventModelEvent = new Promise(resolve => { - SpecialPowers.addSystemEventListener(iframe.contentDocument, "CheckKeyPressEventModel", resolve, {once: true}); + SpecialPowers.wrap(iframe.contentDocument).addEventListener("CheckKeyPressEventModel", resolve, {mozSystemGroup: true, once: true}); }); iframe.contentDocument.body.setAttribute("contenteditable", "true"); await waitForCheckKeyPressEventModelEvent; diff --git a/dom/events/test/test_use_split_keypress_event_model_on_old_Office_Online_Server.html b/dom/events/test/test_use_split_keypress_event_model_on_old_Office_Online_Server.html index 68f52be41d..02047fef90 100644 --- a/dom/events/test/test_use_split_keypress_event_model_on_old_Office_Online_Server.html +++ b/dom/events/test/test_use_split_keypress_event_model_on_old_Office_Online_Server.html @@ -23,7 +23,7 @@ function srcdocLoaded() { waitForCheckKeyPressEventModelEvent = new Promise(resolve => { dump(document.querySelector("iframe").contentDocument.location + "\n"); var doc = document.querySelector("iframe").contentDocument; - SpecialPowers.addSystemEventListener(doc, "CheckKeyPressEventModel", resolve, {once: true}); + SpecialPowers.wrap(doc).addEventListener("CheckKeyPressEventModel", resolve, {mozSystemGroup: true, once: true}); doc.getElementById("WACViewPanel_EditingElement").contentEditable = "true"; }); } |