1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "sdnTextAccessible.h"
8 
9 #include "ISimpleDOM.h"
10 
11 #include "nsCoreUtils.h"
12 #include "DocAccessible.h"
13 
14 #include "nsIFrame.h"
15 #include "nsFontMetrics.h"
16 #include "nsPresContext.h"
17 #include "nsLayoutUtils.h"
18 #include "nsRange.h"
19 #include "gfxTextRun.h"
20 #include "nsIAccessibleTypes.h"
21 #include "mozilla/gfx/2D.h"
22 
23 using namespace mozilla::a11y;
24 
25 ////////////////////////////////////////////////////////////////////////////////
26 // sdnTextAccessible
27 ////////////////////////////////////////////////////////////////////////////////
28 
29 IMPL_IUNKNOWN_QUERY_HEAD(sdnTextAccessible)
IMPL_IUNKNOWN_QUERY_IFACE(ISimpleDOMText)30 IMPL_IUNKNOWN_QUERY_IFACE(ISimpleDOMText)
31 IMPL_IUNKNOWN_QUERY_TAIL_AGGREGATED(mMsaa)
32 
33 STDMETHODIMP
34 sdnTextAccessible::get_domText(BSTR __RPC_FAR* aText) {
35   if (!aText) return E_INVALIDARG;
36   *aText = nullptr;
37 
38   AccessibleWrap* acc = mMsaa->LocalAcc();
39   if (!acc) return CO_E_OBJNOTCONNECTED;
40 
41   nsAutoString nodeValue;
42 
43   acc->GetContent()->GetNodeValue(nodeValue);
44   if (nodeValue.IsEmpty()) return S_FALSE;
45 
46   *aText = ::SysAllocStringLen(nodeValue.get(), nodeValue.Length());
47   return *aText ? S_OK : E_OUTOFMEMORY;
48 }
49 
50 STDMETHODIMP
get_clippedSubstringBounds(unsigned int aStartIndex,unsigned int aEndIndex,int __RPC_FAR * aX,int __RPC_FAR * aY,int __RPC_FAR * aWidth,int __RPC_FAR * aHeight)51 sdnTextAccessible::get_clippedSubstringBounds(
52     unsigned int aStartIndex, unsigned int aEndIndex, int __RPC_FAR* aX,
53     int __RPC_FAR* aY, int __RPC_FAR* aWidth, int __RPC_FAR* aHeight) {
54   nscoord x = 0, y = 0, width = 0, height = 0;
55   HRESULT rv = get_unclippedSubstringBounds(aStartIndex, aEndIndex, &x, &y,
56                                             &width, &height);
57   if (FAILED(rv)) return rv;
58 
59   DocAccessible* document = mMsaa->LocalAcc()->Document();
60   NS_ASSERTION(
61       document,
62       "There must always be a doc accessible, but there isn't. Crash!");
63 
64   nsIntRect docRect = document->Bounds();
65   nsIntRect unclippedRect(x, y, width, height);
66 
67   nsIntRect clippedRect;
68   clippedRect.IntersectRect(unclippedRect, docRect);
69 
70   *aX = clippedRect.X();
71   *aY = clippedRect.Y();
72   *aWidth = clippedRect.Width();
73   *aHeight = clippedRect.Height();
74   return S_OK;
75 }
76 
77 STDMETHODIMP
get_unclippedSubstringBounds(unsigned int aStartIndex,unsigned int aEndIndex,int __RPC_FAR * aX,int __RPC_FAR * aY,int __RPC_FAR * aWidth,int __RPC_FAR * aHeight)78 sdnTextAccessible::get_unclippedSubstringBounds(
79     unsigned int aStartIndex, unsigned int aEndIndex, int __RPC_FAR* aX,
80     int __RPC_FAR* aY, int __RPC_FAR* aWidth, int __RPC_FAR* aHeight) {
81   if (!aX || !aY || !aWidth || !aHeight) return E_INVALIDARG;
82   *aX = *aY = *aWidth = *aHeight = 0;
83 
84   AccessibleWrap* acc = mMsaa->LocalAcc();
85   if (!acc) return CO_E_OBJNOTCONNECTED;
86 
87   nsIFrame* frame = acc->GetFrame();
88   NS_ENSURE_TRUE(frame, E_FAIL);
89 
90   nsPoint startPoint, endPoint;
91   nsIFrame* startFrame =
92       GetPointFromOffset(frame, aStartIndex, true, startPoint);
93   nsIFrame* endFrame = GetPointFromOffset(frame, aEndIndex, false, endPoint);
94   if (!startFrame || !endFrame) return E_FAIL;
95 
96   nsRect sum;
97   nsIFrame* iter = startFrame;
98   nsIFrame* stopLoopFrame = endFrame->GetNextContinuation();
99   for (; iter != stopLoopFrame; iter = iter->GetNextContinuation()) {
100     nsRect rect = iter->GetScreenRectInAppUnits();
101     nscoord start = (iter == startFrame) ? startPoint.x : 0;
102     nscoord end = (iter == endFrame) ? endPoint.x : rect.Width();
103     rect.MoveByX(start);
104     rect.SetWidth(end - start);
105     sum.UnionRect(sum, rect);
106   }
107 
108   nsPresContext* presContext = acc->Document()->PresContext();
109   *aX = presContext->AppUnitsToDevPixels(sum.X());
110   *aY = presContext->AppUnitsToDevPixels(sum.Y());
111   *aWidth = presContext->AppUnitsToDevPixels(sum.Width());
112   *aHeight = presContext->AppUnitsToDevPixels(sum.Height());
113 
114   return S_OK;
115 }
116 
117 STDMETHODIMP
scrollToSubstring(unsigned int aStartIndex,unsigned int aEndIndex)118 sdnTextAccessible::scrollToSubstring(unsigned int aStartIndex,
119                                      unsigned int aEndIndex) {
120   AccessibleWrap* acc = mMsaa->LocalAcc();
121   if (!acc) return CO_E_OBJNOTCONNECTED;
122 
123   RefPtr<nsRange> range = nsRange::Create(acc->GetContent());
124   if (NS_FAILED(range->SetStart(acc->GetContent(), aStartIndex))) return E_FAIL;
125 
126   if (NS_FAILED(range->SetEnd(acc->GetContent(), aEndIndex))) return E_FAIL;
127 
128   nsresult rv = nsCoreUtils::ScrollSubstringTo(
129       acc->GetFrame(), range, nsIAccessibleScrollType::SCROLL_TYPE_ANYWHERE);
130   return GetHRESULT(rv);
131 }
132 
133 STDMETHODIMP
get_fontFamily(BSTR __RPC_FAR * aFontFamily)134 sdnTextAccessible::get_fontFamily(BSTR __RPC_FAR* aFontFamily) {
135   if (!aFontFamily) return E_INVALIDARG;
136   *aFontFamily = nullptr;
137 
138   AccessibleWrap* acc = mMsaa->LocalAcc();
139   if (!acc) return CO_E_OBJNOTCONNECTED;
140 
141   nsIFrame* frame = acc->GetFrame();
142   if (!frame) return E_FAIL;
143 
144   RefPtr<nsFontMetrics> fm = nsLayoutUtils::GetFontMetricsForFrame(frame, 1.0f);
145 
146   const nsCString& name =
147       fm->GetThebesFontGroup()->GetFirstValidFont()->GetName();
148   if (name.IsEmpty()) return S_FALSE;
149 
150   NS_ConvertUTF8toUTF16 str(name);
151   *aFontFamily = ::SysAllocStringLen(str.get(), str.Length());
152   return *aFontFamily ? S_OK : E_OUTOFMEMORY;
153 }
154 
GetPointFromOffset(nsIFrame * aContainingFrame,int32_t aOffset,bool aPreferNext,nsPoint & aOutPoint)155 nsIFrame* sdnTextAccessible::GetPointFromOffset(nsIFrame* aContainingFrame,
156                                                 int32_t aOffset,
157                                                 bool aPreferNext,
158                                                 nsPoint& aOutPoint) {
159   nsIFrame* textFrame = nullptr;
160   int32_t outOffset;
161   aContainingFrame->GetChildFrameContainingOffset(aOffset, aPreferNext,
162                                                   &outOffset, &textFrame);
163   if (textFrame) textFrame->GetPointFromOffset(aOffset, &aOutPoint);
164 
165   return textFrame;
166 }
167