From fbaf0bb26397aa498eb9156f06d5a6fe34dd7dd8 Mon Sep 17 00:00:00 2001
From: Daniel Baumann
Date: Fri, 19 Apr 2024 03:14:29 +0200
Subject: Merging upstream version 125.0.1.
Signed-off-by: Daniel Baumann
---
dom/events/Clipboard.cpp | 84 ++++++--
dom/events/Clipboard.h | 7 +-
dom/events/DataTransfer.cpp | 114 ++++++----
dom/events/DataTransfer.h | 25 ++-
dom/events/EventListenerService.cpp | 63 ------
dom/events/EventNameList.h | 3 +
dom/events/PointerEventHandler.cpp | 51 +++--
dom/events/PointerEventHandler.h | 38 +++-
dom/events/TouchEvent.cpp | 2 +-
dom/events/nsIEventListenerService.idl | 30 ---
..._navigator_clipboard_contextmenu_suppression.js | 163 ++++++++++++++-
dom/events/test/mochitest.toml | 3 +
.../pointerevents/mochitest_support_external.js | 2 +
dom/events/test/pointerevents/test_bug1315862.html | 4 +-
dom/events/test/test_accesskey.html | 2 +-
dom/events/test/test_bug226361.xhtml | 9 +-
dom/events/test/test_bug238987.html | 2 +-
dom/events/test/test_bug336682.js | 2 +
dom/events/test/test_bug448602.html | 21 --
.../test/test_mouse_events_after_touchend.html | 232 +++++++++++++++++++++
...lit_keypress_event_model_on_old_Confluence.html | 2 +-
...ss_event_model_on_old_Office_Online_Server.html | 2 +-
22 files changed, 636 insertions(+), 225 deletions(-)
create mode 100644 dom/events/test/test_mouse_events_after_touchend.html
(limited to 'dom/events')
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 mPromise;
};
+static nsTArray 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{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, 3> entries;
- for (const auto& format : flavorList) {
- auto entry = MakeRefPtr(
- 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(
+ mGlobal, NS_ConvertUTF8toUTF16(format));
+ entry->LoadDataFromSystemClipboard(aAsyncGetClipboardData);
+ entries.AppendElement(std::move(entry));
+ }
}
RefPtr 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 callback;
+ switch (aType) {
+ case ReadRequestType::eRead: {
+ callback =
+ MakeRefPtr(aOwner.AsGlobal(), &aPromise);
+ break;
+ }
+ case ReadRequestType::eReadText: {
+ callback = MakeRefPtr(&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(global, std::move(p));
rv = clipboardService->AsyncGetData(
- // Mandatory data types defined in
- // https://w3c.github.io/clipboard-apis/#mandatory-data-types-x
- AutoTArray{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(std::move(p));
rv = clipboardService->AsyncGetData(
- AutoTArray{nsDependentCString(kTextMime)},
+ AutoTArray{nsLiteralCString(kTextMime)},
nsIClipboard::kGlobalClipboard, owner->GetWindowContext(),
&aPrincipal, callback);
break;
@@ -288,6 +328,24 @@ already_AddRefed 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 =
+ nsGlobalWindowInner::Cast(owner)->GetCurrentPasteDataTransfer()) {
+ // If there is valid nsIAsyncGetClipboardData, use it directly.
+ if (nsCOMPtr 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::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* 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& aResult) {
// NOTE: When you change this method, you may need to change
// GetExternalTransferableFormats() too since those methods should
// work similarly.
+ MOZ_ASSERT(!mAsyncGetClipboardData);
+
+ RefPtr wc = GetWindowContext();
+ if (NS_WARN_IF(!wc)) {
+ MOZ_ASSERT_UNREACHABLE(
+ "How could this DataTransfer be created with a non-window global?");
+ return;
+ }
+
nsCOMPtr clipboard =
do_GetService("@mozilla.org/widget/clipboard;1");
- if (!clipboard || aWhichClipboard < 0) {
+ if (!clipboard || mClipboardType < 0) {
return;
}
+ nsresult rv = NS_ERROR_FAILURE;
+ nsCOMPtr asyncGetClipboardData;
if (aPlainTextOnly) {
- bool hasType;
- AutoTArray textMime = {nsDependentCString(kTextMime)};
- nsresult rv =
- clipboard->HasDataMatchingFlavors(textMime, aWhichClipboard, &hasType);
- NS_SUCCEEDED(rv);
- if (hasType) {
- aResult->AppendElement(kTextMime);
- }
+ rv = clipboard->GetDataSnapshotSync(
+ AutoTArray{nsLiteralCString(kTextMime)}, mClipboardType,
+ wc, getter_AddRefs(asyncGetClipboardData));
+ } else {
+ AutoTArray formats;
+ formats.AppendElements(Span(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 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 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 DataTransfer::GetGlobal() const {
return global.forget();
}
+already_AddRefed DataTransfer::GetWindowContext() const {
+ nsCOMPtr 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 sysPrincipal = nsContentUtils::GetSystemPrincipal();
-
nsTArray 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* 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 GetGlobal() const;
+ already_AddRefed 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& 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 mAsyncGetClipboardData;
+
// The items contained with the DataTransfer
RefPtr 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
@@ -219,21 +219,6 @@ EventListenerService::GetListenerInfoFor(
return NS_OK;
}
-NS_IMETHODIMP
-EventListenerService::GetEventTargetChainFor(
- EventTarget* aEventTarget, bool aComposed,
- nsTArray>& aOutArray) {
- NS_ENSURE_ARG(aEventTarget);
- WidgetEvent event(true, eVoidEvent);
- event.SetComposed(aComposed);
- nsTArray 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) {
@@ -257,54 +242,6 @@ static already_AddRefed ToEventListener(
return listener.forget();
}
-NS_IMETHODIMP
-EventListenerService::AddSystemEventListener(EventTarget* aTarget,
- const nsAString& aType,
- JS::Handle aListener,
- bool aUseCapture, JSContext* aCx) {
- MOZ_ASSERT(aTarget, "Missing target");
-
- NS_ENSURE_TRUE(aTarget, NS_ERROR_UNEXPECTED);
-
- RefPtr 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 aListener,
- bool aUseCapture,
- JSContext* aCx) {
- MOZ_ASSERT(aTarget, "Missing target");
-
- NS_ENSURE_TRUE(aTarget, NS_ERROR_UNEXPECTED);
-
- RefPtr 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 aListener, bool aUseCapture,
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 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 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
@@ -74,42 +74,12 @@ interface nsIEventListenerService : nsISupports
*/
Array 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 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
+/* 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
-
+
+
+
+
+
+
+
+
+
+
+