Bug 1898343 - Don't fire selectionchange if there is one pending, r=masayuki

Differential Revision: https://phabricator.services.mozilla.com/D211263
This commit is contained in:
Olli Pettay 2024-05-27 12:47:53 +00:00
parent 977c6db99e
commit c9f32debe1
10 changed files with 50 additions and 59 deletions

View file

@ -23,9 +23,7 @@ async function testFontHighlighting(view) {
// The number of window selection change events we expect to get as we hover over each
// font in the list. Waiting for those events is how we know that text-runs were
// highlighted in the page.
// The reason why these numbers vary is because the highlighter may create more than
// 1 selection range object, depending on the number of text-runs found.
const expectedSelectionChangeEvents = [2, 2, 2, 1, 1];
const expectedSelectionChangeEvents = [1, 1, 1, 1, 1];
const viewDoc = view.document;

View file

@ -164,12 +164,21 @@ void SelectionChangeEventDispatcher::OnSelectionChange(Document* aDoc,
nsINode* target =
textControl ? static_cast<nsINode*>(textControl.get()) : aDoc;
if (target) {
CanBubble canBubble = textControl ? CanBubble::eYes : CanBubble::eNo;
RefPtr<AsyncEventDispatcher> asyncDispatcher =
new AsyncEventDispatcher(target, eSelectionChange, canBubble);
asyncDispatcher->PostDOMEvent();
if (!target) {
return;
}
if (target->HasScheduledSelectionChangeEvent()) {
return;
}
target->SetHasScheduledSelectionChangeEvent();
CanBubble canBubble = textControl ? CanBubble::eYes : CanBubble::eNo;
RefPtr<AsyncEventDispatcher> asyncDispatcher =
new AsyncSelectionChangeEventDispatcher(target, eSelectionChange,
canBubble);
asyncDispatcher->PostDOMEvent();
}
} // namespace mozilla

View file

@ -164,8 +164,10 @@ enum : uint32_t {
NODE_MAY_HAVE_ELEMENT_CHILDREN = NODE_FLAG_BIT(12),
NODE_HAS_SCHEDULED_SELECTION_CHANGE_EVENT = NODE_FLAG_BIT(13),
// Remaining bits are node type specific.
NODE_TYPE_SPECIFIC_BITS_OFFSET = 13
NODE_TYPE_SPECIFIC_BITS_OFFSET = 14
};
// Flags for selectors that persist to the DOM node.
@ -1642,6 +1644,18 @@ class nsINode : public mozilla::dom::EventTarget {
MOZ_CAN_RUN_SCRIPT nsIContent* GetSelectionRootContent(
mozilla::PresShell* aPresShell, bool aAllowCrossShadowBoundary = false);
bool HasScheduledSelectionChangeEvent() {
return HasFlag(NODE_HAS_SCHEDULED_SELECTION_CHANGE_EVENT);
}
void SetHasScheduledSelectionChangeEvent() {
SetFlags(NODE_HAS_SCHEDULED_SELECTION_CHANGE_EVENT);
}
void ClearHasScheduledSelectionChangeEvent() {
UnsetFlags(NODE_HAS_SCHEDULED_SELECTION_CHANGE_EVENT);
}
nsINodeList* ChildNodes();
nsIContent* GetFirstChild() const { return mFirstChild; }

View file

@ -205,6 +205,19 @@ class LoadBlockingAsyncEventDispatcher final : public AsyncEventDispatcher {
RefPtr<dom::Document> mBlockedDoc;
};
class AsyncSelectionChangeEventDispatcher : public AsyncEventDispatcher {
public:
AsyncSelectionChangeEventDispatcher(dom::EventTarget* aTarget,
EventMessage aEventMessage,
CanBubble aCanBubble)
: AsyncEventDispatcher(aTarget, aEventMessage, aCanBubble) {}
MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD Run() override {
mTarget->GetAsNode()->ClearHasScheduledSelectionChangeEvent();
return AsyncEventDispatcher::Run();
}
};
} // namespace mozilla
#endif // mozilla_AsyncEventDispatcher_h_

View file

@ -2116,8 +2116,10 @@ void TextControlState::SetSelectionRange(uint32_t aStart, uint32_t aEnd,
// XXX(krosylight): Shouldn't it fire before select event?
// Currently Gecko and Blink both fire selectionchange after select.
if (IsSelectionCached() &&
StaticPrefs::dom_select_events_textcontrols_selectionchange_enabled()) {
asyncDispatcher = new AsyncEventDispatcher(
StaticPrefs::dom_select_events_textcontrols_selectionchange_enabled() &&
!mTextCtrlElement->HasScheduledSelectionChangeEvent()) {
mTextCtrlElement->SetHasScheduledSelectionChangeEvent();
asyncDispatcher = new AsyncSelectionChangeEventDispatcher(
mTextCtrlElement, eSelectionChange, CanBubble::eYes);
asyncDispatcher->PostDOMEvent();
}

View file

@ -645,14 +645,7 @@
{ x: 50 },
"mousedown",
{
// XXX Bug 1721287: For some reason we fire 2 selectchange events
// on the body when switching from the <input> to the <textarea>.
selectionchangeOnDocument:
document.activeElement != textControl &&
(document.activeElement.tagName.toLocaleLowerCase() == "input" ||
document.activeElement.tagName.toLocaleLowerCase() == "textarea")
? 2
: 1,
selectionchangeOnDocument: 1,
}
);

View file

@ -12,8 +12,8 @@
</div>
<div id="editor" contenteditable></div>
<div id="clickaway" style="width: 3px; height: 3px;"></div>
<div id="editor" contenteditable></div><br>
<div id="clickaway" style="width: 30px; height: 30px; border: 3px solid red;"></div>
<img src="green.png"><!-- for ensuring to load the image at first test of <img> case -->
<pre id="test">

View file

@ -1,6 +0,0 @@
[onselectionchange-on-distinct-text-controls.html]
[selectionchange event on each input element fires independently]
expected: FAIL
[selectionchange event on each textarea element fires independently]
expected: FAIL

View file

@ -1,6 +0,0 @@
[onselectionchange-on-document.html]
[task to fire selectionchange event gets queued each time selection is mutated]
expected: FAIL
[has scheduled selectionchange event is set to false at the beginning of a task to fire selectionchange event]
expected: FAIL

View file

@ -1,26 +0,0 @@
[selectionchange.html]
[Calling setRangeText() after select() on the input element]
expected:
if (os == "android") and not debug: [PASS, FAIL]
FAIL
[Calling setRangeText() repeatedly on the input element]
expected: FAIL
[Calling setRangeText() after select() on the disconnected input element]
expected: FAIL
[Calling setRangeText() repeatedly on the disconnected input element]
expected: FAIL
[Calling setRangeText() after select() on the textarea element]
expected: FAIL
[Calling setRangeText() repeatedly on the textarea element]
expected: FAIL
[Calling setRangeText() after select() on the disconnected textarea element]
expected: FAIL
[Calling setRangeText() repeatedly on the disconnected textarea element]
expected: FAIL