From 2f2f6eb3ee99630f84c67505afe0f578f537df00 Mon Sep 17 00:00:00 2001 From: Eitan Isaacson Date: Thu, 2 Jul 2020 21:57:55 +0000 Subject: [PATCH] Bug 1649575 - Part 5: Implement string building for GeckoTextMarkerRange. r=morgan This is a seperate patch because of the complexity. Differential Revision: https://phabricator.services.mozilla.com/D81761 --- accessible/mac/GeckoTextMarker.h | 31 ++++++- accessible/mac/GeckoTextMarker.mm | 112 ++++++++++++++++++++++++ accessible/mac/MOXTextMarkerDelegate.mm | 5 +- 3 files changed, 143 insertions(+), 5 deletions(-) diff --git a/accessible/mac/GeckoTextMarker.h b/accessible/mac/GeckoTextMarker.h index d1e62be7515f..a10b201bdd0d 100644 --- a/accessible/mac/GeckoTextMarker.h +++ b/accessible/mac/GeckoTextMarker.h @@ -48,12 +48,37 @@ class GeckoTextMarkerRange final { id CreateAXTextMarkerRange(); - bool IsValid() const { - return !mStart.mContainer.IsNull() && !mEnd.mContainer.IsNull(); - }; + bool IsValid() const { return !mStart.mContainer.IsNull() && !mEnd.mContainer.IsNull(); }; + + /** + * Return text enclosed by the range. + */ + NSString* Text() const; GeckoTextMarker mStart; GeckoTextMarker mEnd; + + private: + int32_t StartOffset(const AccessibleOrProxy& aChild) const; + + int32_t EndOffset(const AccessibleOrProxy& aChild) const; + + int32_t LinkCount(const AccessibleOrProxy& aContainer) const; + + AccessibleOrProxy LinkAt(const AccessibleOrProxy& aContainer, uint32_t aIndex) const; + + void AppendTextTo(const AccessibleOrProxy& aContainer, nsAString& aText, uint32_t aStartOffset, + uint32_t aEndOffset) const; + + /** + * Text() method helper. + * @param aText [in,out] calculated text + * @param aCurrent [in] currently traversed node + * @param aStartIntlOffset [in] start offset if current node is a text node + * @return true if calculation is not finished yet + */ + bool TextInternal(nsAString& aText, AccessibleOrProxy aCurrent, + int32_t aStartIntlOffset) const; }; } // namespace a11y diff --git a/accessible/mac/GeckoTextMarker.mm b/accessible/mac/GeckoTextMarker.mm index 9cf659f5e30c..8007ce917c6a 100644 --- a/accessible/mac/GeckoTextMarker.mm +++ b/accessible/mac/GeckoTextMarker.mm @@ -6,6 +6,7 @@ #include "DocAccessibleParent.h" #include "AccessibleOrProxy.h" +#include "nsCocoaUtils.h" #import "GeckoTextMarker.h" @@ -123,5 +124,116 @@ id GeckoTextMarkerRange::CreateAXTextMarkerRange() { return [static_cast(cf_text_marker_range) autorelease]; } +NSString* GeckoTextMarkerRange::Text() const { + nsAutoString text; + TextInternal(text, mStart.mContainer, mStart.mOffset); + return nsCocoaUtils::ToNSString(text); +} + +void GeckoTextMarkerRange::AppendTextTo(const AccessibleOrProxy& aContainer, nsAString& aText, + uint32_t aStartOffset, uint32_t aEndOffset) const { + nsAutoString text; + if (aContainer.IsProxy()) { + aContainer.AsProxy()->TextSubstring(aStartOffset, aEndOffset, text); + } else if (aContainer.AsAccessible()->IsHyperText()) { + aContainer.AsAccessible()->AsHyperText()->TextSubstring(aStartOffset, aEndOffset, text); + } + + aText.Append(text); +} + +int32_t GeckoTextMarkerRange::StartOffset(const AccessibleOrProxy& aChild) const { + if (aChild.IsProxy()) { + bool unused; + return aChild.AsProxy()->StartOffset(&unused); + } + + return aChild.AsAccessible()->StartOffset(); +} + +int32_t GeckoTextMarkerRange::EndOffset(const AccessibleOrProxy& aChild) const { + if (aChild.IsProxy()) { + bool unused; + return aChild.AsProxy()->EndOffset(&unused); + } + + return aChild.AsAccessible()->EndOffset(); +} + +int32_t GeckoTextMarkerRange::LinkCount(const AccessibleOrProxy& aContainer) const { + if (aContainer.IsProxy()) { + return aContainer.AsProxy()->LinkCount(); + } + + if (aContainer.AsAccessible()->IsHyperText()) { + return aContainer.AsAccessible()->AsHyperText()->LinkCount(); + } + + return 0; +} + +AccessibleOrProxy GeckoTextMarkerRange::LinkAt(const AccessibleOrProxy& aContainer, + uint32_t aIndex) const { + if (aContainer.IsProxy()) { + return aContainer.AsProxy()->LinkAt(aIndex); + } + + if (aContainer.AsAccessible()->IsHyperText()) { + return aContainer.AsAccessible()->AsHyperText()->LinkAt(aIndex); + } + + return AccessibleOrProxy(); +} + +bool GeckoTextMarkerRange::TextInternal(nsAString& aText, AccessibleOrProxy aCurrent, + int32_t aStartIntlOffset) const { + int32_t endIntlOffset = aCurrent == mEnd.mContainer ? mEnd.mOffset : -1; + if (endIntlOffset == 0) { + return false; + } + + AccessibleOrProxy next; + int32_t linkCount = LinkCount(aCurrent); + int32_t linkStartOffset = 0; + int32_t end = endIntlOffset; + // Find the first link that is at or after the start offset. + for (int32_t i = 0; i < linkCount; i++) { + AccessibleOrProxy link = LinkAt(aCurrent, i); + linkStartOffset = StartOffset(link); + if (aStartIntlOffset <= linkStartOffset) { + next = link; + break; + } + } + + bool endIsBeyondLink = !next.IsNull() && (endIntlOffset < 0 || endIntlOffset > linkStartOffset); + + if (endIsBeyondLink) { + end = linkStartOffset; + } + + AppendTextTo(aCurrent, aText, aStartIntlOffset, end); + + if (endIsBeyondLink) { + if (!TextInternal(aText, next, 0)) { + return false; + } + } + + if (endIntlOffset >= 0) { + // If our original end marker is positive, we know we found all the text we + // needed within this current node, and our search is complete. + return false; + } + + next = aCurrent.Parent(); + if (!next.IsNull()) { + // The end offset is passed this link, go back to the parent + // and continue from this link's end offset. + return TextInternal(aText, next, EndOffset(aCurrent)); + } + + return true; +} } } diff --git a/accessible/mac/MOXTextMarkerDelegate.mm b/accessible/mac/MOXTextMarkerDelegate.mm index 293939e179f5..ecc39f9e398e 100644 --- a/accessible/mac/MOXTextMarkerDelegate.mm +++ b/accessible/mac/MOXTextMarkerDelegate.mm @@ -60,11 +60,12 @@ static nsDataHashtable sDelegates; } - (NSString*)moxStringForTextMarkerRange:(id)textMarkerRange { - return nil; + mozilla::a11y::GeckoTextMarkerRange range(mGeckoDocAccessible, textMarkerRange); + return range.Text(); } - (NSNumber*)moxLengthForTextMarkerRange:(id)textMarkerRange { - return nil; + return @([[self moxStringForTextMarkerRange:textMarkerRange] length]); } - (id)moxTextMarkerRangeForUnorderedTextMarkers:(NSArray*)textMarkers {