|
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 "InterfaceInitFuncs.h" |
|
8 |
|
9 #include "Accessible-inl.h" |
|
10 #include "HyperTextAccessible-inl.h" |
|
11 #include "nsMai.h" |
|
12 |
|
13 #include "nsIAccessibleTypes.h" |
|
14 #include "nsIPersistentProperties2.h" |
|
15 #include "nsISimpleEnumerator.h" |
|
16 |
|
17 #include "mozilla/Likely.h" |
|
18 |
|
19 using namespace mozilla; |
|
20 using namespace mozilla::a11y; |
|
21 |
|
22 static const char* sAtkTextAttrNames[ATK_TEXT_ATTR_LAST_DEFINED]; |
|
23 |
|
24 static AtkAttributeSet* |
|
25 ConvertToAtkTextAttributeSet(nsIPersistentProperties* aAttributes) |
|
26 { |
|
27 if (!aAttributes) |
|
28 return nullptr; |
|
29 |
|
30 AtkAttributeSet* objAttributeSet = nullptr; |
|
31 nsCOMPtr<nsISimpleEnumerator> propEnum; |
|
32 nsresult rv = aAttributes->Enumerate(getter_AddRefs(propEnum)); |
|
33 NS_ENSURE_SUCCESS(rv, nullptr); |
|
34 |
|
35 bool hasMore = false; |
|
36 while (NS_SUCCEEDED(propEnum->HasMoreElements(&hasMore)) && hasMore) { |
|
37 nsCOMPtr<nsISupports> sup; |
|
38 rv = propEnum->GetNext(getter_AddRefs(sup)); |
|
39 NS_ENSURE_SUCCESS(rv, objAttributeSet); |
|
40 |
|
41 nsCOMPtr<nsIPropertyElement> propElem(do_QueryInterface(sup)); |
|
42 NS_ENSURE_TRUE(propElem, objAttributeSet); |
|
43 |
|
44 nsAutoCString name; |
|
45 rv = propElem->GetKey(name); |
|
46 NS_ENSURE_SUCCESS(rv, objAttributeSet); |
|
47 |
|
48 nsAutoString value; |
|
49 rv = propElem->GetValue(value); |
|
50 NS_ENSURE_SUCCESS(rv, objAttributeSet); |
|
51 |
|
52 AtkAttribute* objAttr = (AtkAttribute*)g_malloc(sizeof(AtkAttribute)); |
|
53 objAttr->name = g_strdup(name.get()); |
|
54 objAttr->value = g_strdup(NS_ConvertUTF16toUTF8(value).get()); |
|
55 objAttributeSet = g_slist_prepend(objAttributeSet, objAttr); |
|
56 |
|
57 // Handle attributes where atk has its own name. |
|
58 const char* atkName = nullptr; |
|
59 nsAutoString atkValue; |
|
60 if (name.EqualsLiteral("color")) { |
|
61 // The format of the atk attribute is r,g,b and the gecko one is |
|
62 // rgb(r,g,b). |
|
63 atkValue = Substring(value, 5, value.Length() - 1); |
|
64 atkName = sAtkTextAttrNames[ATK_TEXT_ATTR_FG_COLOR]; |
|
65 } else if (name.EqualsLiteral("background-color")) { |
|
66 // The format of the atk attribute is r,g,b and the gecko one is |
|
67 // rgb(r,g,b). |
|
68 atkValue = Substring(value, 5, value.Length() - 1); |
|
69 atkName = sAtkTextAttrNames[ATK_TEXT_ATTR_BG_COLOR]; |
|
70 } else if (name.EqualsLiteral("font-family")) { |
|
71 atkValue = value; |
|
72 atkName = sAtkTextAttrNames[ATK_TEXT_ATTR_FAMILY_NAME]; |
|
73 } else if (name.Equals("font-size")) { |
|
74 // ATK wants the number of pixels without px at the end. |
|
75 atkValue = StringHead(value, value.Length() - 2); |
|
76 atkName = sAtkTextAttrNames[ATK_TEXT_ATTR_SIZE]; |
|
77 } else if (name.EqualsLiteral("font-weight")) { |
|
78 atkValue = value; |
|
79 atkName = sAtkTextAttrNames[ATK_TEXT_ATTR_WEIGHT]; |
|
80 } else if (name.EqualsLiteral("invalid")) { |
|
81 atkValue = value; |
|
82 atkName = sAtkTextAttrNames[ATK_TEXT_ATTR_INVALID]; |
|
83 } |
|
84 |
|
85 if (atkName) { |
|
86 objAttr = static_cast<AtkAttribute*>(g_malloc(sizeof(AtkAttribute))); |
|
87 objAttr->name = g_strdup(atkName); |
|
88 objAttr->value = g_strdup(NS_ConvertUTF16toUTF8(atkValue).get()); |
|
89 objAttributeSet = g_slist_prepend(objAttributeSet, objAttr); |
|
90 } |
|
91 } |
|
92 |
|
93 // libatk-adaptor will free it |
|
94 return objAttributeSet; |
|
95 } |
|
96 |
|
97 static void |
|
98 ConvertTexttoAsterisks(AccessibleWrap* accWrap, nsAString& aString) |
|
99 { |
|
100 // convert each char to "*" when it's "password text" |
|
101 if (accWrap->NativeRole() == roles::PASSWORD_TEXT) { |
|
102 for (uint32_t i = 0; i < aString.Length(); i++) |
|
103 aString.Replace(i, 1, NS_LITERAL_STRING("*")); |
|
104 } |
|
105 } |
|
106 |
|
107 extern "C" { |
|
108 |
|
109 static gchar* |
|
110 getTextCB(AtkText *aText, gint aStartOffset, gint aEndOffset) |
|
111 { |
|
112 AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText)); |
|
113 if (!accWrap) |
|
114 return nullptr; |
|
115 |
|
116 HyperTextAccessible* text = accWrap->AsHyperText(); |
|
117 if (!text || !text->IsTextRole()) |
|
118 return nullptr; |
|
119 |
|
120 nsAutoString autoStr; |
|
121 text->TextSubstring(aStartOffset, aEndOffset, autoStr); |
|
122 |
|
123 ConvertTexttoAsterisks(accWrap, autoStr); |
|
124 NS_ConvertUTF16toUTF8 cautoStr(autoStr); |
|
125 |
|
126 //copy and return, libspi will free it. |
|
127 return (cautoStr.get()) ? g_strdup(cautoStr.get()) : nullptr; |
|
128 } |
|
129 |
|
130 static gchar* |
|
131 getTextAfterOffsetCB(AtkText *aText, gint aOffset, |
|
132 AtkTextBoundary aBoundaryType, |
|
133 gint *aStartOffset, gint *aEndOffset) |
|
134 { |
|
135 AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText)); |
|
136 if (!accWrap) |
|
137 return nullptr; |
|
138 |
|
139 HyperTextAccessible* text = accWrap->AsHyperText(); |
|
140 if (!text || !text->IsTextRole()) |
|
141 return nullptr; |
|
142 |
|
143 nsAutoString autoStr; |
|
144 int32_t startOffset = 0, endOffset = 0; |
|
145 text->TextAfterOffset(aOffset, aBoundaryType, &startOffset, &endOffset, autoStr); |
|
146 |
|
147 *aStartOffset = startOffset; |
|
148 *aEndOffset = endOffset; |
|
149 |
|
150 ConvertTexttoAsterisks(accWrap, autoStr); |
|
151 NS_ConvertUTF16toUTF8 cautoStr(autoStr); |
|
152 return (cautoStr.get()) ? g_strdup(cautoStr.get()) : nullptr; |
|
153 } |
|
154 |
|
155 static gchar* |
|
156 getTextAtOffsetCB(AtkText *aText, gint aOffset, |
|
157 AtkTextBoundary aBoundaryType, |
|
158 gint *aStartOffset, gint *aEndOffset) |
|
159 { |
|
160 AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText)); |
|
161 if (!accWrap) |
|
162 return nullptr; |
|
163 |
|
164 HyperTextAccessible* text = accWrap->AsHyperText(); |
|
165 if (!text || !text->IsTextRole()) |
|
166 return nullptr; |
|
167 |
|
168 nsAutoString autoStr; |
|
169 int32_t startOffset = 0, endOffset = 0; |
|
170 text->TextAtOffset(aOffset, aBoundaryType, &startOffset, &endOffset, autoStr); |
|
171 *aStartOffset = startOffset; |
|
172 *aEndOffset = endOffset; |
|
173 |
|
174 ConvertTexttoAsterisks(accWrap, autoStr); |
|
175 NS_ConvertUTF16toUTF8 cautoStr(autoStr); |
|
176 return (cautoStr.get()) ? g_strdup(cautoStr.get()) : nullptr; |
|
177 } |
|
178 |
|
179 static gunichar |
|
180 getCharacterAtOffsetCB(AtkText* aText, gint aOffset) |
|
181 { |
|
182 AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText)); |
|
183 if (!accWrap) |
|
184 return 0; |
|
185 |
|
186 HyperTextAccessible* text = accWrap->AsHyperText(); |
|
187 if (!text || !text->IsTextRole()) |
|
188 return 0; |
|
189 |
|
190 // char16_t is unsigned short in Mozilla, gnuichar is guint32 in glib. |
|
191 return static_cast<gunichar>(text->CharAt(aOffset)); |
|
192 } |
|
193 |
|
194 static gchar* |
|
195 getTextBeforeOffsetCB(AtkText *aText, gint aOffset, |
|
196 AtkTextBoundary aBoundaryType, |
|
197 gint *aStartOffset, gint *aEndOffset) |
|
198 { |
|
199 AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText)); |
|
200 if (!accWrap) |
|
201 return nullptr; |
|
202 |
|
203 HyperTextAccessible* text = accWrap->AsHyperText(); |
|
204 if (!text || !text->IsTextRole()) |
|
205 return nullptr; |
|
206 |
|
207 nsAutoString autoStr; |
|
208 int32_t startOffset = 0, endOffset = 0; |
|
209 text->TextBeforeOffset(aOffset, aBoundaryType, |
|
210 &startOffset, &endOffset, autoStr); |
|
211 *aStartOffset = startOffset; |
|
212 *aEndOffset = endOffset; |
|
213 |
|
214 ConvertTexttoAsterisks(accWrap, autoStr); |
|
215 NS_ConvertUTF16toUTF8 cautoStr(autoStr); |
|
216 return (cautoStr.get()) ? g_strdup(cautoStr.get()) : nullptr; |
|
217 } |
|
218 |
|
219 static gint |
|
220 getCaretOffsetCB(AtkText *aText) |
|
221 { |
|
222 AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText)); |
|
223 if (!accWrap) |
|
224 return 0; |
|
225 |
|
226 HyperTextAccessible* text = accWrap->AsHyperText(); |
|
227 if (!text || !text->IsTextRole()) |
|
228 return 0; |
|
229 |
|
230 return static_cast<gint>(text->CaretOffset()); |
|
231 } |
|
232 |
|
233 static AtkAttributeSet* |
|
234 getRunAttributesCB(AtkText *aText, gint aOffset, |
|
235 gint *aStartOffset, |
|
236 gint *aEndOffset) |
|
237 { |
|
238 *aStartOffset = -1; |
|
239 *aEndOffset = -1; |
|
240 |
|
241 AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText)); |
|
242 if (!accWrap) |
|
243 return nullptr; |
|
244 |
|
245 HyperTextAccessible* text = accWrap->AsHyperText(); |
|
246 if (!text || !text->IsTextRole()) |
|
247 return nullptr; |
|
248 |
|
249 int32_t startOffset = 0, endOffset = 0; |
|
250 nsCOMPtr<nsIPersistentProperties> attributes = |
|
251 text->TextAttributes(false, aOffset, &startOffset, &endOffset); |
|
252 |
|
253 *aStartOffset = startOffset; |
|
254 *aEndOffset = endOffset; |
|
255 |
|
256 return ConvertToAtkTextAttributeSet(attributes); |
|
257 } |
|
258 |
|
259 static AtkAttributeSet* |
|
260 getDefaultAttributesCB(AtkText *aText) |
|
261 { |
|
262 AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText)); |
|
263 if (!accWrap) |
|
264 return nullptr; |
|
265 |
|
266 HyperTextAccessible* text = accWrap->AsHyperText(); |
|
267 if (!text || !text->IsTextRole()) |
|
268 return nullptr; |
|
269 |
|
270 nsCOMPtr<nsIPersistentProperties> attributes = text->DefaultTextAttributes(); |
|
271 return ConvertToAtkTextAttributeSet(attributes); |
|
272 } |
|
273 |
|
274 static void |
|
275 getCharacterExtentsCB(AtkText *aText, gint aOffset, |
|
276 gint *aX, gint *aY, |
|
277 gint *aWidth, gint *aHeight, |
|
278 AtkCoordType aCoords) |
|
279 { |
|
280 AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText)); |
|
281 if(!accWrap || !aX || !aY || !aWidth || !aHeight) |
|
282 return; |
|
283 |
|
284 HyperTextAccessible* text = accWrap->AsHyperText(); |
|
285 if (!text || !text->IsTextRole()) |
|
286 return; |
|
287 |
|
288 uint32_t geckoCoordType; |
|
289 if (aCoords == ATK_XY_SCREEN) |
|
290 geckoCoordType = nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE; |
|
291 else |
|
292 geckoCoordType = nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE; |
|
293 |
|
294 nsIntRect rect = text->CharBounds(aOffset, geckoCoordType); |
|
295 *aX = rect.x; |
|
296 *aY = rect.y; |
|
297 *aWidth = rect.width; |
|
298 *aHeight = rect.height; |
|
299 } |
|
300 |
|
301 static void |
|
302 getRangeExtentsCB(AtkText *aText, gint aStartOffset, gint aEndOffset, |
|
303 AtkCoordType aCoords, AtkTextRectangle *aRect) |
|
304 { |
|
305 AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText)); |
|
306 if(!accWrap || !aRect) |
|
307 return; |
|
308 |
|
309 HyperTextAccessible* text = accWrap->AsHyperText(); |
|
310 if (!text || !text->IsTextRole()) |
|
311 return; |
|
312 |
|
313 uint32_t geckoCoordType; |
|
314 if (aCoords == ATK_XY_SCREEN) |
|
315 geckoCoordType = nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE; |
|
316 else |
|
317 geckoCoordType = nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE; |
|
318 |
|
319 nsIntRect rect = text->TextBounds(aStartOffset, aEndOffset, geckoCoordType); |
|
320 aRect->x = rect.x; |
|
321 aRect->y = rect.y; |
|
322 aRect->width = rect.width; |
|
323 aRect->height = rect.height; |
|
324 } |
|
325 |
|
326 static gint |
|
327 getCharacterCountCB(AtkText *aText) |
|
328 { |
|
329 AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText)); |
|
330 if (!accWrap) |
|
331 return 0; |
|
332 |
|
333 HyperTextAccessible* textAcc = accWrap->AsHyperText(); |
|
334 return textAcc->IsDefunct() ? |
|
335 0 : static_cast<gint>(textAcc->CharacterCount()); |
|
336 } |
|
337 |
|
338 static gint |
|
339 getOffsetAtPointCB(AtkText *aText, |
|
340 gint aX, gint aY, |
|
341 AtkCoordType aCoords) |
|
342 { |
|
343 AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText)); |
|
344 if (!accWrap) |
|
345 return -1; |
|
346 |
|
347 HyperTextAccessible* text = accWrap->AsHyperText(); |
|
348 if (!text || !text->IsTextRole()) |
|
349 return -1; |
|
350 |
|
351 return static_cast<gint>( |
|
352 text->OffsetAtPoint(aX, aY, |
|
353 (aCoords == ATK_XY_SCREEN ? |
|
354 nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE : |
|
355 nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE))); |
|
356 } |
|
357 |
|
358 static gint |
|
359 getTextSelectionCountCB(AtkText *aText) |
|
360 { |
|
361 AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText)); |
|
362 if (!accWrap) |
|
363 return 0; |
|
364 |
|
365 HyperTextAccessible* text = accWrap->AsHyperText(); |
|
366 if (!text || !text->IsTextRole()) |
|
367 return 0; |
|
368 |
|
369 return text->SelectionCount(); |
|
370 } |
|
371 |
|
372 static gchar* |
|
373 getTextSelectionCB(AtkText *aText, gint aSelectionNum, |
|
374 gint *aStartOffset, gint *aEndOffset) |
|
375 { |
|
376 AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText)); |
|
377 if (!accWrap) |
|
378 return nullptr; |
|
379 |
|
380 HyperTextAccessible* text = accWrap->AsHyperText(); |
|
381 if (!text || !text->IsTextRole()) |
|
382 return nullptr; |
|
383 |
|
384 int32_t startOffset = 0, endOffset = 0; |
|
385 text->SelectionBoundsAt(aSelectionNum, &startOffset, &endOffset); |
|
386 |
|
387 *aStartOffset = startOffset; |
|
388 *aEndOffset = endOffset; |
|
389 |
|
390 return getTextCB(aText, *aStartOffset, *aEndOffset); |
|
391 } |
|
392 |
|
393 // set methods |
|
394 static gboolean |
|
395 addTextSelectionCB(AtkText *aText, |
|
396 gint aStartOffset, |
|
397 gint aEndOffset) |
|
398 { |
|
399 AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText)); |
|
400 if (!accWrap) |
|
401 return FALSE; |
|
402 |
|
403 HyperTextAccessible* text = accWrap->AsHyperText(); |
|
404 if (!text || !text->IsTextRole()) |
|
405 return FALSE; |
|
406 |
|
407 return text->AddToSelection(aStartOffset, aEndOffset); |
|
408 } |
|
409 |
|
410 static gboolean |
|
411 removeTextSelectionCB(AtkText *aText, |
|
412 gint aSelectionNum) |
|
413 { |
|
414 AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText)); |
|
415 if (!accWrap) |
|
416 return FALSE; |
|
417 |
|
418 HyperTextAccessible* text = accWrap->AsHyperText(); |
|
419 if (!text || !text->IsTextRole()) |
|
420 return FALSE; |
|
421 |
|
422 return text->RemoveFromSelection(aSelectionNum); |
|
423 } |
|
424 |
|
425 static gboolean |
|
426 setTextSelectionCB(AtkText *aText, gint aSelectionNum, |
|
427 gint aStartOffset, gint aEndOffset) |
|
428 { |
|
429 AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText)); |
|
430 if (!accWrap) |
|
431 return FALSE; |
|
432 |
|
433 HyperTextAccessible* text = accWrap->AsHyperText(); |
|
434 if (!text || !text->IsTextRole()) |
|
435 return FALSE; |
|
436 |
|
437 return text->SetSelectionBoundsAt(aSelectionNum, aStartOffset, aEndOffset); |
|
438 } |
|
439 |
|
440 static gboolean |
|
441 setCaretOffsetCB(AtkText *aText, gint aOffset) |
|
442 { |
|
443 AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText)); |
|
444 if (!accWrap) |
|
445 return FALSE; |
|
446 |
|
447 HyperTextAccessible* text = accWrap->AsHyperText(); |
|
448 if (!text || !text->IsTextRole() || !text->IsValidOffset(aOffset)) |
|
449 return FALSE; |
|
450 |
|
451 text->SetCaretOffset(aOffset); |
|
452 return TRUE; |
|
453 } |
|
454 } |
|
455 |
|
456 void |
|
457 textInterfaceInitCB(AtkTextIface* aIface) |
|
458 { |
|
459 NS_ASSERTION(aIface, "Invalid aIface"); |
|
460 if (MOZ_UNLIKELY(!aIface)) |
|
461 return; |
|
462 |
|
463 aIface->get_text = getTextCB; |
|
464 aIface->get_text_after_offset = getTextAfterOffsetCB; |
|
465 aIface->get_text_at_offset = getTextAtOffsetCB; |
|
466 aIface->get_character_at_offset = getCharacterAtOffsetCB; |
|
467 aIface->get_text_before_offset = getTextBeforeOffsetCB; |
|
468 aIface->get_caret_offset = getCaretOffsetCB; |
|
469 aIface->get_run_attributes = getRunAttributesCB; |
|
470 aIface->get_default_attributes = getDefaultAttributesCB; |
|
471 aIface->get_character_extents = getCharacterExtentsCB; |
|
472 aIface->get_range_extents = getRangeExtentsCB; |
|
473 aIface->get_character_count = getCharacterCountCB; |
|
474 aIface->get_offset_at_point = getOffsetAtPointCB; |
|
475 aIface->get_n_selections = getTextSelectionCountCB; |
|
476 aIface->get_selection = getTextSelectionCB; |
|
477 |
|
478 // set methods |
|
479 aIface->add_selection = addTextSelectionCB; |
|
480 aIface->remove_selection = removeTextSelectionCB; |
|
481 aIface->set_selection = setTextSelectionCB; |
|
482 aIface->set_caret_offset = setCaretOffsetCB; |
|
483 |
|
484 // Cache the string values of the atk text attribute names. |
|
485 for (uint32_t i = 0; i < ArrayLength(sAtkTextAttrNames); i++) |
|
486 sAtkTextAttrNames[i] = |
|
487 atk_text_attribute_get_name(static_cast<AtkTextAttribute>(i)); |
|
488 } |