diff --git a/dom/webidl/CaretStateChangedEvent.webidl b/dom/webidl/CaretStateChangedEvent.webidl index 5fdb9f34b3cb..0a661fe73733 100644 --- a/dom/webidl/CaretStateChangedEvent.webidl +++ b/dom/webidl/CaretStateChangedEvent.webidl @@ -11,7 +11,8 @@ enum CaretChangedReason { "taponcaret", "presscaret", "releasecaret", - "scroll" + "scroll", + "dragcaret" }; dictionary CaretStateChangedEventInit : EventInit { @@ -23,6 +24,8 @@ dictionary CaretStateChangedEventInit : EventInit { boolean selectionVisible = false; boolean selectionEditable = false; DOMString selectedTextContent = ""; + long clientX = 0; + long clientY = 0; }; [ChromeOnly, @@ -40,4 +43,8 @@ interface CaretStateChangedEvent : Event { readonly attribute boolean selectionVisible; readonly attribute boolean selectionEditable; readonly attribute DOMString selectedTextContent; + [Pref="layout.accessiblecaret.magnifier.enabled"] + readonly attribute long clientX; + [Pref="layout.accessiblecaret.magnifier.enabled"] + readonly attribute long clientY; }; diff --git a/layout/base/AccessibleCaretManager.cpp b/layout/base/AccessibleCaretManager.cpp index 918ee8e518c3..0d1875333a44 100644 --- a/layout/base/AccessibleCaretManager.cpp +++ b/layout/base/AccessibleCaretManager.cpp @@ -497,7 +497,7 @@ nsresult AccessibleCaretManager::PressCaret(const nsPoint& aPoint, mOffsetYToCaretLogicalPosition = mActiveCaret->LogicalPosition().y - aPoint.y; SetSelectionDragState(true); - DispatchCaretStateChangedEvent(CaretChangedReason::Presscaret); + DispatchCaretStateChangedEvent(CaretChangedReason::Presscaret, &aPoint); rv = NS_OK; } @@ -518,6 +518,10 @@ nsresult AccessibleCaretManager::DragCaret(const nsPoint& aPoint) { // We want to scroll the page even if we failed to drag the caret. StartSelectionAutoScrollTimer(aPoint); UpdateCarets(); + + if (StaticPrefs::layout_accessiblecaret_magnifier_enabled()) { + DispatchCaretStateChangedEvent(CaretChangedReason::Dragcaret, &aPoint); + } return NS_OK; } @@ -537,7 +541,7 @@ nsresult AccessibleCaretManager::TapCaret(const nsPoint& aPoint) { nsresult rv = NS_ERROR_FAILURE; if (GetCaretMode() == CaretMode::Cursor) { - DispatchCaretStateChangedEvent(CaretChangedReason::Taponcaret); + DispatchCaretStateChangedEvent(CaretChangedReason::Taponcaret, &aPoint); rv = NS_OK; } @@ -1427,7 +1431,7 @@ void AccessibleCaretManager::StopSelectionAutoScrollTimer() const { } void AccessibleCaretManager::DispatchCaretStateChangedEvent( - CaretChangedReason aReason) { + CaretChangedReason aReason, const nsPoint* aPoint) { if (MaybeFlushLayout() == Terminated::Yes) { return; } @@ -1488,6 +1492,12 @@ void AccessibleCaretManager::DispatchCaretStateChangedEvent( init.mCaretVisuallyVisible = mCarets.HasVisuallyVisibleCaret(); init.mSelectedTextContent = StringifiedSelection(); + if (aPoint) { + CSSIntPoint pt = CSSPixel::FromAppUnitsRounded(*aPoint); + init.mClientX = pt.x; + init.mClientY = pt.y; + } + RefPtr event = CaretStateChangedEvent::Constructor( doc, u"mozcaretstatechanged"_ns, init); diff --git a/layout/base/AccessibleCaretManager.h b/layout/base/AccessibleCaretManager.h index 049c4a44be63..909aeb883895 100644 --- a/layout/base/AccessibleCaretManager.h +++ b/layout/base/AccessibleCaretManager.h @@ -304,8 +304,11 @@ class AccessibleCaretManager { // This function will flush layout, so caller must ensure the PresShell is // still valid after calling this method. + // @param aPoint The event point when the user is pressing or dragging a + // caret, which is relative to the root frame. MOZ_CAN_RUN_SCRIPT - virtual void DispatchCaretStateChangedEvent(dom::CaretChangedReason aReason); + virtual void DispatchCaretStateChangedEvent(dom::CaretChangedReason aReason, + const nsPoint* aPoint = nullptr); // --------------------------------------------------------------------------- // Member variables diff --git a/layout/base/gtest/TestAccessibleCaretManager.cpp b/layout/base/gtest/TestAccessibleCaretManager.cpp index d23558a18294..464ad68fb043 100644 --- a/layout/base/gtest/TestAccessibleCaretManager.cpp +++ b/layout/base/gtest/TestAccessibleCaretManager.cpp @@ -105,8 +105,8 @@ class AccessibleCaretManagerTester : public ::testing::Test { Terminated MaybeFlushLayout() override { return Terminated::No; } MOCK_CONST_METHOD0(GetCaretMode, CaretMode()); - MOCK_METHOD1(DispatchCaretStateChangedEvent, - void(CaretChangedReason aReason)); + MOCK_METHOD2(DispatchCaretStateChangedEvent, + void(CaretChangedReason aReason, const nsPoint* aPoint)); MOCK_CONST_METHOD1(HasNonEmptyTextContent, bool(nsINode* aNode)); }; // class MockAccessibleCaretManager @@ -148,7 +148,7 @@ MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION { .WillRepeatedly(Return(CaretMode::Selection)); EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( - CaretChangedReason::Updateposition)) + CaretChangedReason::Updateposition, nullptr)) .Times(3); mManager.UpdateCarets(); @@ -176,33 +176,33 @@ MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION { InSequence dummy; EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( - CaretChangedReason::Updateposition)) + CaretChangedReason::Updateposition, nullptr)) .Times(1); EXPECT_CALL(check, Call("update")); EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( - CaretChangedReason::Visibilitychange)) + CaretChangedReason::Visibilitychange, nullptr)) .Times(1); EXPECT_CALL(check, Call("mouse down")); - EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(_)).Times(0); + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(_, nullptr)).Times(0); EXPECT_CALL(check, Call("reflow")); - EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(_)).Times(0); + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(_, nullptr)).Times(0); EXPECT_CALL(check, Call("blur")); EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( - CaretChangedReason::Updateposition)) + CaretChangedReason::Updateposition, nullptr)) .Times(1); EXPECT_CALL(check, Call("mouse up")); EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( - CaretChangedReason::Updateposition)) + CaretChangedReason::Updateposition, nullptr)) .Times(1); EXPECT_CALL(check, Call("reflow2")); EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( - CaretChangedReason::Updateposition)) + CaretChangedReason::Updateposition, nullptr)) .Times(1); } @@ -256,33 +256,33 @@ MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION { InSequence dummy; EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( - CaretChangedReason::Updateposition)) + CaretChangedReason::Updateposition, nullptr)) .Times(1); EXPECT_CALL(check, Call("update")); EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( - CaretChangedReason::Visibilitychange)) + CaretChangedReason::Visibilitychange, nullptr)) .Times(1); EXPECT_CALL(check, Call("mouse down")); - EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(_)).Times(0); + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(_, nullptr)).Times(0); EXPECT_CALL(check, Call("reflow")); - EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(_)).Times(0); + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(_, nullptr)).Times(0); EXPECT_CALL(check, Call("blur")); EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( - CaretChangedReason::Updateposition)) + CaretChangedReason::Updateposition, nullptr)) .Times(1); EXPECT_CALL(check, Call("mouse up")); EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( - CaretChangedReason::Updateposition)) + CaretChangedReason::Updateposition, nullptr)) .Times(1); EXPECT_CALL(check, Call("reflow2")); EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( - CaretChangedReason::Updateposition)) + CaretChangedReason::Updateposition, nullptr)) .Times(1); } @@ -330,18 +330,18 @@ MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION { InSequence dummy; EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( - CaretChangedReason::Updateposition)) + CaretChangedReason::Updateposition, nullptr)) .Times(1); EXPECT_CALL(check, Call("update")); EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( - CaretChangedReason::Visibilitychange)) + CaretChangedReason::Visibilitychange, nullptr)) .Times(1); EXPECT_CALL(check, Call("keyboard")); // No CaretStateChanged events should be dispatched since the caret has // being hidden in cursor mode. - EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(_)).Times(0); + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(_, nullptr)).Times(0); } // Simulate typing the end of the input. @@ -378,15 +378,15 @@ MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION { .WillOnce(Return(PositionChangedResult::Invisible)); EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( - CaretChangedReason::Updateposition)); + CaretChangedReason::Updateposition, nullptr)); EXPECT_CALL(check, Call("updatecarets")); - EXPECT_CALL(mManager, - DispatchCaretStateChangedEvent(CaretChangedReason::Scroll)); + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Scroll, nullptr)); EXPECT_CALL(check, Call("scrollstart1")); EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( - CaretChangedReason::Updateposition)); + CaretChangedReason::Updateposition, nullptr)); EXPECT_CALL(check, Call("reflow1")); // After scroll ended, first caret is visible and second caret is out of @@ -395,20 +395,20 @@ MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION { .WillOnce(Return(PositionChangedResult::Invisible)); EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( - CaretChangedReason::Updateposition)); + CaretChangedReason::Updateposition, nullptr)); EXPECT_CALL(check, Call("scrollend1")); - EXPECT_CALL(mManager, - DispatchCaretStateChangedEvent(CaretChangedReason::Scroll)); + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Scroll, nullptr)); EXPECT_CALL(check, Call("scrollstart2")); EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( - CaretChangedReason::Updateposition)); + CaretChangedReason::Updateposition, nullptr)); EXPECT_CALL(check, Call("reflow2")); // After the scroll ended, both carets are visible. EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( - CaretChangedReason::Updateposition)); + CaretChangedReason::Updateposition, nullptr)); EXPECT_CALL(check, Call("scrollend2")); } @@ -466,18 +466,18 @@ MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION { .WillOnce(Return(PositionChangedResult::Invisible)); EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( - CaretChangedReason::Updateposition)); + CaretChangedReason::Updateposition, nullptr)); EXPECT_CALL(check, Call("updatecarets")); - EXPECT_CALL(mManager, - DispatchCaretStateChangedEvent(CaretChangedReason::Scroll)); + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Scroll, nullptr)); EXPECT_CALL(check, Call("scrollstart1")); - EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(_)).Times(0); + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(_, nullptr)).Times(0); EXPECT_CALL(check, Call("scrollPositionChanged1")); EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( - CaretChangedReason::Updateposition)); + CaretChangedReason::Updateposition, nullptr)); EXPECT_CALL(check, Call("reflow1")); // After scroll ended, first caret is visible and second caret is out of @@ -486,23 +486,23 @@ MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION { .WillOnce(Return(PositionChangedResult::Invisible)); EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( - CaretChangedReason::Updateposition)); + CaretChangedReason::Updateposition, nullptr)); EXPECT_CALL(check, Call("scrollend1")); - EXPECT_CALL(mManager, - DispatchCaretStateChangedEvent(CaretChangedReason::Scroll)); + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Scroll, nullptr)); EXPECT_CALL(check, Call("scrollstart2")); - EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(_)).Times(0); + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(_, nullptr)).Times(0); EXPECT_CALL(check, Call("scrollPositionChanged2")); EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( - CaretChangedReason::Updateposition)); + CaretChangedReason::Updateposition, nullptr)); EXPECT_CALL(check, Call("reflow2")); // After the scroll ended, both carets are visible. EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( - CaretChangedReason::Updateposition)); + CaretChangedReason::Updateposition, nullptr)); EXPECT_CALL(check, Call("scrollend2")); } @@ -564,12 +564,12 @@ MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION { InSequence dummy; EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( - CaretChangedReason::Updateposition)) + CaretChangedReason::Updateposition, nullptr)) .Times(1); EXPECT_CALL(check, Call("updatecarets")); - EXPECT_CALL(mManager, - DispatchCaretStateChangedEvent(CaretChangedReason::Scroll)) + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Scroll, nullptr)) .Times(1); EXPECT_CALL(check, Call("scrollstart1")); @@ -577,12 +577,12 @@ MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION { EXPECT_CALL(mManager.FirstCaret(), SetPosition(_, _)) .WillRepeatedly(Return(PositionChangedResult::Invisible)); EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( - CaretChangedReason::Updateposition)) + CaretChangedReason::Updateposition, nullptr)) .Times(1); EXPECT_CALL(check, Call("scrollend1")); - EXPECT_CALL(mManager, - DispatchCaretStateChangedEvent(CaretChangedReason::Scroll)) + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Scroll, nullptr)) .Times(1); EXPECT_CALL(check, Call("scrollstart2")); @@ -590,7 +590,7 @@ MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION { EXPECT_CALL(mManager.FirstCaret(), SetPosition(_, _)) .WillRepeatedly(Return(PositionChangedResult::Position)); EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( - CaretChangedReason::Updateposition)) + CaretChangedReason::Updateposition, nullptr)) .Times(1); EXPECT_CALL(check, Call("scrollend2")); } @@ -628,12 +628,12 @@ MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION { InSequence dummy; EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( - CaretChangedReason::Updateposition)) + CaretChangedReason::Updateposition, nullptr)) .Times(1); EXPECT_CALL(check, Call("updatecarets")); EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( - CaretChangedReason::Visibilitychange)) + CaretChangedReason::Visibilitychange, nullptr)) .Times(1); EXPECT_CALL(check, Call("hidecarets")); @@ -689,33 +689,33 @@ MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION { InSequence dummy; EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( - CaretChangedReason::Updateposition)); + CaretChangedReason::Updateposition, nullptr)); EXPECT_CALL(check, Call("updatecarets")); - EXPECT_CALL(mManager, - DispatchCaretStateChangedEvent(CaretChangedReason::Scroll)); + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Scroll, nullptr)); EXPECT_CALL(check, Call("scrollstart1")); EXPECT_CALL(mManager.FirstCaret(), SetPosition(_, _)) .WillOnce(Return(PositionChangedResult::Invisible)); EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( - CaretChangedReason::Updateposition)); + CaretChangedReason::Updateposition, nullptr)); EXPECT_CALL(check, Call("scrollend1")); - EXPECT_CALL(mManager, - DispatchCaretStateChangedEvent(CaretChangedReason::Scroll)); + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Scroll, nullptr)); EXPECT_CALL(check, Call("scrollstart2")); EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( - CaretChangedReason::Updateposition)); + CaretChangedReason::Updateposition, nullptr)); EXPECT_CALL(check, Call("scrollend2")); - EXPECT_CALL(mManager, - DispatchCaretStateChangedEvent(CaretChangedReason::Scroll)); + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Scroll, nullptr)); EXPECT_CALL(check, Call("scrollstart3")); EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( - CaretChangedReason::Updateposition)); + CaretChangedReason::Updateposition, nullptr)); EXPECT_CALL(check, Call("scrollend3")); } @@ -770,37 +770,37 @@ MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION { InSequence dummy; EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( - CaretChangedReason::Updateposition)); + CaretChangedReason::Updateposition, nullptr)); EXPECT_CALL(check, Call("singletap updatecarets")); EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( - CaretChangedReason::Updateposition)); + CaretChangedReason::Updateposition, nullptr)); EXPECT_CALL(check, Call("longtap updatecarets")); - EXPECT_CALL(mManager, - DispatchCaretStateChangedEvent(CaretChangedReason::Scroll)); + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Scroll, nullptr)); EXPECT_CALL(check, Call("longtap scrollstart1")); EXPECT_CALL(mManager.FirstCaret(), SetPosition(_, _)) .WillOnce(Return(PositionChangedResult::Invisible)); EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( - CaretChangedReason::Updateposition)); + CaretChangedReason::Updateposition, nullptr)); EXPECT_CALL(check, Call("longtap scrollend1")); - EXPECT_CALL(mManager, - DispatchCaretStateChangedEvent(CaretChangedReason::Scroll)); + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Scroll, nullptr)); EXPECT_CALL(check, Call("longtap scrollstart2")); EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( - CaretChangedReason::Updateposition)); + CaretChangedReason::Updateposition, nullptr)); EXPECT_CALL(check, Call("longtap scrollend2")); - EXPECT_CALL(mManager, - DispatchCaretStateChangedEvent(CaretChangedReason::Scroll)); + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Scroll, nullptr)); EXPECT_CALL(check, Call("longtap scrollstart3")); EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( - CaretChangedReason::Updateposition)); + CaretChangedReason::Updateposition, nullptr)); EXPECT_CALL(check, Call("longtap scrollend3")); } diff --git a/layout/base/tests/accessiblecaret_magnifier.html b/layout/base/tests/accessiblecaret_magnifier.html new file mode 100644 index 000000000000..855a027725a6 --- /dev/null +++ b/layout/base/tests/accessiblecaret_magnifier.html @@ -0,0 +1,91 @@ + + + + + + +

+

foobarbaz
+

+ + + diff --git a/layout/base/tests/mochitest.ini b/layout/base/tests/mochitest.ini index 6845928bc1c2..4e9c4882bcf3 100644 --- a/layout/base/tests/mochitest.ini +++ b/layout/base/tests/mochitest.ini @@ -8,6 +8,8 @@ support-files = selection-utils.js !/gfx/layers/apz/test/mochitest/apz_test_utils.js +[test_accessiblecaret_magnifier.html] +support-files = accessiblecaret_magnifier.html [test_after_paint_pref.html] [test_border_radius_hit_testing.html] support-files = border_radius_hit_testing_iframe.html diff --git a/layout/base/tests/test_accessiblecaret_magnifier.html b/layout/base/tests/test_accessiblecaret_magnifier.html new file mode 100644 index 000000000000..2ac97cccc504 --- /dev/null +++ b/layout/base/tests/test_accessiblecaret_magnifier.html @@ -0,0 +1,33 @@ + + + + + Test for magnifier event + + + + + +Mozilla Bug 1639087 +

+

+

+ + + diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml index ae1e683a32e8..4ba1bda0dff8 100644 --- a/modules/libpref/init/StaticPrefList.yaml +++ b/modules/libpref/init/StaticPrefList.yaml @@ -7221,6 +7221,13 @@ value: false mirror: always +# To support magnify glass, whether we dispatch additional chrome event such as +# dragcaret. +- name: layout.accessiblecaret.magnifier.enabled + type: bool + value: @IS_ANDROID@ + mirror: always + # One of several prefs affecting the maximum area to pre-render when animating # a large element on the compositor. # This pref enables transform (and transform like properties) animations on a