diff --git a/accessible/atk/nsMaiInterfaceText.cpp b/accessible/atk/nsMaiInterfaceText.cpp index 93b50df994f1..91c9d57ef6ae 100644 --- a/accessible/atk/nsMaiInterfaceText.cpp +++ b/accessible/atk/nsMaiInterfaceText.cpp @@ -532,25 +532,19 @@ static gboolean setCaretOffsetCB(AtkText* aText, gint aOffset) { static gboolean scrollSubstringToCB(AtkText* aText, gint aStartOffset, gint aEndOffset, AtkScrollType aType) { - AtkObject* atkObject = ATK_OBJECT(aText); - AccessibleWrap* accWrap = GetAccessibleWrap(atkObject); - if (accWrap) { - HyperTextAccessible* text = accWrap->AsHyperText(); - if (!text || !text->IsTextRole() || - !text->IsValidRange(aStartOffset, aEndOffset)) { - return FALSE; - } - text->ScrollSubstringTo(aStartOffset, aEndOffset, aType); - return TRUE; + Accessible* acc = GetInternalObj(ATK_OBJECT(aText)); + if (!acc) { + return FALSE; } - RemoteAccessible* proxy = GetProxy(atkObject); - if (proxy) { - proxy->ScrollSubstringTo(aStartOffset, aEndOffset, aType); - return TRUE; + HyperTextAccessibleBase* text = acc->AsHyperTextBase(); + if (!text) { + return FALSE; } - return FALSE; + text->ScrollSubstringTo(aStartOffset, aEndOffset, aType); + + return TRUE; } static gboolean scrollSubstringToPointCB(AtkText* aText, gint aStartOffset, diff --git a/accessible/base/TextLeafRange.cpp b/accessible/base/TextLeafRange.cpp index 7a8c4bce6a78..d8941c430f42 100644 --- a/accessible/base/TextLeafRange.cpp +++ b/accessible/base/TextLeafRange.cpp @@ -1858,6 +1858,42 @@ bool TextLeafRange::SetSelection(int32_t aSelectionNum) const { return false; } +void TextLeafRange::ScrollIntoView(uint32_t aScrollType) const { + if (!mStart || !mEnd || mStart.mAcc->IsLocal() != mEnd.mAcc->IsLocal()) { + return; + } + + if (mStart.mAcc->IsRemote()) { + DocAccessibleParent* doc = mStart.mAcc->AsRemote()->Document(); + if (doc != mEnd.mAcc->AsRemote()->Document()) { + // Can't scroll range that spans docs. + return; + } + + Unused << doc->SendScrollTextLeafRangeIntoView( + mStart.mAcc->ID(), mStart.mOffset, mEnd.mAcc->ID(), mEnd.mOffset, + aScrollType); + return; + } + + auto [startContent, startContentOffset] = mStart.ToDOMPoint(); + auto [endContent, endContentOffset] = mEnd.ToDOMPoint(); + + if (!startContent || !endContent) { + return; + } + + ErrorResult er; + RefPtr domRange = nsRange::Create(startContent, startContentOffset, + endContent, endContentOffset, er); + if (er.Failed()) { + return; + } + + nsCoreUtils::ScrollSubstringTo(mStart.mAcc->AsLocal()->GetFrame(), domRange, + aScrollType); +} + TextLeafRange::Iterator TextLeafRange::Iterator::BeginIterator( const TextLeafRange& aRange) { Iterator result(aRange); diff --git a/accessible/base/TextLeafRange.h b/accessible/base/TextLeafRange.h index 4d4a1cbf9b53..d3f33b562258 100644 --- a/accessible/base/TextLeafRange.h +++ b/accessible/base/TextLeafRange.h @@ -297,6 +297,8 @@ class TextLeafRange final { */ MOZ_CAN_RUN_SCRIPT bool SetSelection(int32_t aSelectionNum) const; + MOZ_CAN_RUN_SCRIPT void ScrollIntoView(uint32_t aScrollType) const; + private: TextLeafPoint mStart; TextLeafPoint mEnd; diff --git a/accessible/base/TextRange.cpp b/accessible/base/TextRange.cpp index 31e361cbeef1..9a62a6171216 100644 --- a/accessible/base/TextRange.cpp +++ b/accessible/base/TextRange.cpp @@ -263,19 +263,6 @@ bool TextRange::Crop(Accessible* aContainer) { return true; } -void TextRange::ScrollIntoView(uint32_t aScrollType) const { - LocalAccessible* root = mRoot->AsLocal(); - if (!root) { - MOZ_ASSERT_UNREACHABLE("Not supported for RemoteAccessible"); - return; - } - RefPtr range = nsRange::Create(root->GetContent()); - if (AssignDOMRange(range)) { - nsCoreUtils::ScrollSubstringTo(mStartContainer->AsLocal()->GetFrame(), - range, aScrollType); - } -} - /** * Convert the given DOM point to a DOM point in non-generated contents. * diff --git a/accessible/base/TextRange.h b/accessible/base/TextRange.h index bcc41c60ad4e..59839ff3a7d3 100644 --- a/accessible/base/TextRange.h +++ b/accessible/base/TextRange.h @@ -111,11 +111,6 @@ class TextRange final { */ bool Crop(Accessible* aContainer); - /** - * Scroll the text range into view. - */ - void ScrollIntoView(uint32_t aScrollType) const; - /** * Convert stored hypertext offsets into DOM offsets and assign it to DOM * range. diff --git a/accessible/basetypes/HyperTextAccessibleBase.cpp b/accessible/basetypes/HyperTextAccessibleBase.cpp index 00328cd7ec7b..0be1b57851cc 100644 --- a/accessible/basetypes/HyperTextAccessibleBase.cpp +++ b/accessible/basetypes/HyperTextAccessibleBase.cpp @@ -789,4 +789,12 @@ bool HyperTextAccessibleBase::SetSelectionBoundsAt(int32_t aSelectionNum, return range.SetSelection(aSelectionNum); } +void HyperTextAccessibleBase::ScrollSubstringTo(int32_t aStartOffset, + int32_t aEndOffset, + uint32_t aScrollType) { + TextLeafRange range(ToTextLeafPoint(aStartOffset), + ToTextLeafPoint(aEndOffset, true)); + range.ScrollIntoView(aScrollType); +} + } // namespace mozilla::a11y diff --git a/accessible/basetypes/HyperTextAccessibleBase.h b/accessible/basetypes/HyperTextAccessibleBase.h index cd19f309cd23..f085e5ad9c5b 100644 --- a/accessible/basetypes/HyperTextAccessibleBase.h +++ b/accessible/basetypes/HyperTextAccessibleBase.h @@ -233,6 +233,12 @@ class HyperTextAccessibleBase { MOZ_CAN_RUN_SCRIPT_BOUNDARY virtual bool RemoveFromSelection( int32_t aSelectionNum) = 0; + /** + * Scroll the given text range into view. + */ + MOZ_CAN_RUN_SCRIPT_BOUNDARY virtual void ScrollSubstringTo( + int32_t aStartOffset, int32_t aEndOffset, uint32_t aScrollType); + protected: virtual const Accessible* Acc() const = 0; Accessible* Acc() { diff --git a/accessible/generic/HyperTextAccessible.cpp b/accessible/generic/HyperTextAccessible.cpp index 89f0987bf90a..d3efad582f7f 100644 --- a/accessible/generic/HyperTextAccessible.cpp +++ b/accessible/generic/HyperTextAccessible.cpp @@ -1943,13 +1943,6 @@ bool HyperTextAccessible::RemoveFromSelection(int32_t aSelectionNum) { return true; } -void HyperTextAccessible::ScrollSubstringTo(int32_t aStartOffset, - int32_t aEndOffset, - uint32_t aScrollType) { - TextRange range(this, this, aStartOffset, this, aEndOffset); - range.ScrollIntoView(aScrollType); -} - void HyperTextAccessible::ScrollSubstringToPoint(int32_t aStartOffset, int32_t aEndOffset, uint32_t aCoordinateType, diff --git a/accessible/generic/HyperTextAccessible.h b/accessible/generic/HyperTextAccessible.h index e7e588d00283..51f017312015 100644 --- a/accessible/generic/HyperTextAccessible.h +++ b/accessible/generic/HyperTextAccessible.h @@ -211,12 +211,6 @@ class HyperTextAccessible : public AccessibleWrap, MOZ_CAN_RUN_SCRIPT_BOUNDARY virtual bool RemoveFromSelection( int32_t aSelectionNum) override; - /** - * Scroll the given text range into view. - */ - void ScrollSubstringTo(int32_t aStartOffset, int32_t aEndOffset, - uint32_t aScrollType); - /** * Scroll the given text range to the given point. */ diff --git a/accessible/ipc/DocAccessibleChildBase.cpp b/accessible/ipc/DocAccessibleChildBase.cpp index 5711b004da26..a9a57be2cede 100644 --- a/accessible/ipc/DocAccessibleChildBase.cpp +++ b/accessible/ipc/DocAccessibleChildBase.cpp @@ -244,6 +244,19 @@ mozilla::ipc::IPCResult DocAccessibleChildBase::RecvSetTextSelection( return IPC_OK(); } +mozilla::ipc::IPCResult DocAccessibleChildBase::RecvScrollTextLeafRangeIntoView( + const uint64_t& aStartID, const int32_t& aStartOffset, + const uint64_t& aEndID, const int32_t& aEndOffset, + const uint32_t& aScrollType) { + TextLeafRange range(TextLeafPoint(IdToAccessible(aStartID), aStartOffset), + TextLeafPoint(IdToAccessible(aEndID), aEndOffset)); + if (range) { + range.ScrollIntoView(aScrollType); + } + + return IPC_OK(); +} + mozilla::ipc::IPCResult DocAccessibleChildBase::RecvRemoveTextSelection( const uint64_t& aID, const int32_t& aSelectionNum) { HyperTextAccessible* acc = IdToHyperTextAccessible(aID); diff --git a/accessible/ipc/DocAccessibleChildBase.h b/accessible/ipc/DocAccessibleChildBase.h index 4658bad37044..52767308dc1e 100644 --- a/accessible/ipc/DocAccessibleChildBase.h +++ b/accessible/ipc/DocAccessibleChildBase.h @@ -84,6 +84,12 @@ class DocAccessibleChildBase : public PDocAccessibleChild { const uint64_t& aEndID, const int32_t& aEndOffset, const int32_t& aSelectionNum) override; + MOZ_CAN_RUN_SCRIPT_BOUNDARY + virtual mozilla::ipc::IPCResult RecvScrollTextLeafRangeIntoView( + const uint64_t& aStartID, const int32_t& aStartOffset, + const uint64_t& aEndID, const int32_t& aEndOffset, + const uint32_t& aScrollType) override; + virtual mozilla::ipc::IPCResult RecvRemoveTextSelection( const uint64_t& aID, const int32_t& aSelectionNum) override; diff --git a/accessible/ipc/RemoteAccessibleShared.h b/accessible/ipc/RemoteAccessibleShared.h index 6eaa2e879e9b..39857bdafaa5 100644 --- a/accessible/ipc/RemoteAccessibleShared.h +++ b/accessible/ipc/RemoteAccessibleShared.h @@ -85,8 +85,8 @@ virtual bool SetSelectionBoundsAt(int32_t aSelectionNum, int32_t aStartOffset, virtual bool RemoveFromSelection(int32_t aSelectionNum) override; -void ScrollSubstringTo(int32_t aStartOffset, int32_t aEndOffset, - uint32_t aScrollType); +virtual void ScrollSubstringTo(int32_t aStartOffset, int32_t aEndOffset, + uint32_t aScrollType) override; void ScrollSubstringToPoint(int32_t aStartOffset, int32_t aEndOffset, uint32_t aCoordinateType, int32_t aX, int32_t aY); diff --git a/accessible/ipc/other/PDocAccessible.ipdl b/accessible/ipc/other/PDocAccessible.ipdl index 3a2ce8e0ccb3..efda8437f3d3 100644 --- a/accessible/ipc/other/PDocAccessible.ipdl +++ b/accessible/ipc/other/PDocAccessible.ipdl @@ -211,6 +211,9 @@ child: int32_t aSelectionNum); async RemoveTextSelection(uint64_t aID, int32_t aSelectionNum); + async ScrollTextLeafRangeIntoView(uint64_t aStartID, int32_t aStartOffset, + uint64_t aEndID, int32_t aEndOffset, + uint32_t aScrollType); async ScrollSubstringTo(uint64_t aID, int32_t aStartOffset, int32_t aEndOffset, uint32_t aScrollType); async ScrollSubstringToPoint(uint64_t aID, diff --git a/accessible/ipc/other/RemoteAccessible.cpp b/accessible/ipc/other/RemoteAccessible.cpp index 361ba7b76439..9b2219514b6b 100644 --- a/accessible/ipc/other/RemoteAccessible.cpp +++ b/accessible/ipc/other/RemoteAccessible.cpp @@ -351,6 +351,13 @@ bool RemoteAccessible::RemoveFromSelection(int32_t aSelectionNum) { void RemoteAccessible::ScrollSubstringTo(int32_t aStartOffset, int32_t aEndOffset, uint32_t aScrollType) { + if (StaticPrefs::accessibility_cache_enabled_AtStartup()) { + MOZ_ASSERT(IsHyperText(), "is not hypertext?"); + RemoteAccessibleBase::ScrollSubstringTo( + aStartOffset, aEndOffset, aScrollType); + return; + } + Unused << mDoc->SendScrollSubstringTo(mID, aStartOffset, aEndOffset, aScrollType); } diff --git a/accessible/ipc/win/PDocAccessible.ipdl b/accessible/ipc/win/PDocAccessible.ipdl index 29a480c18d3c..ab85ecc30121 100644 --- a/accessible/ipc/win/PDocAccessible.ipdl +++ b/accessible/ipc/win/PDocAccessible.ipdl @@ -142,6 +142,10 @@ child: int32_t aSelectionNum); async RemoveTextSelection(uint64_t aID, int32_t aSelectionNum); + async ScrollTextLeafRangeIntoView(uint64_t aStartID, int32_t aStartOffset, + uint64_t aEndID, int32_t aEndOffset, + uint32_t aScrollType); + /* * Verify the cache. Used for testing purposes. */ diff --git a/accessible/ipc/win/RemoteAccessible.cpp b/accessible/ipc/win/RemoteAccessible.cpp index 7cd3758239fe..eec0ba59b761 100644 --- a/accessible/ipc/win/RemoteAccessible.cpp +++ b/accessible/ipc/win/RemoteAccessible.cpp @@ -733,9 +733,12 @@ void RemoteAccessible::ScrollSubstringTo(int32_t aStartOffset, int32_t aEndOffset, uint32_t aScrollType) { if (StaticPrefs::accessibility_cache_enabled_AtStartup()) { - // Not yet supported by the cache. + MOZ_ASSERT(IsHyperText(), "is not hypertext?"); + RemoteAccessibleBase::ScrollSubstringTo( + aStartOffset, aEndOffset, aScrollType); return; } + RefPtr acc = QueryInterface(this); if (!acc) { return; diff --git a/accessible/mac/mozTextAccessible.mm b/accessible/mac/mozTextAccessible.mm index 9dbdc8e7b8cf..5aa73b8215f9 100644 --- a/accessible/mac/mozTextAccessible.mm +++ b/accessible/mac/mozTextAccessible.mm @@ -236,16 +236,9 @@ inline NSString* ToNSString(id aValue) { return; } - if (mGeckoAccessible->IsLocal()) { - if (HyperTextAccessible* textAcc = - mGeckoAccessible->AsLocal()->AsHyperText()) { - textAcc->ScrollSubstringTo(range.location, range.location + range.length, - nsIAccessibleScrollType::SCROLL_TYPE_TOP_EDGE); - } - } else { - mGeckoAccessible->AsRemote()->ScrollSubstringTo( - range.location, range.location + range.length, - nsIAccessibleScrollType::SCROLL_TYPE_TOP_EDGE); + if (HyperTextAccessibleBase* textAcc = mGeckoAccessible->AsHyperTextBase()) { + textAcc->ScrollSubstringTo(range.location, range.location + range.length, + nsIAccessibleScrollType::SCROLL_TYPE_TOP_EDGE); } } diff --git a/accessible/tests/browser/scroll/browser.ini b/accessible/tests/browser/scroll/browser.ini index e332597f0e7a..0cae6a7c0e3b 100644 --- a/accessible/tests/browser/scroll/browser.ini +++ b/accessible/tests/browser/scroll/browser.ini @@ -12,3 +12,4 @@ prefs = skip-if = os == 'win' # bug 1372296 [browser_test_scroll_bounds.js] [browser_test_scrollTo.js] +[browser_test_scroll_substring.js] diff --git a/accessible/tests/browser/scroll/browser_test_scroll_substring.js b/accessible/tests/browser/scroll/browser_test_scroll_substring.js new file mode 100644 index 000000000000..989c9286b29a --- /dev/null +++ b/accessible/tests/browser/scroll/browser_test_scroll_substring.js @@ -0,0 +1,67 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/layout.js */ +loadScripts({ name: "layout.js", dir: MOCHITESTS_DIR }); + +/** + * Test nsIAccessibleText::scrollSubstringTo. + */ +addAccessibleTask( + ` + +
+
+
+
+
+
+It's a jetpack, Michael. What could possibly go wrong?
+
+
+
+
+
+The only thing I found in the fridge was a dead dove in a bag.
+
`, + async function(browser, docAcc) { + let text = findAccessibleChildByID(docAcc, "text", [nsIAccessibleText]); + let [, containerY, , containerHeight] = getBounds(text); + let getCharY = () => { + let objY = {}; + text.getCharacterExtents(7, {}, objY, {}, {}, COORDTYPE_SCREEN_RELATIVE); + return objY.value; + }; + ok( + containerHeight < getCharY(), + "Character is outside of container bounds" + ); + text.scrollSubstringTo(7, 8, SCROLL_TYPE_TOP_EDGE); + + await waitForContentPaint(browser); + await untilCacheIs( + getCharY, + containerY, + "Character is scrolled to top of container" + ); + }, + { + topLevel: !isWinNoCache, + iframe: !isWinNoCache, + remoteIframe: !isWinNoCache, + chrome: true, + } +); diff --git a/accessible/tests/mochitest/common.js b/accessible/tests/mochitest/common.js index ae12b4b7bce3..a13e18a26c5c 100644 --- a/accessible/tests/mochitest/common.js +++ b/accessible/tests/mochitest/common.js @@ -82,6 +82,7 @@ const SEAMONKEY = navigator.userAgent.match(/ SeaMonkey\//); const STATE_BUSY = nsIAccessibleStates.STATE_BUSY; +const SCROLL_TYPE_TOP_EDGE = nsIAccessibleScrollType.SCROLL_TYPE_TOP_EDGE; const SCROLL_TYPE_ANYWHERE = nsIAccessibleScrollType.SCROLL_TYPE_ANYWHERE; const COORDTYPE_SCREEN_RELATIVE = diff --git a/accessible/windows/ia2/ia2AccessibleText.cpp b/accessible/windows/ia2/ia2AccessibleText.cpp index 217da104b421..c94a3ee9421a 100644 --- a/accessible/windows/ia2/ia2AccessibleText.cpp +++ b/accessible/windows/ia2/ia2AccessibleText.cpp @@ -380,9 +380,9 @@ ia2AccessibleText::get_nCharacters(long* aNCharacters) { STDMETHODIMP ia2AccessibleText::scrollSubstringTo(long aStartIndex, long aEndIndex, enum IA2ScrollType aScrollType) { - auto [textAcc, hr] = LocalTextAcc(); + HyperTextAccessibleBase* textAcc = TextAcc(); if (!textAcc) { - return hr; + return CO_E_OBJNOTCONNECTED; } if (!textAcc->IsValidRange(aStartIndex, aEndIndex)) return E_INVALIDARG; diff --git a/accessible/xpcom/xpcAccessibleHyperText.cpp b/accessible/xpcom/xpcAccessibleHyperText.cpp index f3296118c3ca..d90bd249ce42 100644 --- a/accessible/xpcom/xpcAccessibleHyperText.cpp +++ b/accessible/xpcom/xpcAccessibleHyperText.cpp @@ -388,11 +388,7 @@ xpcAccessibleHyperText::ScrollSubstringTo(int32_t aStartOffset, uint32_t aScrollType) { if (!mIntl) return NS_ERROR_FAILURE; - if (mIntl->IsLocal()) { - IntlLocal()->ScrollSubstringTo(aStartOffset, aEndOffset, aScrollType); - } else { - mIntl->AsRemote()->ScrollSubstringTo(aStartOffset, aEndOffset, aScrollType); - } + Intl()->ScrollSubstringTo(aStartOffset, aEndOffset, aScrollType); return NS_OK; }