accessible/src/generic/HyperTextAccessible.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set ts=2 sw=2 et tw=78: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "HyperTextAccessible-inl.h"
michael@0 8
michael@0 9 #include "Accessible-inl.h"
michael@0 10 #include "nsAccessibilityService.h"
michael@0 11 #include "nsIAccessibleTypes.h"
michael@0 12 #include "DocAccessible.h"
michael@0 13 #include "HTMLListAccessible.h"
michael@0 14 #include "Role.h"
michael@0 15 #include "States.h"
michael@0 16 #include "TextAttrs.h"
michael@0 17 #include "TextRange.h"
michael@0 18 #include "TreeWalker.h"
michael@0 19
michael@0 20 #include "nsCaret.h"
michael@0 21 #include "nsContentUtils.h"
michael@0 22 #include "nsFocusManager.h"
michael@0 23 #include "nsIDOMRange.h"
michael@0 24 #include "nsIEditingSession.h"
michael@0 25 #include "nsIFrame.h"
michael@0 26 #include "nsFrameSelection.h"
michael@0 27 #include "nsILineIterator.h"
michael@0 28 #include "nsIInterfaceRequestorUtils.h"
michael@0 29 #include "nsIPersistentProperties2.h"
michael@0 30 #include "nsIScrollableFrame.h"
michael@0 31 #include "nsIServiceManager.h"
michael@0 32 #include "nsITextControlElement.h"
michael@0 33 #include "nsTextFragment.h"
michael@0 34 #include "mozilla/dom/Element.h"
michael@0 35 #include "mozilla/EventStates.h"
michael@0 36 #include "mozilla/dom/Selection.h"
michael@0 37 #include "mozilla/MathAlgorithms.h"
michael@0 38 #include "gfxSkipChars.h"
michael@0 39 #include <algorithm>
michael@0 40
michael@0 41 using namespace mozilla;
michael@0 42 using namespace mozilla::a11y;
michael@0 43
michael@0 44 ////////////////////////////////////////////////////////////////////////////////
michael@0 45 // HyperTextAccessible
michael@0 46 ////////////////////////////////////////////////////////////////////////////////
michael@0 47
michael@0 48 HyperTextAccessible::
michael@0 49 HyperTextAccessible(nsIContent* aNode, DocAccessible* aDoc) :
michael@0 50 AccessibleWrap(aNode, aDoc), xpcAccessibleHyperText()
michael@0 51 {
michael@0 52 mGenericTypes |= eHyperText;
michael@0 53 }
michael@0 54
michael@0 55 nsresult
michael@0 56 HyperTextAccessible::QueryInterface(REFNSIID aIID, void** aInstancePtr)
michael@0 57 {
michael@0 58 xpcAccessibleHyperText::QueryInterface(aIID, aInstancePtr);
michael@0 59 return *aInstancePtr ? NS_OK : Accessible::QueryInterface(aIID, aInstancePtr);
michael@0 60 }
michael@0 61 NS_IMPL_ADDREF_INHERITED(HyperTextAccessible, AccessibleWrap)
michael@0 62 NS_IMPL_RELEASE_INHERITED(HyperTextAccessible, AccessibleWrap)
michael@0 63
michael@0 64 role
michael@0 65 HyperTextAccessible::NativeRole()
michael@0 66 {
michael@0 67 nsIAtom *tag = mContent->Tag();
michael@0 68
michael@0 69 if (tag == nsGkAtoms::dd)
michael@0 70 return roles::DEFINITION;
michael@0 71
michael@0 72 if (tag == nsGkAtoms::form)
michael@0 73 return roles::FORM;
michael@0 74
michael@0 75 if (tag == nsGkAtoms::blockquote || tag == nsGkAtoms::div ||
michael@0 76 tag == nsGkAtoms::section || tag == nsGkAtoms::nav)
michael@0 77 return roles::SECTION;
michael@0 78
michael@0 79 if (tag == nsGkAtoms::h1 || tag == nsGkAtoms::h2 ||
michael@0 80 tag == nsGkAtoms::h3 || tag == nsGkAtoms::h4 ||
michael@0 81 tag == nsGkAtoms::h5 || tag == nsGkAtoms::h6)
michael@0 82 return roles::HEADING;
michael@0 83
michael@0 84 if (tag == nsGkAtoms::article)
michael@0 85 return roles::DOCUMENT;
michael@0 86
michael@0 87 // Deal with html landmark elements
michael@0 88 if (tag == nsGkAtoms::header)
michael@0 89 return roles::HEADER;
michael@0 90
michael@0 91 if (tag == nsGkAtoms::footer)
michael@0 92 return roles::FOOTER;
michael@0 93
michael@0 94 if (tag == nsGkAtoms::aside)
michael@0 95 return roles::NOTE;
michael@0 96
michael@0 97 // Treat block frames as paragraphs
michael@0 98 nsIFrame *frame = GetFrame();
michael@0 99 if (frame && frame->GetType() == nsGkAtoms::blockFrame)
michael@0 100 return roles::PARAGRAPH;
michael@0 101
michael@0 102 return roles::TEXT_CONTAINER; // In ATK this works
michael@0 103 }
michael@0 104
michael@0 105 uint64_t
michael@0 106 HyperTextAccessible::NativeState()
michael@0 107 {
michael@0 108 uint64_t states = AccessibleWrap::NativeState();
michael@0 109
michael@0 110 if (mContent->AsElement()->State().HasState(NS_EVENT_STATE_MOZ_READWRITE)) {
michael@0 111 states |= states::EDITABLE;
michael@0 112
michael@0 113 } else if (mContent->Tag() == nsGkAtoms::article) {
michael@0 114 // We want <article> to behave like a document in terms of readonly state.
michael@0 115 states |= states::READONLY;
michael@0 116 }
michael@0 117
michael@0 118 if (HasChildren())
michael@0 119 states |= states::SELECTABLE_TEXT;
michael@0 120
michael@0 121 return states;
michael@0 122 }
michael@0 123
michael@0 124 nsIntRect
michael@0 125 HyperTextAccessible::GetBoundsInFrame(nsIFrame* aFrame,
michael@0 126 uint32_t aStartRenderedOffset,
michael@0 127 uint32_t aEndRenderedOffset)
michael@0 128 {
michael@0 129 nsPresContext* presContext = mDoc->PresContext();
michael@0 130 if (aFrame->GetType() != nsGkAtoms::textFrame) {
michael@0 131 return aFrame->GetScreenRectInAppUnits().
michael@0 132 ToNearestPixels(presContext->AppUnitsPerDevPixel());
michael@0 133 }
michael@0 134
michael@0 135 // Substring must be entirely within the same text node.
michael@0 136 int32_t startContentOffset, endContentOffset;
michael@0 137 nsresult rv = RenderedToContentOffset(aFrame, aStartRenderedOffset, &startContentOffset);
michael@0 138 NS_ENSURE_SUCCESS(rv, nsIntRect());
michael@0 139 rv = RenderedToContentOffset(aFrame, aEndRenderedOffset, &endContentOffset);
michael@0 140 NS_ENSURE_SUCCESS(rv, nsIntRect());
michael@0 141
michael@0 142 nsIFrame *frame;
michael@0 143 int32_t startContentOffsetInFrame;
michael@0 144 // Get the right frame continuation -- not really a child, but a sibling of
michael@0 145 // the primary frame passed in
michael@0 146 rv = aFrame->GetChildFrameContainingOffset(startContentOffset, false,
michael@0 147 &startContentOffsetInFrame, &frame);
michael@0 148 NS_ENSURE_SUCCESS(rv, nsIntRect());
michael@0 149
michael@0 150 nsRect screenRect;
michael@0 151 while (frame && startContentOffset < endContentOffset) {
michael@0 152 // Start with this frame's screen rect, which we will
michael@0 153 // shrink based on the substring we care about within it.
michael@0 154 // We will then add that frame to the total screenRect we
michael@0 155 // are returning.
michael@0 156 nsRect frameScreenRect = frame->GetScreenRectInAppUnits();
michael@0 157
michael@0 158 // Get the length of the substring in this frame that we want the bounds for
michael@0 159 int32_t startFrameTextOffset, endFrameTextOffset;
michael@0 160 frame->GetOffsets(startFrameTextOffset, endFrameTextOffset);
michael@0 161 int32_t frameTotalTextLength = endFrameTextOffset - startFrameTextOffset;
michael@0 162 int32_t seekLength = endContentOffset - startContentOffset;
michael@0 163 int32_t frameSubStringLength = std::min(frameTotalTextLength - startContentOffsetInFrame, seekLength);
michael@0 164
michael@0 165 // Add the point where the string starts to the frameScreenRect
michael@0 166 nsPoint frameTextStartPoint;
michael@0 167 rv = frame->GetPointFromOffset(startContentOffset, &frameTextStartPoint);
michael@0 168 NS_ENSURE_SUCCESS(rv, nsIntRect());
michael@0 169
michael@0 170 // Use the point for the end offset to calculate the width
michael@0 171 nsPoint frameTextEndPoint;
michael@0 172 rv = frame->GetPointFromOffset(startContentOffset + frameSubStringLength, &frameTextEndPoint);
michael@0 173 NS_ENSURE_SUCCESS(rv, nsIntRect());
michael@0 174
michael@0 175 frameScreenRect.x += std::min(frameTextStartPoint.x, frameTextEndPoint.x);
michael@0 176 frameScreenRect.width = mozilla::Abs(frameTextStartPoint.x - frameTextEndPoint.x);
michael@0 177
michael@0 178 screenRect.UnionRect(frameScreenRect, screenRect);
michael@0 179
michael@0 180 // Get ready to loop back for next frame continuation
michael@0 181 startContentOffset += frameSubStringLength;
michael@0 182 startContentOffsetInFrame = 0;
michael@0 183 frame = frame->GetNextContinuation();
michael@0 184 }
michael@0 185
michael@0 186 return screenRect.ToNearestPixels(presContext->AppUnitsPerDevPixel());
michael@0 187 }
michael@0 188
michael@0 189 void
michael@0 190 HyperTextAccessible::TextSubstring(int32_t aStartOffset, int32_t aEndOffset,
michael@0 191 nsAString& aText)
michael@0 192 {
michael@0 193 aText.Truncate();
michael@0 194
michael@0 195 int32_t startOffset = ConvertMagicOffset(aStartOffset);
michael@0 196 int32_t endOffset = ConvertMagicOffset(aEndOffset);
michael@0 197
michael@0 198 int32_t startChildIdx = GetChildIndexAtOffset(startOffset);
michael@0 199 if (startChildIdx == -1)
michael@0 200 return;
michael@0 201
michael@0 202 int32_t endChildIdx = GetChildIndexAtOffset(endOffset);
michael@0 203 if (endChildIdx == -1)
michael@0 204 return;
michael@0 205
michael@0 206 if (startChildIdx == endChildIdx) {
michael@0 207 int32_t childOffset = GetChildOffset(startChildIdx);
michael@0 208 if (childOffset == -1)
michael@0 209 return;
michael@0 210
michael@0 211 Accessible* child = GetChildAt(startChildIdx);
michael@0 212 child->AppendTextTo(aText, startOffset - childOffset,
michael@0 213 endOffset - startOffset);
michael@0 214 return;
michael@0 215 }
michael@0 216
michael@0 217 int32_t startChildOffset = GetChildOffset(startChildIdx);
michael@0 218 if (startChildOffset == -1)
michael@0 219 return;
michael@0 220
michael@0 221 Accessible* startChild = GetChildAt(startChildIdx);
michael@0 222 startChild->AppendTextTo(aText, startOffset - startChildOffset);
michael@0 223
michael@0 224 for (int32_t childIdx = startChildIdx + 1; childIdx < endChildIdx; childIdx++) {
michael@0 225 Accessible* child = GetChildAt(childIdx);
michael@0 226 child->AppendTextTo(aText);
michael@0 227 }
michael@0 228
michael@0 229 int32_t endChildOffset = GetChildOffset(endChildIdx);
michael@0 230 if (endChildOffset == -1)
michael@0 231 return;
michael@0 232
michael@0 233 Accessible* endChild = GetChildAt(endChildIdx);
michael@0 234 endChild->AppendTextTo(aText, 0, endOffset - endChildOffset);
michael@0 235 }
michael@0 236
michael@0 237 int32_t
michael@0 238 HyperTextAccessible::DOMPointToOffset(nsINode* aNode, int32_t aNodeOffset,
michael@0 239 bool aIsEndOffset) const
michael@0 240 {
michael@0 241 if (!aNode)
michael@0 242 return 0;
michael@0 243
michael@0 244 uint32_t offset = 0;
michael@0 245 nsINode* findNode = nullptr;
michael@0 246
michael@0 247 if (aNodeOffset == -1) {
michael@0 248 findNode = aNode;
michael@0 249
michael@0 250 } else if (aNode->IsNodeOfType(nsINode::eTEXT)) {
michael@0 251 // For text nodes, aNodeOffset comes in as a character offset
michael@0 252 // Text offset will be added at the end, if we find the offset in this hypertext
michael@0 253 // We want the "skipped" offset into the text (rendered text without the extra whitespace)
michael@0 254 nsIFrame* frame = aNode->AsContent()->GetPrimaryFrame();
michael@0 255 NS_ENSURE_TRUE(frame, 0);
michael@0 256
michael@0 257 nsresult rv = ContentToRenderedOffset(frame, aNodeOffset, &offset);
michael@0 258 NS_ENSURE_SUCCESS(rv, 0);
michael@0 259 // Get the child node and
michael@0 260 findNode = aNode;
michael@0 261
michael@0 262 } else {
michael@0 263 // findNode could be null if aNodeOffset == # of child nodes, which means
michael@0 264 // one of two things:
michael@0 265 // 1) there are no children, and the passed-in node is not mContent -- use
michael@0 266 // parentContent for the node to find
michael@0 267 // 2) there are no children and the passed-in node is mContent, which means
michael@0 268 // we're an empty nsIAccessibleText
michael@0 269 // 3) there are children and we're at the end of the children
michael@0 270
michael@0 271 findNode = aNode->GetChildAt(aNodeOffset);
michael@0 272 if (!findNode) {
michael@0 273 if (aNodeOffset == 0) {
michael@0 274 if (aNode == GetNode()) {
michael@0 275 // Case #1: this accessible has no children and thus has empty text,
michael@0 276 // we can only be at hypertext offset 0.
michael@0 277 return 0;
michael@0 278 }
michael@0 279
michael@0 280 // Case #2: there are no children, we're at this node.
michael@0 281 findNode = aNode;
michael@0 282 } else if (aNodeOffset == aNode->GetChildCount()) {
michael@0 283 // Case #3: we're after the last child, get next node to this one.
michael@0 284 for (nsINode* tmpNode = aNode;
michael@0 285 !findNode && tmpNode && tmpNode != mContent;
michael@0 286 tmpNode = tmpNode->GetParent()) {
michael@0 287 findNode = tmpNode->GetNextSibling();
michael@0 288 }
michael@0 289 }
michael@0 290 }
michael@0 291 }
michael@0 292
michael@0 293 // Get accessible for this findNode, or if that node isn't accessible, use the
michael@0 294 // accessible for the next DOM node which has one (based on forward depth first search)
michael@0 295 Accessible* descendant = nullptr;
michael@0 296 if (findNode) {
michael@0 297 nsCOMPtr<nsIContent> findContent(do_QueryInterface(findNode));
michael@0 298 if (findContent && findContent->IsHTML() &&
michael@0 299 findContent->NodeInfo()->Equals(nsGkAtoms::br) &&
michael@0 300 findContent->AttrValueIs(kNameSpaceID_None,
michael@0 301 nsGkAtoms::mozeditorbogusnode,
michael@0 302 nsGkAtoms::_true,
michael@0 303 eIgnoreCase)) {
michael@0 304 // This <br> is the hacky "bogus node" used when there is no text in a control
michael@0 305 return 0;
michael@0 306 }
michael@0 307
michael@0 308 descendant = mDoc->GetAccessible(findNode);
michael@0 309 if (!descendant && findNode->IsContent()) {
michael@0 310 Accessible* container = mDoc->GetContainerAccessible(findNode);
michael@0 311 if (container) {
michael@0 312 TreeWalker walker(container, findNode->AsContent(),
michael@0 313 TreeWalker::eWalkContextTree);
michael@0 314 descendant = walker.NextChild();
michael@0 315 }
michael@0 316 }
michael@0 317 }
michael@0 318
michael@0 319 return TransformOffset(descendant, offset, aIsEndOffset);
michael@0 320 }
michael@0 321
michael@0 322 int32_t
michael@0 323 HyperTextAccessible::TransformOffset(Accessible* aDescendant,
michael@0 324 int32_t aOffset, bool aIsEndOffset) const
michael@0 325 {
michael@0 326 // From the descendant, go up and get the immediate child of this hypertext.
michael@0 327 int32_t offset = aOffset;
michael@0 328 Accessible* descendant = aDescendant;
michael@0 329 while (descendant) {
michael@0 330 Accessible* parent = descendant->Parent();
michael@0 331 if (parent == this)
michael@0 332 return GetChildOffset(descendant) + offset;
michael@0 333
michael@0 334 // This offset no longer applies because the passed-in text object is not
michael@0 335 // a child of the hypertext. This happens when there are nested hypertexts,
michael@0 336 // e.g. <div>abc<h1>def</h1>ghi</div>. Thus we need to adjust the offset
michael@0 337 // to make it relative the hypertext.
michael@0 338 // If the end offset is not supposed to be inclusive and the original point
michael@0 339 // is not at 0 offset then the returned offset should be after an embedded
michael@0 340 // character the original point belongs to.
michael@0 341 if (aIsEndOffset)
michael@0 342 offset = (offset > 0 || descendant->IndexInParent() > 0) ? 1 : 0;
michael@0 343 else
michael@0 344 offset = 0;
michael@0 345
michael@0 346 descendant = parent;
michael@0 347 }
michael@0 348
michael@0 349 // If the given a11y point cannot be mapped into offset relative this hypertext
michael@0 350 // offset then return length as fallback value.
michael@0 351 return CharacterCount();
michael@0 352 }
michael@0 353
michael@0 354 bool
michael@0 355 HyperTextAccessible::OffsetsToDOMRange(int32_t aStartOffset, int32_t aEndOffset,
michael@0 356 nsRange* aRange)
michael@0 357 {
michael@0 358 DOMPoint startPoint = OffsetToDOMPoint(aStartOffset);
michael@0 359 if (!startPoint.node)
michael@0 360 return false;
michael@0 361
michael@0 362 aRange->SetStart(startPoint.node, startPoint.idx);
michael@0 363 if (aStartOffset == aEndOffset) {
michael@0 364 aRange->SetEnd(startPoint.node, startPoint.idx);
michael@0 365 return true;
michael@0 366 }
michael@0 367
michael@0 368 DOMPoint endPoint = OffsetToDOMPoint(aEndOffset);
michael@0 369 if (!endPoint.node)
michael@0 370 return false;
michael@0 371
michael@0 372 aRange->SetEnd(endPoint.node, endPoint.idx);
michael@0 373 return true;
michael@0 374 }
michael@0 375
michael@0 376 DOMPoint
michael@0 377 HyperTextAccessible::OffsetToDOMPoint(int32_t aOffset)
michael@0 378 {
michael@0 379 // 0 offset is valid even if no children. In this case the associated editor
michael@0 380 // is empty so return a DOM point for editor root element.
michael@0 381 if (aOffset == 0) {
michael@0 382 nsCOMPtr<nsIEditor> editor = GetEditor();
michael@0 383 if (editor) {
michael@0 384 bool isEmpty = false;
michael@0 385 editor->GetDocumentIsEmpty(&isEmpty);
michael@0 386 if (isEmpty) {
michael@0 387 nsCOMPtr<nsIDOMElement> editorRootElm;
michael@0 388 editor->GetRootElement(getter_AddRefs(editorRootElm));
michael@0 389
michael@0 390 nsCOMPtr<nsINode> editorRoot(do_QueryInterface(editorRootElm));
michael@0 391 return DOMPoint(editorRoot, 0);
michael@0 392 }
michael@0 393 }
michael@0 394 }
michael@0 395
michael@0 396 int32_t childIdx = GetChildIndexAtOffset(aOffset);
michael@0 397 if (childIdx == -1)
michael@0 398 return DOMPoint();
michael@0 399
michael@0 400 Accessible* child = GetChildAt(childIdx);
michael@0 401 int32_t innerOffset = aOffset - GetChildOffset(childIdx);
michael@0 402
michael@0 403 // A text leaf case. The point is inside the text node.
michael@0 404 if (child->IsTextLeaf()) {
michael@0 405 nsIContent* content = child->GetContent();
michael@0 406 int32_t idx = 0;
michael@0 407 if (NS_FAILED(RenderedToContentOffset(content->GetPrimaryFrame(),
michael@0 408 innerOffset, &idx)))
michael@0 409 return DOMPoint();
michael@0 410
michael@0 411 return DOMPoint(content, idx);
michael@0 412 }
michael@0 413
michael@0 414 // Case of embedded object. The point is either before or after the element.
michael@0 415 NS_ASSERTION(innerOffset == 0 || innerOffset == 1, "A wrong inner offset!");
michael@0 416 nsINode* node = child->GetNode();
michael@0 417 nsINode* parentNode = node->GetParentNode();
michael@0 418 return parentNode ?
michael@0 419 DOMPoint(parentNode, parentNode->IndexOf(node) + innerOffset) :
michael@0 420 DOMPoint();
michael@0 421 }
michael@0 422
michael@0 423 int32_t
michael@0 424 HyperTextAccessible::FindOffset(int32_t aOffset, nsDirection aDirection,
michael@0 425 nsSelectionAmount aAmount,
michael@0 426 EWordMovementType aWordMovementType)
michael@0 427 {
michael@0 428 // Find a leaf accessible frame to start with. PeekOffset wants this.
michael@0 429 HyperTextAccessible* text = this;
michael@0 430 Accessible* child = nullptr;
michael@0 431 int32_t innerOffset = aOffset;
michael@0 432
michael@0 433 do {
michael@0 434 int32_t childIdx = text->GetChildIndexAtOffset(innerOffset);
michael@0 435 NS_ASSERTION(childIdx != -1, "Bad in offset!");
michael@0 436 if (childIdx == -1)
michael@0 437 return -1;
michael@0 438
michael@0 439 child = text->GetChildAt(childIdx);
michael@0 440
michael@0 441 // HTML list items may need special processing because PeekOffset doesn't
michael@0 442 // work with list bullets.
michael@0 443 if (text->IsHTMLListItem()) {
michael@0 444 HTMLLIAccessible* li = text->AsHTMLListItem();
michael@0 445 if (child == li->Bullet()) {
michael@0 446 // It works only when the bullet is one single char.
michael@0 447 if (aDirection == eDirPrevious)
michael@0 448 return text != this ? TransformOffset(text, 0, false) : 0;
michael@0 449
michael@0 450 if (aAmount == eSelectEndLine || aAmount == eSelectLine) {
michael@0 451 if (text != this)
michael@0 452 return TransformOffset(text, 1, true);
michael@0 453
michael@0 454 // Ask a text leaf next (if not empty) to the bullet for an offset
michael@0 455 // since list item may be multiline.
michael@0 456 return aOffset + 1 < CharacterCount() ?
michael@0 457 FindOffset(aOffset + 1, aDirection, aAmount, aWordMovementType) : 1;
michael@0 458 }
michael@0 459
michael@0 460 // Case of word and char boundaries.
michael@0 461 return text != this ? TransformOffset(text, 1, true) : 1;
michael@0 462 }
michael@0 463 }
michael@0 464
michael@0 465 innerOffset -= text->GetChildOffset(childIdx);
michael@0 466
michael@0 467 text = child->AsHyperText();
michael@0 468 } while (text);
michael@0 469
michael@0 470 nsIFrame* childFrame = child->GetFrame();
michael@0 471 NS_ENSURE_TRUE(childFrame, -1);
michael@0 472
michael@0 473 int32_t innerContentOffset = innerOffset;
michael@0 474 if (child->IsTextLeaf()) {
michael@0 475 NS_ASSERTION(childFrame->GetType() == nsGkAtoms::textFrame, "Wrong frame!");
michael@0 476 RenderedToContentOffset(childFrame, innerOffset, &innerContentOffset);
michael@0 477 }
michael@0 478
michael@0 479 nsIFrame* frameAtOffset = childFrame;
michael@0 480 int32_t unusedOffsetInFrame = 0;
michael@0 481 childFrame->GetChildFrameContainingOffset(innerContentOffset, true,
michael@0 482 &unusedOffsetInFrame,
michael@0 483 &frameAtOffset);
michael@0 484
michael@0 485 const bool kIsJumpLinesOk = true; // okay to jump lines
michael@0 486 const bool kIsScrollViewAStop = false; // do not stop at scroll views
michael@0 487 const bool kIsKeyboardSelect = true; // is keyboard selection
michael@0 488 const bool kIsVisualBidi = false; // use visual order for bidi text
michael@0 489 nsPeekOffsetStruct pos(aAmount, aDirection, innerContentOffset,
michael@0 490 0, kIsJumpLinesOk, kIsScrollViewAStop,
michael@0 491 kIsKeyboardSelect, kIsVisualBidi,
michael@0 492 aWordMovementType);
michael@0 493 nsresult rv = frameAtOffset->PeekOffset(&pos);
michael@0 494
michael@0 495 // PeekOffset fails on last/first lines of the text in certain cases.
michael@0 496 if (NS_FAILED(rv) && aAmount == eSelectLine) {
michael@0 497 pos.mAmount = (aDirection == eDirNext) ? eSelectEndLine : eSelectBeginLine;
michael@0 498 frameAtOffset->PeekOffset(&pos);
michael@0 499 }
michael@0 500 if (!pos.mResultContent)
michael@0 501 return -1;
michael@0 502
michael@0 503 // Turn the resulting DOM point into an offset.
michael@0 504 int32_t hyperTextOffset = DOMPointToOffset(pos.mResultContent,
michael@0 505 pos.mContentOffset,
michael@0 506 aDirection == eDirNext);
michael@0 507
michael@0 508 if (aDirection == eDirPrevious) {
michael@0 509 // If we reached the end during search, this means we didn't find the DOM point
michael@0 510 // and we're actually at the start of the paragraph
michael@0 511 if (hyperTextOffset == CharacterCount())
michael@0 512 return 0;
michael@0 513
michael@0 514 // PeekOffset stops right before bullet so return 0 to workaround it.
michael@0 515 if (IsHTMLListItem() && aAmount == eSelectBeginLine && hyperTextOffset == 1)
michael@0 516 return 0;
michael@0 517 }
michael@0 518
michael@0 519 return hyperTextOffset;
michael@0 520 }
michael@0 521
michael@0 522 int32_t
michael@0 523 HyperTextAccessible::FindLineBoundary(int32_t aOffset,
michael@0 524 EWhichLineBoundary aWhichLineBoundary)
michael@0 525 {
michael@0 526 // Note: empty last line doesn't have own frame (a previous line contains '\n'
michael@0 527 // character instead) thus when it makes a difference we need to process this
michael@0 528 // case separately (otherwise operations are performed on previous line).
michael@0 529 switch (aWhichLineBoundary) {
michael@0 530 case ePrevLineBegin: {
michael@0 531 // Fetch a previous line and move to its start (as arrow up and home keys
michael@0 532 // were pressed).
michael@0 533 if (IsEmptyLastLineOffset(aOffset))
michael@0 534 return FindOffset(aOffset, eDirPrevious, eSelectBeginLine);
michael@0 535
michael@0 536 int32_t tmpOffset = FindOffset(aOffset, eDirPrevious, eSelectLine);
michael@0 537 return FindOffset(tmpOffset, eDirPrevious, eSelectBeginLine);
michael@0 538 }
michael@0 539
michael@0 540 case ePrevLineEnd: {
michael@0 541 if (IsEmptyLastLineOffset(aOffset))
michael@0 542 return aOffset - 1;
michael@0 543
michael@0 544 // If offset is at first line then return 0 (first line start).
michael@0 545 int32_t tmpOffset = FindOffset(aOffset, eDirPrevious, eSelectBeginLine);
michael@0 546 if (tmpOffset == 0)
michael@0 547 return 0;
michael@0 548
michael@0 549 // Otherwise move to end of previous line (as arrow up and end keys were
michael@0 550 // pressed).
michael@0 551 tmpOffset = FindOffset(aOffset, eDirPrevious, eSelectLine);
michael@0 552 return FindOffset(tmpOffset, eDirNext, eSelectEndLine);
michael@0 553 }
michael@0 554
michael@0 555 case eThisLineBegin:
michael@0 556 if (IsEmptyLastLineOffset(aOffset))
michael@0 557 return aOffset;
michael@0 558
michael@0 559 // Move to begin of the current line (as home key was pressed).
michael@0 560 return FindOffset(aOffset, eDirPrevious, eSelectBeginLine);
michael@0 561
michael@0 562 case eThisLineEnd:
michael@0 563 if (IsEmptyLastLineOffset(aOffset))
michael@0 564 return aOffset;
michael@0 565
michael@0 566 // Move to end of the current line (as end key was pressed).
michael@0 567 return FindOffset(aOffset, eDirNext, eSelectEndLine);
michael@0 568
michael@0 569 case eNextLineBegin: {
michael@0 570 if (IsEmptyLastLineOffset(aOffset))
michael@0 571 return aOffset;
michael@0 572
michael@0 573 // Move to begin of the next line if any (arrow down and home keys),
michael@0 574 // otherwise end of the current line (arrow down only).
michael@0 575 int32_t tmpOffset = FindOffset(aOffset, eDirNext, eSelectLine);
michael@0 576 if (tmpOffset == CharacterCount())
michael@0 577 return tmpOffset;
michael@0 578
michael@0 579 return FindOffset(tmpOffset, eDirPrevious, eSelectBeginLine);
michael@0 580 }
michael@0 581
michael@0 582 case eNextLineEnd: {
michael@0 583 if (IsEmptyLastLineOffset(aOffset))
michael@0 584 return aOffset;
michael@0 585
michael@0 586 // Move to next line end (as down arrow and end key were pressed).
michael@0 587 int32_t tmpOffset = FindOffset(aOffset, eDirNext, eSelectLine);
michael@0 588 if (tmpOffset != CharacterCount())
michael@0 589 return FindOffset(tmpOffset, eDirNext, eSelectEndLine);
michael@0 590 return tmpOffset;
michael@0 591 }
michael@0 592 }
michael@0 593
michael@0 594 return -1;
michael@0 595 }
michael@0 596
michael@0 597 void
michael@0 598 HyperTextAccessible::TextBeforeOffset(int32_t aOffset,
michael@0 599 AccessibleTextBoundary aBoundaryType,
michael@0 600 int32_t* aStartOffset, int32_t* aEndOffset,
michael@0 601 nsAString& aText)
michael@0 602 {
michael@0 603 *aStartOffset = *aEndOffset = 0;
michael@0 604 aText.Truncate();
michael@0 605
michael@0 606 int32_t convertedOffset = ConvertMagicOffset(aOffset);
michael@0 607 if (convertedOffset < 0) {
michael@0 608 NS_ERROR("Wrong given offset!");
michael@0 609 return;
michael@0 610 }
michael@0 611
michael@0 612 int32_t adjustedOffset = convertedOffset;
michael@0 613 if (aOffset == nsIAccessibleText::TEXT_OFFSET_CARET)
michael@0 614 adjustedOffset = AdjustCaretOffset(adjustedOffset);
michael@0 615
michael@0 616 switch (aBoundaryType) {
michael@0 617 case BOUNDARY_CHAR:
michael@0 618 if (convertedOffset != 0)
michael@0 619 CharAt(convertedOffset - 1, aText, aStartOffset, aEndOffset);
michael@0 620 break;
michael@0 621
michael@0 622 case BOUNDARY_WORD_START: {
michael@0 623 // If the offset is a word start (except text length offset) then move
michael@0 624 // backward to find a start offset (end offset is the given offset).
michael@0 625 // Otherwise move backward twice to find both start and end offsets.
michael@0 626 if (adjustedOffset == CharacterCount()) {
michael@0 627 *aEndOffset = FindWordBoundary(adjustedOffset, eDirPrevious, eStartWord);
michael@0 628 *aStartOffset = FindWordBoundary(*aEndOffset, eDirPrevious, eStartWord);
michael@0 629 } else {
michael@0 630 *aStartOffset = FindWordBoundary(adjustedOffset, eDirPrevious, eStartWord);
michael@0 631 *aEndOffset = FindWordBoundary(*aStartOffset, eDirNext, eStartWord);
michael@0 632 if (*aEndOffset != adjustedOffset) {
michael@0 633 *aEndOffset = *aStartOffset;
michael@0 634 *aStartOffset = FindWordBoundary(*aEndOffset, eDirPrevious, eStartWord);
michael@0 635 }
michael@0 636 }
michael@0 637 TextSubstring(*aStartOffset, *aEndOffset, aText);
michael@0 638 break;
michael@0 639 }
michael@0 640
michael@0 641 case BOUNDARY_WORD_END: {
michael@0 642 // Move word backward twice to find start and end offsets.
michael@0 643 *aEndOffset = FindWordBoundary(convertedOffset, eDirPrevious, eEndWord);
michael@0 644 *aStartOffset = FindWordBoundary(*aEndOffset, eDirPrevious, eEndWord);
michael@0 645 TextSubstring(*aStartOffset, *aEndOffset, aText);
michael@0 646 break;
michael@0 647 }
michael@0 648
michael@0 649 case BOUNDARY_LINE_START:
michael@0 650 *aStartOffset = FindLineBoundary(adjustedOffset, ePrevLineBegin);
michael@0 651 *aEndOffset = FindLineBoundary(adjustedOffset, eThisLineBegin);
michael@0 652 TextSubstring(*aStartOffset, *aEndOffset, aText);
michael@0 653 break;
michael@0 654
michael@0 655 case BOUNDARY_LINE_END: {
michael@0 656 *aEndOffset = FindLineBoundary(adjustedOffset, ePrevLineEnd);
michael@0 657 int32_t tmpOffset = *aEndOffset;
michael@0 658 // Adjust offset if line is wrapped.
michael@0 659 if (*aEndOffset != 0 && !IsLineEndCharAt(*aEndOffset))
michael@0 660 tmpOffset--;
michael@0 661
michael@0 662 *aStartOffset = FindLineBoundary(tmpOffset, ePrevLineEnd);
michael@0 663 TextSubstring(*aStartOffset, *aEndOffset, aText);
michael@0 664 break;
michael@0 665 }
michael@0 666 }
michael@0 667 }
michael@0 668
michael@0 669 void
michael@0 670 HyperTextAccessible::TextAtOffset(int32_t aOffset,
michael@0 671 AccessibleTextBoundary aBoundaryType,
michael@0 672 int32_t* aStartOffset, int32_t* aEndOffset,
michael@0 673 nsAString& aText)
michael@0 674 {
michael@0 675 *aStartOffset = *aEndOffset = 0;
michael@0 676 aText.Truncate();
michael@0 677
michael@0 678 int32_t adjustedOffset = ConvertMagicOffset(aOffset);
michael@0 679 if (adjustedOffset < 0) {
michael@0 680 NS_ERROR("Wrong given offset!");
michael@0 681 return;
michael@0 682 }
michael@0 683
michael@0 684 switch (aBoundaryType) {
michael@0 685 case BOUNDARY_CHAR:
michael@0 686 // Return no char if caret is at the end of wrapped line (case of no line
michael@0 687 // end character). Returning a next line char is confusing for AT.
michael@0 688 if (aOffset == nsIAccessibleText::TEXT_OFFSET_CARET && IsCaretAtEndOfLine())
michael@0 689 *aStartOffset = *aEndOffset = adjustedOffset;
michael@0 690 else
michael@0 691 CharAt(adjustedOffset, aText, aStartOffset, aEndOffset);
michael@0 692 break;
michael@0 693
michael@0 694 case BOUNDARY_WORD_START:
michael@0 695 if (aOffset == nsIAccessibleText::TEXT_OFFSET_CARET)
michael@0 696 adjustedOffset = AdjustCaretOffset(adjustedOffset);
michael@0 697
michael@0 698 *aEndOffset = FindWordBoundary(adjustedOffset, eDirNext, eStartWord);
michael@0 699 *aStartOffset = FindWordBoundary(*aEndOffset, eDirPrevious, eStartWord);
michael@0 700 TextSubstring(*aStartOffset, *aEndOffset, aText);
michael@0 701 break;
michael@0 702
michael@0 703 case BOUNDARY_WORD_END:
michael@0 704 // Ignore the spec and follow what WebKitGtk does because Orca expects it,
michael@0 705 // i.e. return a next word at word end offset of the current word
michael@0 706 // (WebKitGtk behavior) instead the current word (AKT spec).
michael@0 707 *aEndOffset = FindWordBoundary(adjustedOffset, eDirNext, eEndWord);
michael@0 708 *aStartOffset = FindWordBoundary(*aEndOffset, eDirPrevious, eEndWord);
michael@0 709 TextSubstring(*aStartOffset, *aEndOffset, aText);
michael@0 710 break;
michael@0 711
michael@0 712 case BOUNDARY_LINE_START:
michael@0 713 if (aOffset == nsIAccessibleText::TEXT_OFFSET_CARET)
michael@0 714 adjustedOffset = AdjustCaretOffset(adjustedOffset);
michael@0 715
michael@0 716 *aStartOffset = FindLineBoundary(adjustedOffset, eThisLineBegin);
michael@0 717 *aEndOffset = FindLineBoundary(adjustedOffset, eNextLineBegin);
michael@0 718 TextSubstring(*aStartOffset, *aEndOffset, aText);
michael@0 719 break;
michael@0 720
michael@0 721 case BOUNDARY_LINE_END:
michael@0 722 if (aOffset == nsIAccessibleText::TEXT_OFFSET_CARET)
michael@0 723 adjustedOffset = AdjustCaretOffset(adjustedOffset);
michael@0 724
michael@0 725 // In contrast to word end boundary we follow the spec here.
michael@0 726 *aStartOffset = FindLineBoundary(adjustedOffset, ePrevLineEnd);
michael@0 727 *aEndOffset = FindLineBoundary(adjustedOffset, eThisLineEnd);
michael@0 728 TextSubstring(*aStartOffset, *aEndOffset, aText);
michael@0 729 break;
michael@0 730 }
michael@0 731 }
michael@0 732
michael@0 733 void
michael@0 734 HyperTextAccessible::TextAfterOffset(int32_t aOffset,
michael@0 735 AccessibleTextBoundary aBoundaryType,
michael@0 736 int32_t* aStartOffset, int32_t* aEndOffset,
michael@0 737 nsAString& aText)
michael@0 738 {
michael@0 739 *aStartOffset = *aEndOffset = 0;
michael@0 740 aText.Truncate();
michael@0 741
michael@0 742 int32_t convertedOffset = ConvertMagicOffset(aOffset);
michael@0 743 if (convertedOffset < 0) {
michael@0 744 NS_ERROR("Wrong given offset!");
michael@0 745 return;
michael@0 746 }
michael@0 747
michael@0 748 int32_t adjustedOffset = convertedOffset;
michael@0 749 if (aOffset == nsIAccessibleText::TEXT_OFFSET_CARET)
michael@0 750 adjustedOffset = AdjustCaretOffset(adjustedOffset);
michael@0 751
michael@0 752 switch (aBoundaryType) {
michael@0 753 case BOUNDARY_CHAR:
michael@0 754 // If caret is at the end of wrapped line (case of no line end character)
michael@0 755 // then char after the offset is a first char at next line.
michael@0 756 if (adjustedOffset >= CharacterCount())
michael@0 757 *aStartOffset = *aEndOffset = CharacterCount();
michael@0 758 else
michael@0 759 CharAt(adjustedOffset + 1, aText, aStartOffset, aEndOffset);
michael@0 760 break;
michael@0 761
michael@0 762 case BOUNDARY_WORD_START:
michael@0 763 // Move word forward twice to find start and end offsets.
michael@0 764 *aStartOffset = FindWordBoundary(adjustedOffset, eDirNext, eStartWord);
michael@0 765 *aEndOffset = FindWordBoundary(*aStartOffset, eDirNext, eStartWord);
michael@0 766 TextSubstring(*aStartOffset, *aEndOffset, aText);
michael@0 767 break;
michael@0 768
michael@0 769 case BOUNDARY_WORD_END:
michael@0 770 // If the offset is a word end (except 0 offset) then move forward to find
michael@0 771 // end offset (start offset is the given offset). Otherwise move forward
michael@0 772 // twice to find both start and end offsets.
michael@0 773 if (convertedOffset == 0) {
michael@0 774 *aStartOffset = FindWordBoundary(convertedOffset, eDirNext, eEndWord);
michael@0 775 *aEndOffset = FindWordBoundary(*aStartOffset, eDirNext, eEndWord);
michael@0 776 } else {
michael@0 777 *aEndOffset = FindWordBoundary(convertedOffset, eDirNext, eEndWord);
michael@0 778 *aStartOffset = FindWordBoundary(*aEndOffset, eDirPrevious, eEndWord);
michael@0 779 if (*aStartOffset != convertedOffset) {
michael@0 780 *aStartOffset = *aEndOffset;
michael@0 781 *aEndOffset = FindWordBoundary(*aStartOffset, eDirNext, eEndWord);
michael@0 782 }
michael@0 783 }
michael@0 784 TextSubstring(*aStartOffset, *aEndOffset, aText);
michael@0 785 break;
michael@0 786
michael@0 787 case BOUNDARY_LINE_START:
michael@0 788 *aStartOffset = FindLineBoundary(adjustedOffset, eNextLineBegin);
michael@0 789 *aEndOffset = FindLineBoundary(*aStartOffset, eNextLineBegin);
michael@0 790 TextSubstring(*aStartOffset, *aEndOffset, aText);
michael@0 791 break;
michael@0 792
michael@0 793 case BOUNDARY_LINE_END:
michael@0 794 *aStartOffset = FindLineBoundary(adjustedOffset, eThisLineEnd);
michael@0 795 *aEndOffset = FindLineBoundary(adjustedOffset, eNextLineEnd);
michael@0 796 TextSubstring(*aStartOffset, *aEndOffset, aText);
michael@0 797 break;
michael@0 798 }
michael@0 799 }
michael@0 800
michael@0 801 already_AddRefed<nsIPersistentProperties>
michael@0 802 HyperTextAccessible::TextAttributes(bool aIncludeDefAttrs, int32_t aOffset,
michael@0 803 int32_t* aStartOffset,
michael@0 804 int32_t* aEndOffset)
michael@0 805 {
michael@0 806 // 1. Get each attribute and its ranges one after another.
michael@0 807 // 2. As we get each new attribute, we pass the current start and end offsets
michael@0 808 // as in/out parameters. In other words, as attributes are collected,
michael@0 809 // the attribute range itself can only stay the same or get smaller.
michael@0 810
michael@0 811 *aStartOffset = *aEndOffset = 0;
michael@0 812 nsCOMPtr<nsIPersistentProperties> attributes =
michael@0 813 do_CreateInstance(NS_PERSISTENTPROPERTIES_CONTRACTID);
michael@0 814
michael@0 815 int32_t offset = ConvertMagicOffset(aOffset);
michael@0 816 Accessible* accAtOffset = GetChildAtOffset(offset);
michael@0 817 if (!accAtOffset) {
michael@0 818 // Offset 0 is correct offset when accessible has empty text. Include
michael@0 819 // default attributes if they were requested, otherwise return empty set.
michael@0 820 if (offset == 0) {
michael@0 821 if (aIncludeDefAttrs) {
michael@0 822 TextAttrsMgr textAttrsMgr(this);
michael@0 823 textAttrsMgr.GetAttributes(attributes);
michael@0 824 }
michael@0 825 return attributes.forget();
michael@0 826 }
michael@0 827 return nullptr;
michael@0 828 }
michael@0 829
michael@0 830 int32_t accAtOffsetIdx = accAtOffset->IndexInParent();
michael@0 831 int32_t startOffset = GetChildOffset(accAtOffsetIdx);
michael@0 832 int32_t endOffset = GetChildOffset(accAtOffsetIdx + 1);
michael@0 833 int32_t offsetInAcc = offset - startOffset;
michael@0 834
michael@0 835 TextAttrsMgr textAttrsMgr(this, aIncludeDefAttrs, accAtOffset,
michael@0 836 accAtOffsetIdx);
michael@0 837 textAttrsMgr.GetAttributes(attributes, &startOffset, &endOffset);
michael@0 838
michael@0 839 // Compute spelling attributes on text accessible only.
michael@0 840 nsIFrame *offsetFrame = accAtOffset->GetFrame();
michael@0 841 if (offsetFrame && offsetFrame->GetType() == nsGkAtoms::textFrame) {
michael@0 842 int32_t nodeOffset = 0;
michael@0 843 RenderedToContentOffset(offsetFrame, offsetInAcc, &nodeOffset);
michael@0 844
michael@0 845 // Set 'misspelled' text attribute.
michael@0 846 GetSpellTextAttribute(accAtOffset->GetNode(), nodeOffset,
michael@0 847 &startOffset, &endOffset, attributes);
michael@0 848 }
michael@0 849
michael@0 850 *aStartOffset = startOffset;
michael@0 851 *aEndOffset = endOffset;
michael@0 852 return attributes.forget();
michael@0 853 }
michael@0 854
michael@0 855 already_AddRefed<nsIPersistentProperties>
michael@0 856 HyperTextAccessible::DefaultTextAttributes()
michael@0 857 {
michael@0 858 nsCOMPtr<nsIPersistentProperties> attributes =
michael@0 859 do_CreateInstance(NS_PERSISTENTPROPERTIES_CONTRACTID);
michael@0 860
michael@0 861 TextAttrsMgr textAttrsMgr(this);
michael@0 862 textAttrsMgr.GetAttributes(attributes);
michael@0 863 return attributes.forget();
michael@0 864 }
michael@0 865
michael@0 866 int32_t
michael@0 867 HyperTextAccessible::GetLevelInternal()
michael@0 868 {
michael@0 869 nsIAtom *tag = mContent->Tag();
michael@0 870 if (tag == nsGkAtoms::h1)
michael@0 871 return 1;
michael@0 872 if (tag == nsGkAtoms::h2)
michael@0 873 return 2;
michael@0 874 if (tag == nsGkAtoms::h3)
michael@0 875 return 3;
michael@0 876 if (tag == nsGkAtoms::h4)
michael@0 877 return 4;
michael@0 878 if (tag == nsGkAtoms::h5)
michael@0 879 return 5;
michael@0 880 if (tag == nsGkAtoms::h6)
michael@0 881 return 6;
michael@0 882
michael@0 883 return AccessibleWrap::GetLevelInternal();
michael@0 884 }
michael@0 885
michael@0 886 already_AddRefed<nsIPersistentProperties>
michael@0 887 HyperTextAccessible::NativeAttributes()
michael@0 888 {
michael@0 889 nsCOMPtr<nsIPersistentProperties> attributes =
michael@0 890 AccessibleWrap::NativeAttributes();
michael@0 891
michael@0 892 // 'formatting' attribute is deprecated, 'display' attribute should be
michael@0 893 // instead.
michael@0 894 nsIFrame *frame = GetFrame();
michael@0 895 if (frame && frame->GetType() == nsGkAtoms::blockFrame) {
michael@0 896 nsAutoString unused;
michael@0 897 attributes->SetStringProperty(NS_LITERAL_CSTRING("formatting"),
michael@0 898 NS_LITERAL_STRING("block"), unused);
michael@0 899 }
michael@0 900
michael@0 901 if (FocusMgr()->IsFocused(this)) {
michael@0 902 int32_t lineNumber = CaretLineNumber();
michael@0 903 if (lineNumber >= 1) {
michael@0 904 nsAutoString strLineNumber;
michael@0 905 strLineNumber.AppendInt(lineNumber);
michael@0 906 nsAccUtils::SetAccAttr(attributes, nsGkAtoms::lineNumber, strLineNumber);
michael@0 907 }
michael@0 908 }
michael@0 909
michael@0 910 if (!HasOwnContent())
michael@0 911 return attributes.forget();
michael@0 912
michael@0 913 // For the html landmark elements we expose them like we do aria landmarks to
michael@0 914 // make AT navigation schemes "just work".
michael@0 915 nsIAtom* tag = mContent->Tag();
michael@0 916 if (tag == nsGkAtoms::nav) {
michael@0 917 nsAccUtils::SetAccAttr(attributes, nsGkAtoms::xmlroles,
michael@0 918 NS_LITERAL_STRING("navigation"));
michael@0 919 } else if (tag == nsGkAtoms::section) {
michael@0 920 nsAccUtils::SetAccAttr(attributes, nsGkAtoms::xmlroles,
michael@0 921 NS_LITERAL_STRING("region"));
michael@0 922 } else if (tag == nsGkAtoms::header || tag == nsGkAtoms::footer) {
michael@0 923 // Only map header and footer if they are not descendants
michael@0 924 // of an article or section tag.
michael@0 925 nsIContent* parent = mContent->GetParent();
michael@0 926 while (parent) {
michael@0 927 if (parent->Tag() == nsGkAtoms::article ||
michael@0 928 parent->Tag() == nsGkAtoms::section)
michael@0 929 break;
michael@0 930 parent = parent->GetParent();
michael@0 931 }
michael@0 932
michael@0 933 // No article or section elements found.
michael@0 934 if (!parent) {
michael@0 935 if (tag == nsGkAtoms::header) {
michael@0 936 nsAccUtils::SetAccAttr(attributes, nsGkAtoms::xmlroles,
michael@0 937 NS_LITERAL_STRING("banner"));
michael@0 938 } else if (tag == nsGkAtoms::footer) {
michael@0 939 nsAccUtils::SetAccAttr(attributes, nsGkAtoms::xmlroles,
michael@0 940 NS_LITERAL_STRING("contentinfo"));
michael@0 941 }
michael@0 942 }
michael@0 943 } else if (tag == nsGkAtoms::aside) {
michael@0 944 nsAccUtils::SetAccAttr(attributes, nsGkAtoms::xmlroles,
michael@0 945 NS_LITERAL_STRING("complementary"));
michael@0 946 } else if (tag == nsGkAtoms::article) {
michael@0 947 nsAccUtils::SetAccAttr(attributes, nsGkAtoms::xmlroles,
michael@0 948 NS_LITERAL_STRING("article"));
michael@0 949 } else if (tag == nsGkAtoms::main) {
michael@0 950 nsAccUtils::SetAccAttr(attributes, nsGkAtoms::xmlroles,
michael@0 951 NS_LITERAL_STRING("main"));
michael@0 952 }
michael@0 953
michael@0 954 return attributes.forget();
michael@0 955 }
michael@0 956
michael@0 957 int32_t
michael@0 958 HyperTextAccessible::OffsetAtPoint(int32_t aX, int32_t aY, uint32_t aCoordType)
michael@0 959 {
michael@0 960 nsIFrame* hyperFrame = GetFrame();
michael@0 961 if (!hyperFrame)
michael@0 962 return -1;
michael@0 963
michael@0 964 nsIntPoint coords = nsAccUtils::ConvertToScreenCoords(aX, aY, aCoordType,
michael@0 965 this);
michael@0 966
michael@0 967 nsPresContext* presContext = mDoc->PresContext();
michael@0 968 nsPoint coordsInAppUnits =
michael@0 969 coords.ToAppUnits(presContext->AppUnitsPerDevPixel());
michael@0 970
michael@0 971 nsRect frameScreenRect = hyperFrame->GetScreenRectInAppUnits();
michael@0 972 if (!frameScreenRect.Contains(coordsInAppUnits.x, coordsInAppUnits.y))
michael@0 973 return -1; // Not found
michael@0 974
michael@0 975 nsPoint pointInHyperText(coordsInAppUnits.x - frameScreenRect.x,
michael@0 976 coordsInAppUnits.y - frameScreenRect.y);
michael@0 977
michael@0 978 // Go through the frames to check if each one has the point.
michael@0 979 // When one does, add up the character offsets until we have a match
michael@0 980
michael@0 981 // We have an point in an accessible child of this, now we need to add up the
michael@0 982 // offsets before it to what we already have
michael@0 983 int32_t offset = 0;
michael@0 984 uint32_t childCount = ChildCount();
michael@0 985 for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
michael@0 986 Accessible* childAcc = mChildren[childIdx];
michael@0 987
michael@0 988 nsIFrame *primaryFrame = childAcc->GetFrame();
michael@0 989 NS_ENSURE_TRUE(primaryFrame, -1);
michael@0 990
michael@0 991 nsIFrame *frame = primaryFrame;
michael@0 992 while (frame) {
michael@0 993 nsIContent *content = frame->GetContent();
michael@0 994 NS_ENSURE_TRUE(content, -1);
michael@0 995 nsPoint pointInFrame = pointInHyperText - frame->GetOffsetTo(hyperFrame);
michael@0 996 nsSize frameSize = frame->GetSize();
michael@0 997 if (pointInFrame.x < frameSize.width && pointInFrame.y < frameSize.height) {
michael@0 998 // Finished
michael@0 999 if (frame->GetType() == nsGkAtoms::textFrame) {
michael@0 1000 nsIFrame::ContentOffsets contentOffsets =
michael@0 1001 frame->GetContentOffsetsFromPointExternal(pointInFrame, nsIFrame::IGNORE_SELECTION_STYLE);
michael@0 1002 if (contentOffsets.IsNull() || contentOffsets.content != content) {
michael@0 1003 return -1; // Not found
michael@0 1004 }
michael@0 1005 uint32_t addToOffset;
michael@0 1006 nsresult rv = ContentToRenderedOffset(primaryFrame,
michael@0 1007 contentOffsets.offset,
michael@0 1008 &addToOffset);
michael@0 1009 NS_ENSURE_SUCCESS(rv, -1);
michael@0 1010 offset += addToOffset;
michael@0 1011 }
michael@0 1012 return offset;
michael@0 1013 }
michael@0 1014 frame = frame->GetNextContinuation();
michael@0 1015 }
michael@0 1016
michael@0 1017 offset += nsAccUtils::TextLength(childAcc);
michael@0 1018 }
michael@0 1019
michael@0 1020 return -1; // Not found
michael@0 1021 }
michael@0 1022
michael@0 1023 nsIntRect
michael@0 1024 HyperTextAccessible::TextBounds(int32_t aStartOffset, int32_t aEndOffset,
michael@0 1025 uint32_t aCoordType)
michael@0 1026 {
michael@0 1027 int32_t startOffset = ConvertMagicOffset(aStartOffset);
michael@0 1028 int32_t endOffset = ConvertMagicOffset(aEndOffset);
michael@0 1029 NS_ASSERTION(startOffset < endOffset, "Wrong bad in!");
michael@0 1030
michael@0 1031 int32_t childIdx = GetChildIndexAtOffset(startOffset);
michael@0 1032 if (childIdx == -1)
michael@0 1033 return nsIntRect();
michael@0 1034
michael@0 1035 nsIntRect bounds;
michael@0 1036 int32_t prevOffset = GetChildOffset(childIdx);
michael@0 1037 int32_t offset1 = startOffset - prevOffset;
michael@0 1038
michael@0 1039 while (childIdx < ChildCount()) {
michael@0 1040 nsIFrame* frame = GetChildAt(childIdx++)->GetFrame();
michael@0 1041 if (!frame) {
michael@0 1042 NS_NOTREACHED("No frame for a child!");
michael@0 1043 continue;
michael@0 1044 }
michael@0 1045
michael@0 1046 int32_t nextOffset = GetChildOffset(childIdx);
michael@0 1047 if (nextOffset >= endOffset) {
michael@0 1048 bounds.UnionRect(bounds, GetBoundsInFrame(frame, offset1,
michael@0 1049 endOffset - prevOffset));
michael@0 1050 break;
michael@0 1051 }
michael@0 1052
michael@0 1053 bounds.UnionRect(bounds, GetBoundsInFrame(frame, offset1,
michael@0 1054 nextOffset - prevOffset));
michael@0 1055
michael@0 1056 prevOffset = nextOffset;
michael@0 1057 offset1 = 0;
michael@0 1058 }
michael@0 1059
michael@0 1060 nsAccUtils::ConvertScreenCoordsTo(&bounds.x, &bounds.y, aCoordType, this);
michael@0 1061 return bounds;
michael@0 1062 }
michael@0 1063
michael@0 1064 already_AddRefed<nsIEditor>
michael@0 1065 HyperTextAccessible::GetEditor() const
michael@0 1066 {
michael@0 1067 if (!mContent->HasFlag(NODE_IS_EDITABLE)) {
michael@0 1068 // If we're inside an editable container, then return that container's editor
michael@0 1069 Accessible* ancestor = Parent();
michael@0 1070 while (ancestor) {
michael@0 1071 HyperTextAccessible* hyperText = ancestor->AsHyperText();
michael@0 1072 if (hyperText) {
michael@0 1073 // Recursion will stop at container doc because it has its own impl
michael@0 1074 // of GetEditor()
michael@0 1075 return hyperText->GetEditor();
michael@0 1076 }
michael@0 1077
michael@0 1078 ancestor = ancestor->Parent();
michael@0 1079 }
michael@0 1080
michael@0 1081 return nullptr;
michael@0 1082 }
michael@0 1083
michael@0 1084 nsCOMPtr<nsIDocShell> docShell = nsCoreUtils::GetDocShellFor(mContent);
michael@0 1085 nsCOMPtr<nsIEditingSession> editingSession(do_GetInterface(docShell));
michael@0 1086 if (!editingSession)
michael@0 1087 return nullptr; // No editing session interface
michael@0 1088
michael@0 1089 nsCOMPtr<nsIEditor> editor;
michael@0 1090 nsIDocument* docNode = mDoc->DocumentNode();
michael@0 1091 editingSession->GetEditorForWindow(docNode->GetWindow(),
michael@0 1092 getter_AddRefs(editor));
michael@0 1093 return editor.forget();
michael@0 1094 }
michael@0 1095
michael@0 1096 /**
michael@0 1097 * =================== Caret & Selection ======================
michael@0 1098 */
michael@0 1099
michael@0 1100 nsresult
michael@0 1101 HyperTextAccessible::SetSelectionRange(int32_t aStartPos, int32_t aEndPos)
michael@0 1102 {
michael@0 1103 // Before setting the selection range, we need to ensure that the editor
michael@0 1104 // is initialized. (See bug 804927.)
michael@0 1105 // Otherwise, it's possible that lazy editor initialization will override
michael@0 1106 // the selection we set here and leave the caret at the end of the text.
michael@0 1107 // By calling GetEditor here, we ensure that editor initialization is
michael@0 1108 // completed before we set the selection.
michael@0 1109 nsCOMPtr<nsIEditor> editor = GetEditor();
michael@0 1110
michael@0 1111 bool isFocusable = InteractiveState() & states::FOCUSABLE;
michael@0 1112
michael@0 1113 // If accessible is focusable then focus it before setting the selection to
michael@0 1114 // neglect control's selection changes on focus if any (for example, inputs
michael@0 1115 // that do select all on focus).
michael@0 1116 // some input controls
michael@0 1117 if (isFocusable)
michael@0 1118 TakeFocus();
michael@0 1119
michael@0 1120 dom::Selection* domSel = DOMSelection();
michael@0 1121 NS_ENSURE_STATE(domSel);
michael@0 1122
michael@0 1123 // Set up the selection.
michael@0 1124 for (int32_t idx = domSel->GetRangeCount() - 1; idx > 0; idx--)
michael@0 1125 domSel->RemoveRange(domSel->GetRangeAt(idx));
michael@0 1126 SetSelectionBoundsAt(0, aStartPos, aEndPos);
michael@0 1127
michael@0 1128 // When selection is done, move the focus to the selection if accessible is
michael@0 1129 // not focusable. That happens when selection is set within hypertext
michael@0 1130 // accessible.
michael@0 1131 if (isFocusable)
michael@0 1132 return NS_OK;
michael@0 1133
michael@0 1134 nsFocusManager* DOMFocusManager = nsFocusManager::GetFocusManager();
michael@0 1135 if (DOMFocusManager) {
michael@0 1136 NS_ENSURE_TRUE(mDoc, NS_ERROR_FAILURE);
michael@0 1137 nsIDocument* docNode = mDoc->DocumentNode();
michael@0 1138 NS_ENSURE_TRUE(docNode, NS_ERROR_FAILURE);
michael@0 1139 nsCOMPtr<nsPIDOMWindow> window = docNode->GetWindow();
michael@0 1140 nsCOMPtr<nsIDOMElement> result;
michael@0 1141 DOMFocusManager->MoveFocus(window, nullptr, nsIFocusManager::MOVEFOCUS_CARET,
michael@0 1142 nsIFocusManager::FLAG_BYMOVEFOCUS, getter_AddRefs(result));
michael@0 1143 }
michael@0 1144
michael@0 1145 return NS_OK;
michael@0 1146 }
michael@0 1147
michael@0 1148 int32_t
michael@0 1149 HyperTextAccessible::CaretOffset() const
michael@0 1150 {
michael@0 1151 // Not focused focusable accessible except document accessible doesn't have
michael@0 1152 // a caret.
michael@0 1153 if (!IsDoc() && !FocusMgr()->IsFocused(this) &&
michael@0 1154 (InteractiveState() & states::FOCUSABLE)) {
michael@0 1155 return -1;
michael@0 1156 }
michael@0 1157
michael@0 1158 // No caret if the focused node is not inside this DOM node and this DOM node
michael@0 1159 // is not inside of focused node.
michael@0 1160 FocusManager::FocusDisposition focusDisp =
michael@0 1161 FocusMgr()->IsInOrContainsFocus(this);
michael@0 1162 if (focusDisp == FocusManager::eNone)
michael@0 1163 return -1;
michael@0 1164
michael@0 1165 // Turn the focus node and offset of the selection into caret hypretext
michael@0 1166 // offset.
michael@0 1167 dom::Selection* domSel = DOMSelection();
michael@0 1168 NS_ENSURE_TRUE(domSel, -1);
michael@0 1169
michael@0 1170 nsINode* focusNode = domSel->GetFocusNode();
michael@0 1171 uint32_t focusOffset = domSel->FocusOffset();
michael@0 1172
michael@0 1173 // No caret if this DOM node is inside of focused node but the selection's
michael@0 1174 // focus point is not inside of this DOM node.
michael@0 1175 if (focusDisp == FocusManager::eContainedByFocus) {
michael@0 1176 nsINode* resultNode =
michael@0 1177 nsCoreUtils::GetDOMNodeFromDOMPoint(focusNode, focusOffset);
michael@0 1178
michael@0 1179 nsINode* thisNode = GetNode();
michael@0 1180 if (resultNode != thisNode &&
michael@0 1181 !nsCoreUtils::IsAncestorOf(thisNode, resultNode))
michael@0 1182 return -1;
michael@0 1183 }
michael@0 1184
michael@0 1185 return DOMPointToOffset(focusNode, focusOffset);
michael@0 1186 }
michael@0 1187
michael@0 1188 int32_t
michael@0 1189 HyperTextAccessible::CaretLineNumber()
michael@0 1190 {
michael@0 1191 // Provide the line number for the caret, relative to the
michael@0 1192 // currently focused node. Use a 1-based index
michael@0 1193 nsRefPtr<nsFrameSelection> frameSelection = FrameSelection();
michael@0 1194 if (!frameSelection)
michael@0 1195 return -1;
michael@0 1196
michael@0 1197 dom::Selection* domSel =
michael@0 1198 frameSelection->GetSelection(nsISelectionController::SELECTION_NORMAL);
michael@0 1199 if (!domSel)
michael@0 1200 return - 1;
michael@0 1201
michael@0 1202 nsINode* caretNode = domSel->GetFocusNode();
michael@0 1203 if (!caretNode || !caretNode->IsContent())
michael@0 1204 return -1;
michael@0 1205
michael@0 1206 nsIContent* caretContent = caretNode->AsContent();
michael@0 1207 if (!nsCoreUtils::IsAncestorOf(GetNode(), caretContent))
michael@0 1208 return -1;
michael@0 1209
michael@0 1210 int32_t returnOffsetUnused;
michael@0 1211 uint32_t caretOffset = domSel->FocusOffset();
michael@0 1212 nsFrameSelection::HINT hint = frameSelection->GetHint();
michael@0 1213 nsIFrame *caretFrame = frameSelection->GetFrameForNodeOffset(caretContent, caretOffset,
michael@0 1214 hint, &returnOffsetUnused);
michael@0 1215 NS_ENSURE_TRUE(caretFrame, -1);
michael@0 1216
michael@0 1217 int32_t lineNumber = 1;
michael@0 1218 nsAutoLineIterator lineIterForCaret;
michael@0 1219 nsIContent *hyperTextContent = IsContent() ? mContent.get() : nullptr;
michael@0 1220 while (caretFrame) {
michael@0 1221 if (hyperTextContent == caretFrame->GetContent()) {
michael@0 1222 return lineNumber; // Must be in a single line hyper text, there is no line iterator
michael@0 1223 }
michael@0 1224 nsIFrame *parentFrame = caretFrame->GetParent();
michael@0 1225 if (!parentFrame)
michael@0 1226 break;
michael@0 1227
michael@0 1228 // Add lines for the sibling frames before the caret
michael@0 1229 nsIFrame *sibling = parentFrame->GetFirstPrincipalChild();
michael@0 1230 while (sibling && sibling != caretFrame) {
michael@0 1231 nsAutoLineIterator lineIterForSibling = sibling->GetLineIterator();
michael@0 1232 if (lineIterForSibling) {
michael@0 1233 // For the frames before that grab all the lines
michael@0 1234 int32_t addLines = lineIterForSibling->GetNumLines();
michael@0 1235 lineNumber += addLines;
michael@0 1236 }
michael@0 1237 sibling = sibling->GetNextSibling();
michael@0 1238 }
michael@0 1239
michael@0 1240 // Get the line number relative to the container with lines
michael@0 1241 if (!lineIterForCaret) { // Add the caret line just once
michael@0 1242 lineIterForCaret = parentFrame->GetLineIterator();
michael@0 1243 if (lineIterForCaret) {
michael@0 1244 // Ancestor of caret
michael@0 1245 int32_t addLines = lineIterForCaret->FindLineContaining(caretFrame);
michael@0 1246 lineNumber += addLines;
michael@0 1247 }
michael@0 1248 }
michael@0 1249
michael@0 1250 caretFrame = parentFrame;
michael@0 1251 }
michael@0 1252
michael@0 1253 NS_NOTREACHED("DOM ancestry had this hypertext but frame ancestry didn't");
michael@0 1254 return lineNumber;
michael@0 1255 }
michael@0 1256
michael@0 1257 nsIntRect
michael@0 1258 HyperTextAccessible::GetCaretRect(nsIWidget** aWidget)
michael@0 1259 {
michael@0 1260 *aWidget = nullptr;
michael@0 1261
michael@0 1262 nsRefPtr<nsCaret> caret = mDoc->PresShell()->GetCaret();
michael@0 1263 NS_ENSURE_TRUE(caret, nsIntRect());
michael@0 1264
michael@0 1265 nsISelection* caretSelection = caret->GetCaretDOMSelection();
michael@0 1266 NS_ENSURE_TRUE(caretSelection, nsIntRect());
michael@0 1267
michael@0 1268 bool isVisible = false;
michael@0 1269 caret->GetCaretVisible(&isVisible);
michael@0 1270 if (!isVisible)
michael@0 1271 return nsIntRect();
michael@0 1272
michael@0 1273 nsRect rect;
michael@0 1274 nsIFrame* frame = caret->GetGeometry(caretSelection, &rect);
michael@0 1275 if (!frame || rect.IsEmpty())
michael@0 1276 return nsIntRect();
michael@0 1277
michael@0 1278 nsPoint offset;
michael@0 1279 // Offset from widget origin to the frame origin, which includes chrome
michael@0 1280 // on the widget.
michael@0 1281 *aWidget = frame->GetNearestWidget(offset);
michael@0 1282 NS_ENSURE_TRUE(*aWidget, nsIntRect());
michael@0 1283 rect.MoveBy(offset);
michael@0 1284
michael@0 1285 nsIntRect caretRect;
michael@0 1286 caretRect = rect.ToOutsidePixels(frame->PresContext()->AppUnitsPerDevPixel());
michael@0 1287 // ((content screen origin) - (content offset in the widget)) = widget origin on the screen
michael@0 1288 caretRect.MoveBy((*aWidget)->WidgetToScreenOffset() - (*aWidget)->GetClientOffset());
michael@0 1289
michael@0 1290 // Correct for character size, so that caret always matches the size of
michael@0 1291 // the character. This is important for font size transitions, and is
michael@0 1292 // necessary because the Gecko caret uses the previous character's size as
michael@0 1293 // the user moves forward in the text by character.
michael@0 1294 nsIntRect charRect = CharBounds(CaretOffset(),
michael@0 1295 nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE);
michael@0 1296 if (!charRect.IsEmpty()) {
michael@0 1297 caretRect.height -= charRect.y - caretRect.y;
michael@0 1298 caretRect.y = charRect.y;
michael@0 1299 }
michael@0 1300 return caretRect;
michael@0 1301 }
michael@0 1302
michael@0 1303 void
michael@0 1304 HyperTextAccessible::GetSelectionDOMRanges(int16_t aType,
michael@0 1305 nsTArray<nsRange*>* aRanges)
michael@0 1306 {
michael@0 1307 // Ignore selection if it is not visible.
michael@0 1308 nsRefPtr<nsFrameSelection> frameSelection = FrameSelection();
michael@0 1309 if (!frameSelection ||
michael@0 1310 frameSelection->GetDisplaySelection() <= nsISelectionController::SELECTION_HIDDEN)
michael@0 1311 return;
michael@0 1312
michael@0 1313 dom::Selection* domSel = frameSelection->GetSelection(aType);
michael@0 1314 if (!domSel)
michael@0 1315 return;
michael@0 1316
michael@0 1317 nsCOMPtr<nsINode> startNode = GetNode();
michael@0 1318
michael@0 1319 nsCOMPtr<nsIEditor> editor = GetEditor();
michael@0 1320 if (editor) {
michael@0 1321 nsCOMPtr<nsIDOMElement> editorRoot;
michael@0 1322 editor->GetRootElement(getter_AddRefs(editorRoot));
michael@0 1323 startNode = do_QueryInterface(editorRoot);
michael@0 1324 }
michael@0 1325
michael@0 1326 if (!startNode)
michael@0 1327 return;
michael@0 1328
michael@0 1329 uint32_t childCount = startNode->GetChildCount();
michael@0 1330 nsresult rv = domSel->
michael@0 1331 GetRangesForIntervalArray(startNode, 0, startNode, childCount, true, aRanges);
michael@0 1332 NS_ENSURE_SUCCESS_VOID(rv);
michael@0 1333
michael@0 1334 // Remove collapsed ranges
michael@0 1335 uint32_t numRanges = aRanges->Length();
michael@0 1336 for (uint32_t idx = 0; idx < numRanges; idx ++) {
michael@0 1337 if ((*aRanges)[idx]->Collapsed()) {
michael@0 1338 aRanges->RemoveElementAt(idx);
michael@0 1339 --numRanges;
michael@0 1340 --idx;
michael@0 1341 }
michael@0 1342 }
michael@0 1343 }
michael@0 1344
michael@0 1345 int32_t
michael@0 1346 HyperTextAccessible::SelectionCount()
michael@0 1347 {
michael@0 1348 nsTArray<nsRange*> ranges;
michael@0 1349 GetSelectionDOMRanges(nsISelectionController::SELECTION_NORMAL, &ranges);
michael@0 1350 return ranges.Length();
michael@0 1351 }
michael@0 1352
michael@0 1353 bool
michael@0 1354 HyperTextAccessible::SelectionBoundsAt(int32_t aSelectionNum,
michael@0 1355 int32_t* aStartOffset,
michael@0 1356 int32_t* aEndOffset)
michael@0 1357 {
michael@0 1358 *aStartOffset = *aEndOffset = 0;
michael@0 1359
michael@0 1360 nsTArray<nsRange*> ranges;
michael@0 1361 GetSelectionDOMRanges(nsISelectionController::SELECTION_NORMAL, &ranges);
michael@0 1362
michael@0 1363 uint32_t rangeCount = ranges.Length();
michael@0 1364 if (aSelectionNum < 0 || aSelectionNum >= rangeCount)
michael@0 1365 return false;
michael@0 1366
michael@0 1367 nsRange* range = ranges[aSelectionNum];
michael@0 1368
michael@0 1369 // Get start and end points.
michael@0 1370 nsINode* startNode = range->GetStartParent();
michael@0 1371 nsINode* endNode = range->GetEndParent();
michael@0 1372 int32_t startOffset = range->StartOffset(), endOffset = range->EndOffset();
michael@0 1373
michael@0 1374 // Make sure start is before end, by swapping DOM points. This occurs when
michael@0 1375 // the user selects backwards in the text.
michael@0 1376 int32_t rangeCompare = nsContentUtils::ComparePoints(endNode, endOffset,
michael@0 1377 startNode, startOffset);
michael@0 1378 if (rangeCompare < 0) {
michael@0 1379 nsINode* tempNode = startNode;
michael@0 1380 startNode = endNode;
michael@0 1381 endNode = tempNode;
michael@0 1382 int32_t tempOffset = startOffset;
michael@0 1383 startOffset = endOffset;
michael@0 1384 endOffset = tempOffset;
michael@0 1385 }
michael@0 1386
michael@0 1387 *aStartOffset = DOMPointToOffset(startNode, startOffset);
michael@0 1388 *aEndOffset = DOMPointToOffset(endNode, endOffset, true);
michael@0 1389 return true;
michael@0 1390 }
michael@0 1391
michael@0 1392 bool
michael@0 1393 HyperTextAccessible::SetSelectionBoundsAt(int32_t aSelectionNum,
michael@0 1394 int32_t aStartOffset,
michael@0 1395 int32_t aEndOffset)
michael@0 1396 {
michael@0 1397 int32_t startOffset = ConvertMagicOffset(aStartOffset);
michael@0 1398 int32_t endOffset = ConvertMagicOffset(aEndOffset);
michael@0 1399
michael@0 1400 dom::Selection* domSel = DOMSelection();
michael@0 1401 if (!domSel)
michael@0 1402 return false;
michael@0 1403
michael@0 1404 nsRefPtr<nsRange> range;
michael@0 1405 uint32_t rangeCount = domSel->GetRangeCount();
michael@0 1406 if (aSelectionNum == rangeCount)
michael@0 1407 range = new nsRange(mContent);
michael@0 1408 else
michael@0 1409 range = domSel->GetRangeAt(aSelectionNum);
michael@0 1410
michael@0 1411 if (!range)
michael@0 1412 return false;
michael@0 1413
michael@0 1414 if (!OffsetsToDOMRange(startOffset, endOffset, range))
michael@0 1415 return false;
michael@0 1416
michael@0 1417 // If new range was created then add it, otherwise notify selection listeners
michael@0 1418 // that existing selection range was changed.
michael@0 1419 if (aSelectionNum == rangeCount)
michael@0 1420 return NS_SUCCEEDED(domSel->AddRange(range));
michael@0 1421
michael@0 1422 domSel->RemoveRange(range);
michael@0 1423 return NS_SUCCEEDED(domSel->AddRange(range));
michael@0 1424 }
michael@0 1425
michael@0 1426 bool
michael@0 1427 HyperTextAccessible::RemoveFromSelection(int32_t aSelectionNum)
michael@0 1428 {
michael@0 1429 dom::Selection* domSel = DOMSelection();
michael@0 1430 if (!domSel)
michael@0 1431 return false;
michael@0 1432
michael@0 1433 if (aSelectionNum < 0 || aSelectionNum >= domSel->GetRangeCount())
michael@0 1434 return false;
michael@0 1435
michael@0 1436 domSel->RemoveRange(domSel->GetRangeAt(aSelectionNum));
michael@0 1437 return true;
michael@0 1438 }
michael@0 1439
michael@0 1440 void
michael@0 1441 HyperTextAccessible::ScrollSubstringTo(int32_t aStartOffset, int32_t aEndOffset,
michael@0 1442 uint32_t aScrollType)
michael@0 1443 {
michael@0 1444 nsRefPtr<nsRange> range = new nsRange(mContent);
michael@0 1445 if (OffsetsToDOMRange(aStartOffset, aEndOffset, range))
michael@0 1446 nsCoreUtils::ScrollSubstringTo(GetFrame(), range, aScrollType);
michael@0 1447 }
michael@0 1448
michael@0 1449 void
michael@0 1450 HyperTextAccessible::ScrollSubstringToPoint(int32_t aStartOffset,
michael@0 1451 int32_t aEndOffset,
michael@0 1452 uint32_t aCoordinateType,
michael@0 1453 int32_t aX, int32_t aY)
michael@0 1454 {
michael@0 1455 nsIFrame *frame = GetFrame();
michael@0 1456 if (!frame)
michael@0 1457 return;
michael@0 1458
michael@0 1459 nsIntPoint coords = nsAccUtils::ConvertToScreenCoords(aX, aY, aCoordinateType,
michael@0 1460 this);
michael@0 1461
michael@0 1462 nsRefPtr<nsRange> range = new nsRange(mContent);
michael@0 1463 if (!OffsetsToDOMRange(aStartOffset, aEndOffset, range))
michael@0 1464 return;
michael@0 1465
michael@0 1466 nsPresContext* presContext = frame->PresContext();
michael@0 1467 nsPoint coordsInAppUnits =
michael@0 1468 coords.ToAppUnits(presContext->AppUnitsPerDevPixel());
michael@0 1469
michael@0 1470 bool initialScrolled = false;
michael@0 1471 nsIFrame *parentFrame = frame;
michael@0 1472 while ((parentFrame = parentFrame->GetParent())) {
michael@0 1473 nsIScrollableFrame *scrollableFrame = do_QueryFrame(parentFrame);
michael@0 1474 if (scrollableFrame) {
michael@0 1475 if (!initialScrolled) {
michael@0 1476 // Scroll substring to the given point. Turn the point into percents
michael@0 1477 // relative scrollable area to use nsCoreUtils::ScrollSubstringTo.
michael@0 1478 nsRect frameRect = parentFrame->GetScreenRectInAppUnits();
michael@0 1479 nscoord offsetPointX = coordsInAppUnits.x - frameRect.x;
michael@0 1480 nscoord offsetPointY = coordsInAppUnits.y - frameRect.y;
michael@0 1481
michael@0 1482 nsSize size(parentFrame->GetSize());
michael@0 1483
michael@0 1484 // avoid divide by zero
michael@0 1485 size.width = size.width ? size.width : 1;
michael@0 1486 size.height = size.height ? size.height : 1;
michael@0 1487
michael@0 1488 int16_t hPercent = offsetPointX * 100 / size.width;
michael@0 1489 int16_t vPercent = offsetPointY * 100 / size.height;
michael@0 1490
michael@0 1491 nsresult rv = nsCoreUtils::ScrollSubstringTo(frame, range, vPercent, hPercent);
michael@0 1492 if (NS_FAILED(rv))
michael@0 1493 return;
michael@0 1494
michael@0 1495 initialScrolled = true;
michael@0 1496 } else {
michael@0 1497 // Substring was scrolled to the given point already inside its closest
michael@0 1498 // scrollable area. If there are nested scrollable areas then make
michael@0 1499 // sure we scroll lower areas to the given point inside currently
michael@0 1500 // traversed scrollable area.
michael@0 1501 nsCoreUtils::ScrollFrameToPoint(parentFrame, frame, coords);
michael@0 1502 }
michael@0 1503 }
michael@0 1504 frame = parentFrame;
michael@0 1505 }
michael@0 1506 }
michael@0 1507
michael@0 1508 void
michael@0 1509 HyperTextAccessible::EnclosingRange(a11y::TextRange& aRange) const
michael@0 1510 {
michael@0 1511 if (IsTextField()) {
michael@0 1512 aRange.Set(mDoc, const_cast<HyperTextAccessible*>(this), 0,
michael@0 1513 const_cast<HyperTextAccessible*>(this), ChildCount());
michael@0 1514 } else {
michael@0 1515 aRange.Set(mDoc, mDoc, 0, mDoc, mDoc->ChildCount());
michael@0 1516 }
michael@0 1517 }
michael@0 1518
michael@0 1519 void
michael@0 1520 HyperTextAccessible::SelectionRanges(nsTArray<a11y::TextRange>* aRanges) const
michael@0 1521 {
michael@0 1522 NS_ASSERTION(aRanges->Length() != 0, "TextRange array supposed to be empty");
michael@0 1523
michael@0 1524 dom::Selection* sel = DOMSelection();
michael@0 1525 if (!sel)
michael@0 1526 return;
michael@0 1527
michael@0 1528 aRanges->SetCapacity(sel->RangeCount());
michael@0 1529
michael@0 1530 for (uint32_t idx = 0; idx < sel->RangeCount(); idx++) {
michael@0 1531 nsRange* DOMRange = sel->GetRangeAt(idx);
michael@0 1532 HyperTextAccessible* startParent =
michael@0 1533 nsAccUtils::GetTextContainer(DOMRange->GetStartParent());
michael@0 1534 HyperTextAccessible* endParent =
michael@0 1535 nsAccUtils::GetTextContainer(DOMRange->GetEndParent());
michael@0 1536 if (!startParent || !endParent)
michael@0 1537 continue;
michael@0 1538
michael@0 1539 int32_t startOffset =
michael@0 1540 startParent->DOMPointToOffset(DOMRange->GetStartParent(),
michael@0 1541 DOMRange->StartOffset(), false);
michael@0 1542 int32_t endOffset =
michael@0 1543 endParent->DOMPointToOffset(DOMRange->GetEndParent(),
michael@0 1544 DOMRange->EndOffset(), true);
michael@0 1545
michael@0 1546 TextRange tr(IsTextField() ? const_cast<HyperTextAccessible*>(this) : mDoc,
michael@0 1547 startParent, startOffset, endParent, endOffset);
michael@0 1548 *(aRanges->AppendElement()) = Move(tr);
michael@0 1549 }
michael@0 1550 }
michael@0 1551
michael@0 1552 void
michael@0 1553 HyperTextAccessible::VisibleRanges(nsTArray<a11y::TextRange>* aRanges) const
michael@0 1554 {
michael@0 1555 }
michael@0 1556
michael@0 1557 void
michael@0 1558 HyperTextAccessible::RangeByChild(Accessible* aChild,
michael@0 1559 a11y::TextRange& aRange) const
michael@0 1560 {
michael@0 1561 aRange.Set(mDoc, aChild, 0, aChild, aChild->ChildCount());
michael@0 1562 }
michael@0 1563
michael@0 1564 void
michael@0 1565 HyperTextAccessible::RangeAtPoint(int32_t aX, int32_t aY,
michael@0 1566 a11y::TextRange& aRange) const
michael@0 1567 {
michael@0 1568 Accessible* child = mDoc->ChildAtPoint(aX, aY, eDeepestChild);
michael@0 1569 if (child)
michael@0 1570 aRange.Set(mDoc, child, 0, child, child->ChildCount());
michael@0 1571 }
michael@0 1572
michael@0 1573 ////////////////////////////////////////////////////////////////////////////////
michael@0 1574 // Accessible public
michael@0 1575
michael@0 1576 // Accessible protected
michael@0 1577 ENameValueFlag
michael@0 1578 HyperTextAccessible::NativeName(nsString& aName)
michael@0 1579 {
michael@0 1580 // Check @alt attribute for invalid img elements.
michael@0 1581 bool hasImgAlt = false;
michael@0 1582 if (mContent->IsHTML(nsGkAtoms::img)) {
michael@0 1583 hasImgAlt = mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::alt, aName);
michael@0 1584 if (!aName.IsEmpty())
michael@0 1585 return eNameOK;
michael@0 1586 }
michael@0 1587
michael@0 1588 ENameValueFlag nameFlag = AccessibleWrap::NativeName(aName);
michael@0 1589 if (!aName.IsEmpty())
michael@0 1590 return nameFlag;
michael@0 1591
michael@0 1592 // Get name from title attribute for HTML abbr and acronym elements making it
michael@0 1593 // a valid name from markup. Otherwise their name isn't picked up by recursive
michael@0 1594 // name computation algorithm. See NS_OK_NAME_FROM_TOOLTIP.
michael@0 1595 if (IsAbbreviation() &&
michael@0 1596 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::title, aName))
michael@0 1597 aName.CompressWhitespace();
michael@0 1598
michael@0 1599 return hasImgAlt ? eNoNameOnPurpose : eNameOK;
michael@0 1600 }
michael@0 1601
michael@0 1602 void
michael@0 1603 HyperTextAccessible::InvalidateChildren()
michael@0 1604 {
michael@0 1605 mOffsets.Clear();
michael@0 1606
michael@0 1607 AccessibleWrap::InvalidateChildren();
michael@0 1608 }
michael@0 1609
michael@0 1610 bool
michael@0 1611 HyperTextAccessible::RemoveChild(Accessible* aAccessible)
michael@0 1612 {
michael@0 1613 int32_t childIndex = aAccessible->IndexInParent();
michael@0 1614 int32_t count = mOffsets.Length() - childIndex;
michael@0 1615 if (count > 0)
michael@0 1616 mOffsets.RemoveElementsAt(childIndex, count);
michael@0 1617
michael@0 1618 return Accessible::RemoveChild(aAccessible);
michael@0 1619 }
michael@0 1620
michael@0 1621 void
michael@0 1622 HyperTextAccessible::CacheChildren()
michael@0 1623 {
michael@0 1624 // Trailing HTML br element don't play any difference. We don't need to expose
michael@0 1625 // it to AT (see bug https://bugzilla.mozilla.org/show_bug.cgi?id=899433#c16
michael@0 1626 // for details).
michael@0 1627
michael@0 1628 TreeWalker walker(this, mContent);
michael@0 1629 Accessible* child = nullptr;
michael@0 1630 Accessible* lastChild = nullptr;
michael@0 1631 while ((child = walker.NextChild())) {
michael@0 1632 if (lastChild)
michael@0 1633 AppendChild(lastChild);
michael@0 1634
michael@0 1635 lastChild = child;
michael@0 1636 }
michael@0 1637
michael@0 1638 if (lastChild) {
michael@0 1639 if (lastChild->IsHTMLBr())
michael@0 1640 Document()->UnbindFromDocument(lastChild);
michael@0 1641 else
michael@0 1642 AppendChild(lastChild);
michael@0 1643 }
michael@0 1644 }
michael@0 1645
michael@0 1646 ////////////////////////////////////////////////////////////////////////////////
michael@0 1647 // HyperTextAccessible public static
michael@0 1648
michael@0 1649 nsresult
michael@0 1650 HyperTextAccessible::ContentToRenderedOffset(nsIFrame* aFrame, int32_t aContentOffset,
michael@0 1651 uint32_t* aRenderedOffset) const
michael@0 1652 {
michael@0 1653 if (!aFrame) {
michael@0 1654 // Current frame not rendered -- this can happen if text is set on
michael@0 1655 // something with display: none
michael@0 1656 *aRenderedOffset = 0;
michael@0 1657 return NS_OK;
michael@0 1658 }
michael@0 1659
michael@0 1660 if (IsTextField()) {
michael@0 1661 *aRenderedOffset = aContentOffset;
michael@0 1662 return NS_OK;
michael@0 1663 }
michael@0 1664
michael@0 1665 NS_ASSERTION(aFrame->GetType() == nsGkAtoms::textFrame,
michael@0 1666 "Need text frame for offset conversion");
michael@0 1667 NS_ASSERTION(aFrame->GetPrevContinuation() == nullptr,
michael@0 1668 "Call on primary frame only");
michael@0 1669
michael@0 1670 gfxSkipChars skipChars;
michael@0 1671 gfxSkipCharsIterator iter;
michael@0 1672 // Only get info up to original offset, we know that will be larger than skipped offset
michael@0 1673 nsresult rv = aFrame->GetRenderedText(nullptr, &skipChars, &iter, 0, aContentOffset);
michael@0 1674 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1675
michael@0 1676 uint32_t ourRenderedStart = iter.GetSkippedOffset();
michael@0 1677 int32_t ourContentStart = iter.GetOriginalOffset();
michael@0 1678
michael@0 1679 *aRenderedOffset = iter.ConvertOriginalToSkipped(aContentOffset + ourContentStart) -
michael@0 1680 ourRenderedStart;
michael@0 1681
michael@0 1682 return NS_OK;
michael@0 1683 }
michael@0 1684
michael@0 1685 nsresult
michael@0 1686 HyperTextAccessible::RenderedToContentOffset(nsIFrame* aFrame, uint32_t aRenderedOffset,
michael@0 1687 int32_t* aContentOffset) const
michael@0 1688 {
michael@0 1689 if (IsTextField()) {
michael@0 1690 *aContentOffset = aRenderedOffset;
michael@0 1691 return NS_OK;
michael@0 1692 }
michael@0 1693
michael@0 1694 *aContentOffset = 0;
michael@0 1695 NS_ENSURE_TRUE(aFrame, NS_ERROR_FAILURE);
michael@0 1696
michael@0 1697 NS_ASSERTION(aFrame->GetType() == nsGkAtoms::textFrame,
michael@0 1698 "Need text frame for offset conversion");
michael@0 1699 NS_ASSERTION(aFrame->GetPrevContinuation() == nullptr,
michael@0 1700 "Call on primary frame only");
michael@0 1701
michael@0 1702 gfxSkipChars skipChars;
michael@0 1703 gfxSkipCharsIterator iter;
michael@0 1704 // We only need info up to skipped offset -- that is what we're converting to original offset
michael@0 1705 nsresult rv = aFrame->GetRenderedText(nullptr, &skipChars, &iter, 0, aRenderedOffset);
michael@0 1706 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1707
michael@0 1708 uint32_t ourRenderedStart = iter.GetSkippedOffset();
michael@0 1709 int32_t ourContentStart = iter.GetOriginalOffset();
michael@0 1710
michael@0 1711 *aContentOffset = iter.ConvertSkippedToOriginal(aRenderedOffset + ourRenderedStart) - ourContentStart;
michael@0 1712
michael@0 1713 return NS_OK;
michael@0 1714 }
michael@0 1715
michael@0 1716 ////////////////////////////////////////////////////////////////////////////////
michael@0 1717 // HyperTextAccessible public
michael@0 1718
michael@0 1719 int32_t
michael@0 1720 HyperTextAccessible::GetChildOffset(uint32_t aChildIndex,
michael@0 1721 bool aInvalidateAfter) const
michael@0 1722 {
michael@0 1723 if (aChildIndex == 0) {
michael@0 1724 if (aInvalidateAfter)
michael@0 1725 mOffsets.Clear();
michael@0 1726
michael@0 1727 return aChildIndex;
michael@0 1728 }
michael@0 1729
michael@0 1730 int32_t count = mOffsets.Length() - aChildIndex;
michael@0 1731 if (count > 0) {
michael@0 1732 if (aInvalidateAfter)
michael@0 1733 mOffsets.RemoveElementsAt(aChildIndex, count);
michael@0 1734
michael@0 1735 return mOffsets[aChildIndex - 1];
michael@0 1736 }
michael@0 1737
michael@0 1738 uint32_t lastOffset = mOffsets.IsEmpty() ?
michael@0 1739 0 : mOffsets[mOffsets.Length() - 1];
michael@0 1740
michael@0 1741 while (mOffsets.Length() < aChildIndex) {
michael@0 1742 Accessible* child = mChildren[mOffsets.Length()];
michael@0 1743 lastOffset += nsAccUtils::TextLength(child);
michael@0 1744 mOffsets.AppendElement(lastOffset);
michael@0 1745 }
michael@0 1746
michael@0 1747 return mOffsets[aChildIndex - 1];
michael@0 1748 }
michael@0 1749
michael@0 1750 int32_t
michael@0 1751 HyperTextAccessible::GetChildIndexAtOffset(uint32_t aOffset) const
michael@0 1752 {
michael@0 1753 uint32_t lastOffset = 0;
michael@0 1754 uint32_t offsetCount = mOffsets.Length();
michael@0 1755 if (offsetCount > 0) {
michael@0 1756 lastOffset = mOffsets[offsetCount - 1];
michael@0 1757 if (aOffset < lastOffset) {
michael@0 1758 uint32_t low = 0, high = offsetCount;
michael@0 1759 while (high > low) {
michael@0 1760 uint32_t mid = (high + low) >> 1;
michael@0 1761 if (mOffsets[mid] == aOffset)
michael@0 1762 return mid < offsetCount - 1 ? mid + 1 : mid;
michael@0 1763
michael@0 1764 if (mOffsets[mid] < aOffset)
michael@0 1765 low = mid + 1;
michael@0 1766 else
michael@0 1767 high = mid;
michael@0 1768 }
michael@0 1769 if (high == offsetCount)
michael@0 1770 return -1;
michael@0 1771
michael@0 1772 return low;
michael@0 1773 }
michael@0 1774 }
michael@0 1775
michael@0 1776 uint32_t childCount = ChildCount();
michael@0 1777 while (mOffsets.Length() < childCount) {
michael@0 1778 Accessible* child = GetChildAt(mOffsets.Length());
michael@0 1779 lastOffset += nsAccUtils::TextLength(child);
michael@0 1780 mOffsets.AppendElement(lastOffset);
michael@0 1781 if (aOffset < lastOffset)
michael@0 1782 return mOffsets.Length() - 1;
michael@0 1783 }
michael@0 1784
michael@0 1785 if (aOffset == lastOffset)
michael@0 1786 return mOffsets.Length() - 1;
michael@0 1787
michael@0 1788 return -1;
michael@0 1789 }
michael@0 1790
michael@0 1791 ////////////////////////////////////////////////////////////////////////////////
michael@0 1792 // HyperTextAccessible protected
michael@0 1793
michael@0 1794 nsresult
michael@0 1795 HyperTextAccessible::GetDOMPointByFrameOffset(nsIFrame* aFrame, int32_t aOffset,
michael@0 1796 Accessible* aAccessible,
michael@0 1797 DOMPoint* aPoint)
michael@0 1798 {
michael@0 1799 NS_ENSURE_ARG(aAccessible);
michael@0 1800
michael@0 1801 if (!aFrame) {
michael@0 1802 // If the given frame is null then set offset after the DOM node of the
michael@0 1803 // given accessible.
michael@0 1804 NS_ASSERTION(!aAccessible->IsDoc(),
michael@0 1805 "Shouldn't be called on document accessible!");
michael@0 1806
michael@0 1807 nsIContent* content = aAccessible->GetContent();
michael@0 1808 NS_ASSERTION(content, "Shouldn't operate on defunct accessible!");
michael@0 1809
michael@0 1810 nsIContent* parent = content->GetParent();
michael@0 1811
michael@0 1812 aPoint->idx = parent->IndexOf(content) + 1;
michael@0 1813 aPoint->node = parent;
michael@0 1814
michael@0 1815 } else if (aFrame->GetType() == nsGkAtoms::textFrame) {
michael@0 1816 nsIContent* content = aFrame->GetContent();
michael@0 1817 NS_ENSURE_STATE(content);
michael@0 1818
michael@0 1819 nsIFrame *primaryFrame = content->GetPrimaryFrame();
michael@0 1820 nsresult rv = RenderedToContentOffset(primaryFrame, aOffset, &(aPoint->idx));
michael@0 1821 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1822
michael@0 1823 aPoint->node = content;
michael@0 1824
michael@0 1825 } else {
michael@0 1826 nsIContent* content = aFrame->GetContent();
michael@0 1827 NS_ENSURE_STATE(content);
michael@0 1828
michael@0 1829 nsIContent* parent = content->GetParent();
michael@0 1830 NS_ENSURE_STATE(parent);
michael@0 1831
michael@0 1832 aPoint->idx = parent->IndexOf(content);
michael@0 1833 aPoint->node = parent;
michael@0 1834 }
michael@0 1835
michael@0 1836 return NS_OK;
michael@0 1837 }
michael@0 1838
michael@0 1839 // HyperTextAccessible
michael@0 1840 nsresult
michael@0 1841 HyperTextAccessible::GetSpellTextAttribute(nsINode* aNode,
michael@0 1842 int32_t aNodeOffset,
michael@0 1843 int32_t* aHTStartOffset,
michael@0 1844 int32_t* aHTEndOffset,
michael@0 1845 nsIPersistentProperties* aAttributes)
michael@0 1846 {
michael@0 1847 nsRefPtr<nsFrameSelection> fs = FrameSelection();
michael@0 1848 if (!fs)
michael@0 1849 return NS_OK;
michael@0 1850
michael@0 1851 dom::Selection* domSel = fs->GetSelection(nsISelectionController::SELECTION_SPELLCHECK);
michael@0 1852 if (!domSel)
michael@0 1853 return NS_OK;
michael@0 1854
michael@0 1855 int32_t rangeCount = domSel->GetRangeCount();
michael@0 1856 if (rangeCount <= 0)
michael@0 1857 return NS_OK;
michael@0 1858
michael@0 1859 int32_t startHTOffset = 0, endHTOffset = 0;
michael@0 1860 for (int32_t idx = 0; idx < rangeCount; idx++) {
michael@0 1861 nsRange* range = domSel->GetRangeAt(idx);
michael@0 1862 if (range->Collapsed())
michael@0 1863 continue;
michael@0 1864
michael@0 1865 // See if the point comes after the range in which case we must continue in
michael@0 1866 // case there is another range after this one.
michael@0 1867 nsINode* endNode = range->GetEndParent();
michael@0 1868 int32_t endOffset = range->EndOffset();
michael@0 1869 if (nsContentUtils::ComparePoints(aNode, aNodeOffset, endNode, endOffset) >= 0)
michael@0 1870 continue;
michael@0 1871
michael@0 1872 // At this point our point is either in this range or before it but after
michael@0 1873 // the previous range. So we check to see if the range starts before the
michael@0 1874 // point in which case the point is in the missspelled range, otherwise it
michael@0 1875 // must be before the range and after the previous one if any.
michael@0 1876 nsINode* startNode = range->GetStartParent();
michael@0 1877 int32_t startOffset = range->StartOffset();
michael@0 1878 if (nsContentUtils::ComparePoints(startNode, startOffset, aNode,
michael@0 1879 aNodeOffset) <= 0) {
michael@0 1880 startHTOffset = DOMPointToOffset(startNode, startOffset);
michael@0 1881
michael@0 1882 endHTOffset = DOMPointToOffset(endNode, endOffset);
michael@0 1883
michael@0 1884 if (startHTOffset > *aHTStartOffset)
michael@0 1885 *aHTStartOffset = startHTOffset;
michael@0 1886
michael@0 1887 if (endHTOffset < *aHTEndOffset)
michael@0 1888 *aHTEndOffset = endHTOffset;
michael@0 1889
michael@0 1890 if (aAttributes) {
michael@0 1891 nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::invalid,
michael@0 1892 NS_LITERAL_STRING("spelling"));
michael@0 1893 }
michael@0 1894
michael@0 1895 return NS_OK;
michael@0 1896 }
michael@0 1897
michael@0 1898 // This range came after the point.
michael@0 1899 endHTOffset = DOMPointToOffset(startNode, startOffset);
michael@0 1900
michael@0 1901 if (idx > 0) {
michael@0 1902 nsRange* prevRange = domSel->GetRangeAt(idx - 1);
michael@0 1903 startHTOffset = DOMPointToOffset(prevRange->GetEndParent(),
michael@0 1904 prevRange->EndOffset());
michael@0 1905 }
michael@0 1906
michael@0 1907 if (startHTOffset > *aHTStartOffset)
michael@0 1908 *aHTStartOffset = startHTOffset;
michael@0 1909
michael@0 1910 if (endHTOffset < *aHTEndOffset)
michael@0 1911 *aHTEndOffset = endHTOffset;
michael@0 1912
michael@0 1913 return NS_OK;
michael@0 1914 }
michael@0 1915
michael@0 1916 // We never found a range that ended after the point, therefore we know that
michael@0 1917 // the point is not in a range, that we do not need to compute an end offset,
michael@0 1918 // and that we should use the end offset of the last range to compute the
michael@0 1919 // start offset of the text attribute range.
michael@0 1920 nsRange* prevRange = domSel->GetRangeAt(rangeCount - 1);
michael@0 1921 startHTOffset = DOMPointToOffset(prevRange->GetEndParent(),
michael@0 1922 prevRange->EndOffset());
michael@0 1923
michael@0 1924 if (startHTOffset > *aHTStartOffset)
michael@0 1925 *aHTStartOffset = startHTOffset;
michael@0 1926
michael@0 1927 return NS_OK;
michael@0 1928 }
michael@0 1929
michael@0 1930 bool
michael@0 1931 HyperTextAccessible::IsTextRole()
michael@0 1932 {
michael@0 1933 if (mRoleMapEntry &&
michael@0 1934 (mRoleMapEntry->role == roles::GRAPHIC ||
michael@0 1935 mRoleMapEntry->role == roles::IMAGE_MAP ||
michael@0 1936 mRoleMapEntry->role == roles::SLIDER ||
michael@0 1937 mRoleMapEntry->role == roles::PROGRESSBAR ||
michael@0 1938 mRoleMapEntry->role == roles::SEPARATOR))
michael@0 1939 return false;
michael@0 1940
michael@0 1941 return true;
michael@0 1942 }

mercurial