1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/accessible/src/base/nsAccessiblePivot.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,899 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=2 et sw=2 tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "nsAccessiblePivot.h" 1.11 + 1.12 +#include "HyperTextAccessible.h" 1.13 +#include "nsAccUtils.h" 1.14 +#include "States.h" 1.15 + 1.16 +using namespace mozilla::a11y; 1.17 + 1.18 + 1.19 +/** 1.20 + * An object that stores a given traversal rule during 1.21 + */ 1.22 +class RuleCache 1.23 +{ 1.24 +public: 1.25 + RuleCache(nsIAccessibleTraversalRule* aRule) : mRule(aRule), 1.26 + mAcceptRoles(nullptr) { } 1.27 + ~RuleCache () { 1.28 + if (mAcceptRoles) 1.29 + nsMemory::Free(mAcceptRoles); 1.30 + } 1.31 + 1.32 + nsresult ApplyFilter(Accessible* aAccessible, uint16_t* aResult); 1.33 + 1.34 +private: 1.35 + nsCOMPtr<nsIAccessibleTraversalRule> mRule; 1.36 + uint32_t* mAcceptRoles; 1.37 + uint32_t mAcceptRolesLength; 1.38 + uint32_t mPreFilter; 1.39 +}; 1.40 + 1.41 +//////////////////////////////////////////////////////////////////////////////// 1.42 +// nsAccessiblePivot 1.43 + 1.44 +nsAccessiblePivot::nsAccessiblePivot(Accessible* aRoot) : 1.45 + mRoot(aRoot), mModalRoot(nullptr), mPosition(nullptr), 1.46 + mStartOffset(-1), mEndOffset(-1) 1.47 +{ 1.48 + NS_ASSERTION(aRoot, "A root accessible is required"); 1.49 +} 1.50 + 1.51 +//////////////////////////////////////////////////////////////////////////////// 1.52 +// nsISupports 1.53 + 1.54 +NS_IMPL_CYCLE_COLLECTION(nsAccessiblePivot, mRoot, mPosition, mObservers) 1.55 + 1.56 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsAccessiblePivot) 1.57 + NS_INTERFACE_MAP_ENTRY(nsIAccessiblePivot) 1.58 + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAccessiblePivot) 1.59 +NS_INTERFACE_MAP_END 1.60 + 1.61 +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsAccessiblePivot) 1.62 +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsAccessiblePivot) 1.63 + 1.64 +//////////////////////////////////////////////////////////////////////////////// 1.65 +// nsIAccessiblePivot 1.66 + 1.67 +NS_IMETHODIMP 1.68 +nsAccessiblePivot::GetRoot(nsIAccessible** aRoot) 1.69 +{ 1.70 + NS_ENSURE_ARG_POINTER(aRoot); 1.71 + 1.72 + NS_IF_ADDREF(*aRoot = mRoot); 1.73 + 1.74 + return NS_OK; 1.75 +} 1.76 + 1.77 +NS_IMETHODIMP 1.78 +nsAccessiblePivot::GetPosition(nsIAccessible** aPosition) 1.79 +{ 1.80 + NS_ENSURE_ARG_POINTER(aPosition); 1.81 + 1.82 + NS_IF_ADDREF(*aPosition = mPosition); 1.83 + 1.84 + return NS_OK; 1.85 +} 1.86 + 1.87 +NS_IMETHODIMP 1.88 +nsAccessiblePivot::SetPosition(nsIAccessible* aPosition) 1.89 +{ 1.90 + nsRefPtr<Accessible> secondPosition; 1.91 + 1.92 + if (aPosition) { 1.93 + secondPosition = do_QueryObject(aPosition); 1.94 + if (!secondPosition || !IsDescendantOf(secondPosition, GetActiveRoot())) 1.95 + return NS_ERROR_INVALID_ARG; 1.96 + } 1.97 + 1.98 + // Swap old position with new position, saves us an AddRef/Release. 1.99 + mPosition.swap(secondPosition); 1.100 + int32_t oldStart = mStartOffset, oldEnd = mEndOffset; 1.101 + mStartOffset = mEndOffset = -1; 1.102 + NotifyOfPivotChange(secondPosition, oldStart, oldEnd, 1.103 + nsIAccessiblePivot::REASON_NONE); 1.104 + 1.105 + return NS_OK; 1.106 +} 1.107 + 1.108 +NS_IMETHODIMP 1.109 +nsAccessiblePivot::GetModalRoot(nsIAccessible** aModalRoot) 1.110 +{ 1.111 + NS_ENSURE_ARG_POINTER(aModalRoot); 1.112 + 1.113 + NS_IF_ADDREF(*aModalRoot = mModalRoot); 1.114 + 1.115 + return NS_OK; 1.116 +} 1.117 + 1.118 +NS_IMETHODIMP 1.119 +nsAccessiblePivot::SetModalRoot(nsIAccessible* aModalRoot) 1.120 +{ 1.121 + nsRefPtr<Accessible> modalRoot; 1.122 + 1.123 + if (aModalRoot) { 1.124 + modalRoot = do_QueryObject(aModalRoot); 1.125 + if (!modalRoot || !IsDescendantOf(modalRoot, mRoot)) 1.126 + return NS_ERROR_INVALID_ARG; 1.127 + } 1.128 + 1.129 + mModalRoot.swap(modalRoot); 1.130 + 1.131 + return NS_OK; 1.132 +} 1.133 + 1.134 +NS_IMETHODIMP 1.135 +nsAccessiblePivot::GetStartOffset(int32_t* aStartOffset) 1.136 +{ 1.137 + NS_ENSURE_ARG_POINTER(aStartOffset); 1.138 + 1.139 + *aStartOffset = mStartOffset; 1.140 + 1.141 + return NS_OK; 1.142 +} 1.143 + 1.144 +NS_IMETHODIMP 1.145 +nsAccessiblePivot::GetEndOffset(int32_t* aEndOffset) 1.146 +{ 1.147 + NS_ENSURE_ARG_POINTER(aEndOffset); 1.148 + 1.149 + *aEndOffset = mEndOffset; 1.150 + 1.151 + return NS_OK; 1.152 +} 1.153 + 1.154 +NS_IMETHODIMP 1.155 +nsAccessiblePivot::SetTextRange(nsIAccessibleText* aTextAccessible, 1.156 + int32_t aStartOffset, int32_t aEndOffset) 1.157 +{ 1.158 + NS_ENSURE_ARG(aTextAccessible); 1.159 + 1.160 + // Check that start offset is smaller than end offset, and that if a value is 1.161 + // smaller than 0, both should be -1. 1.162 + NS_ENSURE_TRUE(aStartOffset <= aEndOffset && 1.163 + (aStartOffset >= 0 || (aStartOffset != -1 && aEndOffset != -1)), 1.164 + NS_ERROR_INVALID_ARG); 1.165 + 1.166 + nsRefPtr<Accessible> acc(do_QueryObject(aTextAccessible)); 1.167 + if (!acc) 1.168 + return NS_ERROR_INVALID_ARG; 1.169 + 1.170 + HyperTextAccessible* newPosition = acc->AsHyperText(); 1.171 + if (!newPosition || !IsDescendantOf(newPosition, GetActiveRoot())) 1.172 + return NS_ERROR_INVALID_ARG; 1.173 + 1.174 + // Make sure the given offsets don't exceed the character count. 1.175 + int32_t charCount = newPosition->CharacterCount(); 1.176 + 1.177 + if (aEndOffset > charCount) 1.178 + return NS_ERROR_FAILURE; 1.179 + 1.180 + int32_t oldStart = mStartOffset, oldEnd = mEndOffset; 1.181 + mStartOffset = aStartOffset; 1.182 + mEndOffset = aEndOffset; 1.183 + 1.184 + nsRefPtr<Accessible> oldPosition = mPosition.forget(); 1.185 + mPosition = newPosition; 1.186 + 1.187 + NotifyOfPivotChange(oldPosition, oldStart, oldEnd, 1.188 + nsIAccessiblePivot::REASON_TEXT); 1.189 + 1.190 + return NS_OK; 1.191 +} 1.192 + 1.193 +// Traversal functions 1.194 + 1.195 +NS_IMETHODIMP 1.196 +nsAccessiblePivot::MoveNext(nsIAccessibleTraversalRule* aRule, 1.197 + nsIAccessible* aAnchor, bool aIncludeStart, 1.198 + uint8_t aArgc, bool* aResult) 1.199 +{ 1.200 + NS_ENSURE_ARG(aResult); 1.201 + NS_ENSURE_ARG(aRule); 1.202 + 1.203 + *aResult = false; 1.204 + 1.205 + Accessible* root = GetActiveRoot(); 1.206 + nsRefPtr<Accessible> anchor = 1.207 + (aArgc > 0) ? do_QueryObject(aAnchor) : mPosition; 1.208 + if (anchor && (anchor->IsDefunct() || !IsDescendantOf(anchor, root))) 1.209 + return NS_ERROR_NOT_IN_TREE; 1.210 + 1.211 + nsresult rv = NS_OK; 1.212 + Accessible* accessible = 1.213 + SearchForward(anchor, aRule, (aArgc > 1) ? aIncludeStart : false, &rv); 1.214 + NS_ENSURE_SUCCESS(rv, rv); 1.215 + 1.216 + if (accessible) 1.217 + *aResult = MovePivotInternal(accessible, nsIAccessiblePivot::REASON_NEXT); 1.218 + 1.219 + return NS_OK; 1.220 +} 1.221 + 1.222 +NS_IMETHODIMP 1.223 +nsAccessiblePivot::MovePrevious(nsIAccessibleTraversalRule* aRule, 1.224 + nsIAccessible* aAnchor, 1.225 + bool aIncludeStart, 1.226 + uint8_t aArgc, bool* aResult) 1.227 +{ 1.228 + NS_ENSURE_ARG(aResult); 1.229 + NS_ENSURE_ARG(aRule); 1.230 + 1.231 + *aResult = false; 1.232 + 1.233 + Accessible* root = GetActiveRoot(); 1.234 + nsRefPtr<Accessible> anchor = 1.235 + (aArgc > 0) ? do_QueryObject(aAnchor) : mPosition; 1.236 + if (anchor && (anchor->IsDefunct() || !IsDescendantOf(anchor, root))) 1.237 + return NS_ERROR_NOT_IN_TREE; 1.238 + 1.239 + nsresult rv = NS_OK; 1.240 + Accessible* accessible = 1.241 + SearchBackward(anchor, aRule, (aArgc > 1) ? aIncludeStart : false, &rv); 1.242 + NS_ENSURE_SUCCESS(rv, rv); 1.243 + 1.244 + if (accessible) 1.245 + *aResult = MovePivotInternal(accessible, nsIAccessiblePivot::REASON_PREV); 1.246 + 1.247 + return NS_OK; 1.248 +} 1.249 + 1.250 +NS_IMETHODIMP 1.251 +nsAccessiblePivot::MoveFirst(nsIAccessibleTraversalRule* aRule, bool* aResult) 1.252 +{ 1.253 + NS_ENSURE_ARG(aResult); 1.254 + NS_ENSURE_ARG(aRule); 1.255 + 1.256 + Accessible* root = GetActiveRoot(); 1.257 + NS_ENSURE_TRUE(root && !root->IsDefunct(), NS_ERROR_NOT_IN_TREE); 1.258 + 1.259 + nsresult rv = NS_OK; 1.260 + Accessible* accessible = SearchForward(root, aRule, true, &rv); 1.261 + NS_ENSURE_SUCCESS(rv, rv); 1.262 + 1.263 + if (accessible) 1.264 + *aResult = MovePivotInternal(accessible, nsIAccessiblePivot::REASON_FIRST); 1.265 + 1.266 + return NS_OK; 1.267 +} 1.268 + 1.269 +NS_IMETHODIMP 1.270 +nsAccessiblePivot::MoveLast(nsIAccessibleTraversalRule* aRule, 1.271 + bool* aResult) 1.272 +{ 1.273 + NS_ENSURE_ARG(aResult); 1.274 + NS_ENSURE_ARG(aRule); 1.275 + 1.276 + Accessible* root = GetActiveRoot(); 1.277 + NS_ENSURE_TRUE(root && !root->IsDefunct(), NS_ERROR_NOT_IN_TREE); 1.278 + 1.279 + *aResult = false; 1.280 + nsresult rv = NS_OK; 1.281 + Accessible* lastAccessible = root; 1.282 + Accessible* accessible = nullptr; 1.283 + 1.284 + // First go to the last accessible in pre-order 1.285 + while (lastAccessible->HasChildren()) 1.286 + lastAccessible = lastAccessible->LastChild(); 1.287 + 1.288 + // Search backwards from last accessible and find the last occurrence in the doc 1.289 + accessible = SearchBackward(lastAccessible, aRule, true, &rv); 1.290 + NS_ENSURE_SUCCESS(rv, rv); 1.291 + 1.292 + if (accessible) 1.293 + *aResult = MovePivotInternal(accessible, nsAccessiblePivot::REASON_LAST); 1.294 + 1.295 + return NS_OK; 1.296 +} 1.297 + 1.298 +NS_IMETHODIMP 1.299 +nsAccessiblePivot::MoveNextByText(TextBoundaryType aBoundary, bool* aResult) 1.300 +{ 1.301 + NS_ENSURE_ARG(aResult); 1.302 + 1.303 + *aResult = false; 1.304 + 1.305 + int32_t tempStart = mStartOffset, tempEnd = mEndOffset; 1.306 + Accessible* tempPosition = mPosition; 1.307 + Accessible* root = GetActiveRoot(); 1.308 + while (true) { 1.309 + Accessible* curPosition = tempPosition; 1.310 + HyperTextAccessible* text = nullptr; 1.311 + // Find the nearest text node using a preorder traversal starting from 1.312 + // the current node. 1.313 + if (!(text = tempPosition->AsHyperText())) { 1.314 + text = SearchForText(tempPosition, false); 1.315 + if (!text) 1.316 + return NS_OK; 1.317 + if (text != curPosition) 1.318 + tempStart = tempEnd = -1; 1.319 + tempPosition = text; 1.320 + } 1.321 + 1.322 + // If the search led to the parent of the node we started on (e.g. when 1.323 + // starting on a text leaf), start the text movement from the end of that 1.324 + // node, otherwise we just default to 0. 1.325 + if (tempEnd == -1) 1.326 + tempEnd = text == curPosition->Parent() ? 1.327 + text->GetChildOffset(curPosition) : 0; 1.328 + 1.329 + // If there's no more text on the current node, try to find the next text 1.330 + // node; if there isn't one, bail out. 1.331 + if (tempEnd == text->CharacterCount()) { 1.332 + if (tempPosition == root) 1.333 + return NS_OK; 1.334 + 1.335 + // If we're currently sitting on a link, try move to either the next 1.336 + // sibling or the parent, whichever is closer to the current end 1.337 + // offset. Otherwise, do a forward search for the next node to land on 1.338 + // (we don't do this in the first case because we don't want to go to the 1.339 + // subtree). 1.340 + Accessible* sibling = tempPosition->NextSibling(); 1.341 + if (tempPosition->IsLink()) { 1.342 + if (sibling && sibling->IsLink()) { 1.343 + tempStart = tempEnd = -1; 1.344 + tempPosition = sibling; 1.345 + } else { 1.346 + tempStart = tempPosition->StartOffset(); 1.347 + tempEnd = tempPosition->EndOffset(); 1.348 + tempPosition = tempPosition->Parent(); 1.349 + } 1.350 + } else { 1.351 + tempPosition = SearchForText(tempPosition, false); 1.352 + if (!tempPosition) 1.353 + return NS_OK; 1.354 + tempStart = tempEnd = -1; 1.355 + } 1.356 + continue; 1.357 + } 1.358 + 1.359 + AccessibleTextBoundary startBoundary, endBoundary; 1.360 + switch (aBoundary) { 1.361 + case CHAR_BOUNDARY: 1.362 + startBoundary = nsIAccessibleText::BOUNDARY_CHAR; 1.363 + endBoundary = nsIAccessibleText::BOUNDARY_CHAR; 1.364 + break; 1.365 + case WORD_BOUNDARY: 1.366 + startBoundary = nsIAccessibleText::BOUNDARY_WORD_START; 1.367 + endBoundary = nsIAccessibleText::BOUNDARY_WORD_END; 1.368 + break; 1.369 + default: 1.370 + return NS_ERROR_INVALID_ARG; 1.371 + } 1.372 + 1.373 + nsAutoString unusedText; 1.374 + int32_t newStart = 0, newEnd = 0, currentEnd = tempEnd; 1.375 + text->TextAtOffset(tempEnd, endBoundary, &newStart, &tempEnd, unusedText); 1.376 + text->TextBeforeOffset(tempEnd, startBoundary, &newStart, &newEnd, unusedText); 1.377 + int32_t potentialStart = newEnd == tempEnd ? newStart : newEnd; 1.378 + tempStart = potentialStart > tempStart ? potentialStart : currentEnd; 1.379 + 1.380 + // The offset range we've obtained might have embedded characters in it, 1.381 + // limit the range to the start of the first occurrence of an embedded 1.382 + // character. 1.383 + Accessible* childAtOffset = nullptr; 1.384 + for (int32_t i = tempStart; i < tempEnd; i++) { 1.385 + childAtOffset = text->GetChildAtOffset(i); 1.386 + if (childAtOffset && nsAccUtils::IsEmbeddedObject(childAtOffset)) { 1.387 + tempEnd = i; 1.388 + break; 1.389 + } 1.390 + } 1.391 + // If there's an embedded character at the very start of the range, we 1.392 + // instead want to traverse into it. So restart the movement with 1.393 + // the child as the starting point. 1.394 + if (childAtOffset && nsAccUtils::IsEmbeddedObject(childAtOffset) && 1.395 + tempStart == childAtOffset->StartOffset()) { 1.396 + tempPosition = childAtOffset; 1.397 + tempStart = tempEnd = -1; 1.398 + continue; 1.399 + } 1.400 + 1.401 + *aResult = true; 1.402 + 1.403 + Accessible* startPosition = mPosition; 1.404 + int32_t oldStart = mStartOffset, oldEnd = mEndOffset; 1.405 + mPosition = tempPosition; 1.406 + mStartOffset = tempStart; 1.407 + mEndOffset = tempEnd; 1.408 + NotifyOfPivotChange(startPosition, oldStart, oldEnd, 1.409 + nsIAccessiblePivot::REASON_TEXT); 1.410 + return NS_OK; 1.411 + } 1.412 +} 1.413 + 1.414 +NS_IMETHODIMP 1.415 +nsAccessiblePivot::MovePreviousByText(TextBoundaryType aBoundary, bool* aResult) 1.416 +{ 1.417 + NS_ENSURE_ARG(aResult); 1.418 + 1.419 + *aResult = false; 1.420 + 1.421 + int32_t tempStart = mStartOffset, tempEnd = mEndOffset; 1.422 + Accessible* tempPosition = mPosition; 1.423 + Accessible* root = GetActiveRoot(); 1.424 + while (true) { 1.425 + Accessible* curPosition = tempPosition; 1.426 + HyperTextAccessible* text; 1.427 + // Find the nearest text node using a reverse preorder traversal starting 1.428 + // from the current node. 1.429 + if (!(text = tempPosition->AsHyperText())) { 1.430 + text = SearchForText(tempPosition, true); 1.431 + if (!text) 1.432 + return NS_OK; 1.433 + if (text != curPosition) 1.434 + tempStart = tempEnd = -1; 1.435 + tempPosition = text; 1.436 + } 1.437 + 1.438 + // If the search led to the parent of the node we started on (e.g. when 1.439 + // starting on a text leaf), start the text movement from the end of that 1.440 + // node, otherwise we just default to 0. 1.441 + if (tempStart == -1) { 1.442 + if (tempPosition != curPosition) 1.443 + tempStart = text == curPosition->Parent() ? 1.444 + text->GetChildOffset(curPosition) : text->CharacterCount(); 1.445 + else 1.446 + tempStart = 0; 1.447 + } 1.448 + 1.449 + // If there's no more text on the current node, try to find the previous 1.450 + // text node; if there isn't one, bail out. 1.451 + if (tempStart == 0) { 1.452 + if (tempPosition == root) 1.453 + return NS_OK; 1.454 + 1.455 + // If we're currently sitting on a link, try move to either the previous 1.456 + // sibling or the parent, whichever is closer to the current end 1.457 + // offset. Otherwise, do a forward search for the next node to land on 1.458 + // (we don't do this in the first case because we don't want to go to the 1.459 + // subtree). 1.460 + Accessible* sibling = tempPosition->PrevSibling(); 1.461 + if (tempPosition->IsLink()) { 1.462 + if (sibling && sibling->IsLink()) { 1.463 + HyperTextAccessible* siblingText = sibling->AsHyperText(); 1.464 + tempStart = tempEnd = siblingText ? 1.465 + siblingText->CharacterCount() : -1; 1.466 + tempPosition = sibling; 1.467 + } else { 1.468 + tempStart = tempPosition->StartOffset(); 1.469 + tempEnd = tempPosition->EndOffset(); 1.470 + tempPosition = tempPosition->Parent(); 1.471 + } 1.472 + } else { 1.473 + HyperTextAccessible* tempText = SearchForText(tempPosition, true); 1.474 + if (!tempText) 1.475 + return NS_OK; 1.476 + tempPosition = tempText; 1.477 + tempStart = tempEnd = tempText->CharacterCount(); 1.478 + } 1.479 + continue; 1.480 + } 1.481 + 1.482 + AccessibleTextBoundary startBoundary, endBoundary; 1.483 + switch (aBoundary) { 1.484 + case CHAR_BOUNDARY: 1.485 + startBoundary = nsIAccessibleText::BOUNDARY_CHAR; 1.486 + endBoundary = nsIAccessibleText::BOUNDARY_CHAR; 1.487 + break; 1.488 + case WORD_BOUNDARY: 1.489 + startBoundary = nsIAccessibleText::BOUNDARY_WORD_START; 1.490 + endBoundary = nsIAccessibleText::BOUNDARY_WORD_END; 1.491 + break; 1.492 + default: 1.493 + return NS_ERROR_INVALID_ARG; 1.494 + } 1.495 + 1.496 + nsAutoString unusedText; 1.497 + int32_t newStart = 0, newEnd = 0, currentStart = tempStart, potentialEnd = 0; 1.498 + text->TextBeforeOffset(tempStart, startBoundary, &newStart, &newEnd, unusedText); 1.499 + if (newStart < tempStart) 1.500 + tempStart = newEnd >= currentStart ? newStart : newEnd; 1.501 + else // XXX: In certain odd cases newStart is equal to tempStart 1.502 + text->TextBeforeOffset(tempStart - 1, startBoundary, &newStart, 1.503 + &tempStart, unusedText); 1.504 + text->TextAtOffset(tempStart, endBoundary, &newStart, &potentialEnd, 1.505 + unusedText); 1.506 + tempEnd = potentialEnd < tempEnd ? potentialEnd : currentStart; 1.507 + 1.508 + // The offset range we've obtained might have embedded characters in it, 1.509 + // limit the range to the start of the last occurrence of an embedded 1.510 + // character. 1.511 + Accessible* childAtOffset = nullptr; 1.512 + for (int32_t i = tempEnd - 1; i >= tempStart; i--) { 1.513 + childAtOffset = text->GetChildAtOffset(i); 1.514 + if (childAtOffset && nsAccUtils::IsEmbeddedObject(childAtOffset)) { 1.515 + tempStart = childAtOffset->EndOffset(); 1.516 + break; 1.517 + } 1.518 + } 1.519 + // If there's an embedded character at the very end of the range, we 1.520 + // instead want to traverse into it. So restart the movement with 1.521 + // the child as the starting point. 1.522 + if (childAtOffset && nsAccUtils::IsEmbeddedObject(childAtOffset) && 1.523 + tempEnd == childAtOffset->EndOffset()) { 1.524 + tempPosition = childAtOffset; 1.525 + tempStart = tempEnd = childAtOffset->AsHyperText()->CharacterCount(); 1.526 + continue; 1.527 + } 1.528 + 1.529 + *aResult = true; 1.530 + 1.531 + Accessible* startPosition = mPosition; 1.532 + int32_t oldStart = mStartOffset, oldEnd = mEndOffset; 1.533 + mPosition = tempPosition; 1.534 + mStartOffset = tempStart; 1.535 + mEndOffset = tempEnd; 1.536 + 1.537 + NotifyOfPivotChange(startPosition, oldStart, oldEnd, 1.538 + nsIAccessiblePivot::REASON_TEXT); 1.539 + return NS_OK; 1.540 + } 1.541 +} 1.542 + 1.543 +NS_IMETHODIMP 1.544 +nsAccessiblePivot::MoveToPoint(nsIAccessibleTraversalRule* aRule, 1.545 + int32_t aX, int32_t aY, bool aIgnoreNoMatch, 1.546 + bool* aResult) 1.547 +{ 1.548 + NS_ENSURE_ARG_POINTER(aResult); 1.549 + NS_ENSURE_ARG_POINTER(aRule); 1.550 + 1.551 + *aResult = false; 1.552 + 1.553 + Accessible* root = GetActiveRoot(); 1.554 + NS_ENSURE_TRUE(root && !root->IsDefunct(), NS_ERROR_NOT_IN_TREE); 1.555 + 1.556 + RuleCache cache(aRule); 1.557 + Accessible* match = nullptr; 1.558 + Accessible* child = root->ChildAtPoint(aX, aY, Accessible::eDeepestChild); 1.559 + while (child && root != child) { 1.560 + uint16_t filtered = nsIAccessibleTraversalRule::FILTER_IGNORE; 1.561 + nsresult rv = cache.ApplyFilter(child, &filtered); 1.562 + NS_ENSURE_SUCCESS(rv, rv); 1.563 + 1.564 + // Ignore any matching nodes that were below this one 1.565 + if (filtered & nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE) 1.566 + match = nullptr; 1.567 + 1.568 + // Match if no node below this is a match 1.569 + if ((filtered & nsIAccessibleTraversalRule::FILTER_MATCH) && !match) { 1.570 + int32_t childX, childY, childWidth, childHeight; 1.571 + child->GetBounds(&childX, &childY, &childWidth, &childHeight); 1.572 + // Double-check child's bounds since the deepest child may have been out 1.573 + // of bounds. This assures we don't return a false positive. 1.574 + if (aX >= childX && aX < childX + childWidth && 1.575 + aY >= childY && aY < childY + childHeight) 1.576 + match = child; 1.577 + } 1.578 + 1.579 + child = child->Parent(); 1.580 + } 1.581 + 1.582 + if (match || !aIgnoreNoMatch) 1.583 + *aResult = MovePivotInternal(match, nsIAccessiblePivot::REASON_POINT); 1.584 + 1.585 + return NS_OK; 1.586 +} 1.587 + 1.588 +// Observer functions 1.589 + 1.590 +NS_IMETHODIMP 1.591 +nsAccessiblePivot::AddObserver(nsIAccessiblePivotObserver* aObserver) 1.592 +{ 1.593 + NS_ENSURE_ARG(aObserver); 1.594 + 1.595 + mObservers.AppendElement(aObserver); 1.596 + 1.597 + return NS_OK; 1.598 +} 1.599 + 1.600 +NS_IMETHODIMP 1.601 +nsAccessiblePivot::RemoveObserver(nsIAccessiblePivotObserver* aObserver) 1.602 +{ 1.603 + NS_ENSURE_ARG(aObserver); 1.604 + 1.605 + return mObservers.RemoveElement(aObserver) ? NS_OK : NS_ERROR_FAILURE; 1.606 +} 1.607 + 1.608 +// Private utility methods 1.609 + 1.610 +bool 1.611 +nsAccessiblePivot::IsDescendantOf(Accessible* aAccessible, Accessible* aAncestor) 1.612 +{ 1.613 + if (!aAncestor || aAncestor->IsDefunct()) 1.614 + return false; 1.615 + 1.616 + // XXX Optimize with IsInDocument() when appropriate. Blocked by bug 759875. 1.617 + Accessible* accessible = aAccessible; 1.618 + do { 1.619 + if (accessible == aAncestor) 1.620 + return true; 1.621 + } while ((accessible = accessible->Parent())); 1.622 + 1.623 + return false; 1.624 +} 1.625 + 1.626 +bool 1.627 +nsAccessiblePivot::MovePivotInternal(Accessible* aPosition, 1.628 + PivotMoveReason aReason) 1.629 +{ 1.630 + nsRefPtr<Accessible> oldPosition = mPosition.forget(); 1.631 + mPosition = aPosition; 1.632 + int32_t oldStart = mStartOffset, oldEnd = mEndOffset; 1.633 + mStartOffset = mEndOffset = -1; 1.634 + 1.635 + return NotifyOfPivotChange(oldPosition, oldStart, oldEnd, aReason); 1.636 +} 1.637 + 1.638 +Accessible* 1.639 +nsAccessiblePivot::AdjustStartPosition(Accessible* aAccessible, 1.640 + RuleCache& aCache, 1.641 + uint16_t* aFilterResult, 1.642 + nsresult* aResult) 1.643 +{ 1.644 + Accessible* matched = aAccessible; 1.645 + *aResult = aCache.ApplyFilter(aAccessible, aFilterResult); 1.646 + 1.647 + if (aAccessible != mRoot && aAccessible != mModalRoot) { 1.648 + for (Accessible* temp = aAccessible->Parent(); 1.649 + temp && temp != mRoot && temp != mModalRoot; temp = temp->Parent()) { 1.650 + uint16_t filtered = nsIAccessibleTraversalRule::FILTER_IGNORE; 1.651 + *aResult = aCache.ApplyFilter(temp, &filtered); 1.652 + NS_ENSURE_SUCCESS(*aResult, nullptr); 1.653 + if (filtered & nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE) { 1.654 + *aFilterResult = filtered; 1.655 + matched = temp; 1.656 + } 1.657 + } 1.658 + } 1.659 + 1.660 + return matched; 1.661 +} 1.662 + 1.663 +Accessible* 1.664 +nsAccessiblePivot::SearchBackward(Accessible* aAccessible, 1.665 + nsIAccessibleTraversalRule* aRule, 1.666 + bool aSearchCurrent, 1.667 + nsresult* aResult) 1.668 +{ 1.669 + *aResult = NS_OK; 1.670 + 1.671 + // Initial position could be unset, in that case return null. 1.672 + if (!aAccessible) 1.673 + return nullptr; 1.674 + 1.675 + RuleCache cache(aRule); 1.676 + uint16_t filtered = nsIAccessibleTraversalRule::FILTER_IGNORE; 1.677 + Accessible* accessible = AdjustStartPosition(aAccessible, cache, 1.678 + &filtered, aResult); 1.679 + NS_ENSURE_SUCCESS(*aResult, nullptr); 1.680 + 1.681 + if (aSearchCurrent && (filtered & nsIAccessibleTraversalRule::FILTER_MATCH)) { 1.682 + return accessible; 1.683 + } 1.684 + 1.685 + Accessible* root = GetActiveRoot(); 1.686 + while (accessible != root) { 1.687 + Accessible* parent = accessible->Parent(); 1.688 + int32_t idxInParent = accessible->IndexInParent(); 1.689 + while (idxInParent > 0) { 1.690 + if (!(accessible = parent->GetChildAt(--idxInParent))) 1.691 + continue; 1.692 + 1.693 + *aResult = cache.ApplyFilter(accessible, &filtered); 1.694 + NS_ENSURE_SUCCESS(*aResult, nullptr); 1.695 + 1.696 + Accessible* lastChild = nullptr; 1.697 + while (!(filtered & nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE) && 1.698 + (lastChild = accessible->LastChild())) { 1.699 + parent = accessible; 1.700 + accessible = lastChild; 1.701 + idxInParent = accessible->IndexInParent(); 1.702 + *aResult = cache.ApplyFilter(accessible, &filtered); 1.703 + NS_ENSURE_SUCCESS(*aResult, nullptr); 1.704 + } 1.705 + 1.706 + if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH) 1.707 + return accessible; 1.708 + } 1.709 + 1.710 + if (!(accessible = parent)) 1.711 + break; 1.712 + 1.713 + *aResult = cache.ApplyFilter(accessible, &filtered); 1.714 + NS_ENSURE_SUCCESS(*aResult, nullptr); 1.715 + 1.716 + if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH) 1.717 + return accessible; 1.718 + } 1.719 + 1.720 + return nullptr; 1.721 +} 1.722 + 1.723 +Accessible* 1.724 +nsAccessiblePivot::SearchForward(Accessible* aAccessible, 1.725 + nsIAccessibleTraversalRule* aRule, 1.726 + bool aSearchCurrent, 1.727 + nsresult* aResult) 1.728 +{ 1.729 + *aResult = NS_OK; 1.730 + 1.731 + // Initial position could be not set, in that case begin search from root. 1.732 + Accessible* root = GetActiveRoot(); 1.733 + Accessible* accessible = (!aAccessible) ? root : aAccessible; 1.734 + 1.735 + RuleCache cache(aRule); 1.736 + 1.737 + uint16_t filtered = nsIAccessibleTraversalRule::FILTER_IGNORE; 1.738 + accessible = AdjustStartPosition(accessible, cache, &filtered, aResult); 1.739 + NS_ENSURE_SUCCESS(*aResult, nullptr); 1.740 + if (aSearchCurrent && (filtered & nsIAccessibleTraversalRule::FILTER_MATCH)) 1.741 + return accessible; 1.742 + 1.743 + while (true) { 1.744 + Accessible* firstChild = nullptr; 1.745 + while (!(filtered & nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE) && 1.746 + (firstChild = accessible->FirstChild())) { 1.747 + accessible = firstChild; 1.748 + *aResult = cache.ApplyFilter(accessible, &filtered); 1.749 + NS_ENSURE_SUCCESS(*aResult, nullptr); 1.750 + 1.751 + if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH) 1.752 + return accessible; 1.753 + } 1.754 + 1.755 + Accessible* sibling = nullptr; 1.756 + Accessible* temp = accessible; 1.757 + do { 1.758 + if (temp == root) 1.759 + break; 1.760 + 1.761 + sibling = temp->NextSibling(); 1.762 + 1.763 + if (sibling) 1.764 + break; 1.765 + } while ((temp = temp->Parent())); 1.766 + 1.767 + if (!sibling) 1.768 + break; 1.769 + 1.770 + accessible = sibling; 1.771 + *aResult = cache.ApplyFilter(accessible, &filtered); 1.772 + NS_ENSURE_SUCCESS(*aResult, nullptr); 1.773 + 1.774 + if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH) 1.775 + return accessible; 1.776 + } 1.777 + 1.778 + return nullptr; 1.779 +} 1.780 + 1.781 +HyperTextAccessible* 1.782 +nsAccessiblePivot::SearchForText(Accessible* aAccessible, bool aBackward) 1.783 +{ 1.784 + Accessible* root = GetActiveRoot(); 1.785 + Accessible* accessible = aAccessible; 1.786 + while (true) { 1.787 + Accessible* child = nullptr; 1.788 + 1.789 + while ((child = (aBackward ? accessible->LastChild() : 1.790 + accessible->FirstChild()))) { 1.791 + accessible = child; 1.792 + if (child->IsHyperText()) 1.793 + return child->AsHyperText(); 1.794 + } 1.795 + 1.796 + Accessible* sibling = nullptr; 1.797 + Accessible* temp = accessible; 1.798 + do { 1.799 + if (temp == root) 1.800 + break; 1.801 + 1.802 + if (temp != aAccessible && temp->IsHyperText()) 1.803 + return temp->AsHyperText(); 1.804 + 1.805 + sibling = aBackward ? temp->PrevSibling() : temp->NextSibling(); 1.806 + 1.807 + if (sibling) 1.808 + break; 1.809 + } while ((temp = temp->Parent())); 1.810 + 1.811 + if (!sibling) 1.812 + break; 1.813 + 1.814 + accessible = sibling; 1.815 + if (accessible->IsHyperText()) 1.816 + return accessible->AsHyperText(); 1.817 + } 1.818 + 1.819 + return nullptr; 1.820 +} 1.821 + 1.822 + 1.823 +bool 1.824 +nsAccessiblePivot::NotifyOfPivotChange(Accessible* aOldPosition, 1.825 + int32_t aOldStart, int32_t aOldEnd, 1.826 + int16_t aReason) 1.827 +{ 1.828 + if (aOldPosition == mPosition && 1.829 + aOldStart == mStartOffset && aOldEnd == mEndOffset) 1.830 + return false; 1.831 + 1.832 + nsTObserverArray<nsCOMPtr<nsIAccessiblePivotObserver> >::ForwardIterator iter(mObservers); 1.833 + while (iter.HasMore()) { 1.834 + nsIAccessiblePivotObserver* obs = iter.GetNext(); 1.835 + obs->OnPivotChanged(this, aOldPosition, aOldStart, aOldEnd, aReason); 1.836 + } 1.837 + 1.838 + return true; 1.839 +} 1.840 + 1.841 +nsresult 1.842 +RuleCache::ApplyFilter(Accessible* aAccessible, uint16_t* aResult) 1.843 +{ 1.844 + *aResult = nsIAccessibleTraversalRule::FILTER_IGNORE; 1.845 + 1.846 + if (!mAcceptRoles) { 1.847 + nsresult rv = mRule->GetMatchRoles(&mAcceptRoles, &mAcceptRolesLength); 1.848 + NS_ENSURE_SUCCESS(rv, rv); 1.849 + rv = mRule->GetPreFilter(&mPreFilter); 1.850 + NS_ENSURE_SUCCESS(rv, rv); 1.851 + } 1.852 + 1.853 + if (mPreFilter) { 1.854 + uint64_t state = aAccessible->State(); 1.855 + 1.856 + if ((nsIAccessibleTraversalRule::PREFILTER_INVISIBLE & mPreFilter) && 1.857 + (state & states::INVISIBLE)) 1.858 + return NS_OK; 1.859 + 1.860 + if ((nsIAccessibleTraversalRule::PREFILTER_OFFSCREEN & mPreFilter) && 1.861 + (state & states::OFFSCREEN)) 1.862 + return NS_OK; 1.863 + 1.864 + if ((nsIAccessibleTraversalRule::PREFILTER_NOT_FOCUSABLE & mPreFilter) && 1.865 + !(state & states::FOCUSABLE)) 1.866 + return NS_OK; 1.867 + 1.868 + if (nsIAccessibleTraversalRule::PREFILTER_ARIA_HIDDEN & mPreFilter) { 1.869 + nsIContent* content = aAccessible->GetContent(); 1.870 + if (content && 1.871 + nsAccUtils::HasDefinedARIAToken(content, nsGkAtoms::aria_hidden) && 1.872 + !content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_hidden, 1.873 + nsGkAtoms::_false, eCaseMatters)) { 1.874 + *aResult |= nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE; 1.875 + return NS_OK; 1.876 + } 1.877 + } 1.878 + 1.879 + if ((nsIAccessibleTraversalRule::PREFILTER_TRANSPARENT & mPreFilter) && 1.880 + !(state & states::OPAQUE1)) { 1.881 + nsIFrame* frame = aAccessible->GetFrame(); 1.882 + if (frame->StyleDisplay()->mOpacity == 0.0f) { 1.883 + *aResult |= nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE; 1.884 + return NS_OK; 1.885 + } 1.886 + } 1.887 + } 1.888 + 1.889 + if (mAcceptRolesLength > 0) { 1.890 + uint32_t accessibleRole = aAccessible->Role(); 1.891 + bool matchesRole = false; 1.892 + for (uint32_t idx = 0; idx < mAcceptRolesLength; idx++) { 1.893 + matchesRole = mAcceptRoles[idx] == accessibleRole; 1.894 + if (matchesRole) 1.895 + break; 1.896 + } 1.897 + if (!matchesRole) 1.898 + return NS_OK; 1.899 + } 1.900 + 1.901 + return mRule->Match(aAccessible, aResult); 1.902 +}