Changeset 61134 in webkit
- Timestamp:
- Jun 14, 2010 12:10:59 PM (14 years ago)
- Location:
- trunk
- Files:
-
- 4 added
- 5 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r61132 r61134 1 2010-06-14 Antonio Gomes <tonikitoo@webkit.org> 2 3 Reviewed by Simon Fraser. 4 5 Spatial Navigation: make it work with focusable elements in overflow content 6 https://bugs.webkit.org/show_bug.cgi?id=36463 7 8 * fast/events/spatial-navigation/snav-div-scrollable-but-without-focusable-content-expected.txt: Added. 9 * fast/events/spatial-navigation/snav-div-scrollable-but-without-focusable-content.html: Added. 10 * fast/events/spatial-navigation/snav-clipped-overflow-content-expected.txt: Added. 11 * fast/events/spatial-navigation/snav-clipped-overflow-content.html: Added. 12 1 13 2010-06-14 Chris Fleizach <cfleizach@apple.com> 2 14 -
trunk/WebCore/ChangeLog
r61133 r61134 1 2010-06-14 Antonio Gomes <tonikitoo@webkit.org> 2 3 Reviewed by Simon Fraser and Kenneth Christiansen. 4 5 Spatial Navigation: make it work with focusable elements in overflow content 6 https://bugs.webkit.org/show_bug.cgi?id=36463 7 8 This patch addresses the problem with Spatial Navigation. It currently does not 9 properly traverse scrollable contents, including scrollable div's. For this to work, 10 a new class member called scrollableEnclosingBox was introduced to FocusCandidate class which 11 keeps track of the current scrollable box Node wrapping a FocusCandidate. 12 13 To make use of enclosingScrollableBox of FocusCandidate, the DOM traversal routine 14 (FocusController::findNextFocusableInDirection) was changed as follows: when it 15 encounters a scrollable Node, each focusable node which is 'inner' keeps track of 16 the container reference. By the time a sibling of the scrollable Node is encountered, 17 there is no need to track this reference any more and the traversal algorithm continues 18 normally. 19 20 The common case is obviously that there is no scrollable container wrapping it. 21 22 updateFocusCandiditeIfCloser logic was also adapted to fit the need of the 23 newly introduced enclosingScrollableBox class member, getting simpler and more 24 easily maintainable. 25 26 Tests: fast/events/spatial-navigation/snav-div-scrollable-but-without-focusable-content.html 27 fast/events/spatial-navigation/snav-clipped-overflow-content.html 28 29 * page/FocusController.cpp: 30 (WebCore::updateFocusCandidateInSameContainer): 31 (WebCore::updateFocusCandidateIfCloser): 32 (WebCore::FocusController::findFocusableNodeInDirection): 33 (WebCore::FocusController::deepFindFocusableNodeInDirection): 34 * page/SpatialNavigation.cpp: 35 (WebCore::isScrollableContainerNode): 36 * page/SpatialNavigation.h: 37 (WebCore::FocusCandidate::FocusCandidate): 38 (WebCore::FocusCandidate::isInScrollableContainer): 39 1 40 2010-06-14 Jian Li <jianli@chromium.org> 2 41 -
trunk/WebCore/page/FocusController.cpp
r60873 r61134 342 342 } 343 343 344 // FIXME: Make this method more modular, and simpler to understand and maintain. 345 static void updateFocusCandidateIfCloser(Node* focusedNode, const FocusCandidate& candidate, FocusCandidate& closest) 346 { 347 bool sameDocument = candidate.document() == closest.document(); 348 if (sameDocument) { 349 if (closest.alignment > candidate.alignment 350 || (closest.parentAlignment && candidate.alignment > closest.parentAlignment)) 351 return; 352 } else if (closest.alignment > candidate.alignment 353 && (closest.parentAlignment && candidate.alignment > closest.parentAlignment)) 354 return; 355 356 if (candidate.alignment != None 357 || (closest.parentAlignment >= candidate.alignment 358 && closest.document() == candidate.document())) { 359 360 // If we are now in an higher precedent case, lets reset the current closest's 361 // distance so we force it to be bigger than any result we will get from 362 // spatialDistance(). 363 if (closest.alignment < candidate.alignment 364 && closest.parentAlignment < candidate.alignment) 365 closest.distance = maxDistance(); 366 } 367 368 // Bail out if candidate's distance is larger than that of the closest candidate. 369 if (candidate.distance >= closest.distance) 370 return; 371 344 static void updateFocusCandidateInSameContainer(const FocusCandidate& candidate, FocusCandidate& closest) 345 { 372 346 if (closest.isNull()) { 373 347 closest = candidate; … … 375 349 } 376 350 377 // If the focused node and the candadate are in the same document and current 378 // closest candidate is not in an {i}frame that is preferable to get focused ... 379 if (focusedNode->document() == candidate.document() 380 && candidate.distance < closest.parentDistance) 351 if (candidate.alignment == closest.alignment) { 352 if (candidate.distance < closest.distance) 353 closest = candidate; 354 return; 355 } 356 357 if (candidate.alignment > closest.alignment) 381 358 closest = candidate; 382 else if (focusedNode->document() != candidate.document()) { 383 // If the focusedNode is in an inner document and candidate is in a 384 // different document, we only consider to change focus if there is not 385 // another already good focusable candidate in the same document as focusedNode. 386 if (!((isInRootDocument(candidate.node) && !isInRootDocument(focusedNode)) 387 && focusedNode->document() == closest.document())) 359 } 360 361 static void updateFocusCandidateIfCloser(Node* focusedNode, const FocusCandidate& candidate, FocusCandidate& closest) 362 { 363 // First, check the common case: neither candidate nor closest are 364 // inside scrollable content, then no need to care about enclosingScrollableBox 365 // heuristics or parent{Distance,Alignment}, but only distance and alignment. 366 if (!candidate.inScrollableContainer() && !closest.inScrollableContainer()) { 367 updateFocusCandidateInSameContainer(candidate, closest); 368 return; 369 } 370 371 bool sameContainer = candidate.document() == closest.document() && candidate.enclosingScrollableBox == closest.enclosingScrollableBox; 372 373 // Second, if candidate and closest are in the same "container" (i.e. {i}frame or any 374 // scrollable block element), we can handle them as common case. 375 if (sameContainer) { 376 updateFocusCandidateInSameContainer(candidate, closest); 377 return; 378 } 379 380 // Last, we are considering moving to a candidate located in a different enclosing 381 // scrollable box than closest. 382 bool isInInnerDocument = !isInRootDocument(focusedNode); 383 384 bool sameContainerAsCandidate = isInInnerDocument ? focusedNode->document() == candidate.document() : 385 focusedNode->isDescendantOf(candidate.enclosingScrollableBox); 386 387 bool sameContainerAsClosest = isInInnerDocument ? focusedNode->document() == closest.document() : 388 focusedNode->isDescendantOf(closest.enclosingScrollableBox); 389 390 // sameContainerAsCandidate and sameContainerAsClosest are mutually exclusive. 391 ASSERT(!(sameContainerAsCandidate && sameContainerAsClosest)); 392 393 if (sameContainerAsCandidate) { 394 closest = candidate; 395 return; 396 } 397 398 if (sameContainerAsClosest) { 399 // Nothing to be done. 400 return; 401 } 402 403 // NOTE: !sameContainerAsCandidate && !sameContainerAsClosest 404 // If distance is shorter, and we are talking about scrollable container, 405 // lets compare parent distance and alignment before anything. 406 if (candidate.distance < closest.distance) { 407 if (candidate.alignment >= closest.parentAlignment 408 || candidate.parentAlignment == closest.parentAlignment) { 388 409 closest = candidate; 410 return; 411 } 412 413 } else if (candidate.parentDistance < closest.distance) { 414 if (candidate.parentAlignment >= closest.alignment) { 415 closest = candidate; 416 return; 417 } 389 418 } 390 419 } … … 398 427 ASSERT(candidateParent.isNull() 399 428 || candidateParent.node->hasTagName(frameTag) 400 || candidateParent.node->hasTagName(iframeTag)); 429 || candidateParent.node->hasTagName(iframeTag) 430 || isScrollableContainerNode(candidateParent.node)); 401 431 402 432 // Walk all the child nodes and update closestFocusCandidate if we find a nearer node. 403 433 Node* candidate = outer; 404 434 while (candidate) { 435 405 436 // Inner documents case. 406 407 if (candidate->isFrameOwnerElement()) 437 if (candidate->isFrameOwnerElement()) { 408 438 deepFindFocusableNodeInDirection(candidate, focusedNode, direction, event, closestFocusCandidate); 409 else if (candidate != focusedNode && candidate->isKeyboardFocusable(event)) { 439 440 // Scrollable block elements (e.g. <div>, etc) case. 441 } else if (isScrollableContainerNode(candidate)) { 442 deepFindFocusableNodeInDirection(candidate, focusedNode, direction, event, closestFocusCandidate); 443 candidate = candidate->traverseNextSibling(); 444 continue; 445 446 } else if (candidate != focusedNode && candidate->isKeyboardFocusable(event)) { 410 447 FocusCandidate currentFocusCandidate(candidate); 448 449 // There are two ways to identify we are in a recursive call from deepFindFocusableNodeInDirection 450 // (i.e. processing an element in an iframe, frame or a scrollable block element): 451 452 // 1) If candidateParent is not null, and it holds the distance and alignment data of the 453 // parent container element itself; 454 // 2) Parent of outer is <frame> or <iframe>; 455 // 3) Parent is any other scrollable block element. 456 if (!candidateParent.isNull()) { 457 currentFocusCandidate.parentAlignment = candidateParent.alignment; 458 currentFocusCandidate.parentDistance = candidateParent.distance; 459 currentFocusCandidate.enclosingScrollableBox = candidateParent.node; 460 461 } else if (!isInRootDocument(outer)) { 462 if (Document* document = static_cast<Document*>(outer->parent())) 463 currentFocusCandidate.enclosingScrollableBox = static_cast<Node*>(document->ownerElement()); 464 465 } else if (isScrollableContainerNode(outer->parent())) 466 currentFocusCandidate.enclosingScrollableBox = outer->parent(); 411 467 412 468 // Get distance and alignment from current candidate. … … 419 475 } 420 476 421 // If candidateParent is not null, it means that we are in a recursive call422 // from deepFineFocusableNodeInDirection (i.e. processing an element in an iframe),423 // and holds the distance and alignment data of the iframe element itself.424 if (!candidateParent.isNull()) {425 currentFocusCandidate.parentAlignment = candidateParent.alignment;426 currentFocusCandidate.parentDistance = candidateParent.distance;427 }428 429 477 updateFocusCandidateIfCloser(focusedNode, currentFocusCandidate, closestFocusCandidate); 430 478 } … … 438 486 FocusCandidate& closestFocusCandidate) 439 487 { 440 ASSERT(container->hasTagName(frameTag) || container->hasTagName(iframeTag)); 488 ASSERT(container->hasTagName(frameTag) 489 || container->hasTagName(iframeTag) 490 || isScrollableContainerNode(container)); 441 491 442 492 // Track if focusedNode is a descendant of the current container node being processed. … … 458 508 firstChild = innerDocument->firstChild(); 459 509 510 // Scrollable block elements (e.g. <div>, etc) 511 } else if (isScrollableContainerNode(container)) { 512 513 firstChild = container->firstChild(); 514 descendantOfContainer = focusedNode->isDescendantOf(container); 460 515 } 461 516 -
trunk/WebCore/page/SpatialNavigation.cpp
r60943 r61134 528 528 } 529 529 530 bool isScrollableContainerNode(Node* node) 531 { 532 if (!node) 533 return false; 534 535 if (RenderObject* renderer = node->renderer()) { 536 return (renderer->isBox() && toRenderBox(renderer)->canBeScrolledAndHasScrollableArea() 537 && node->hasChildNodes() && !node->isDocumentNode()); 538 } 539 540 return false; 541 } 542 530 543 } // namespace WebCore -
trunk/WebCore/page/SpatialNavigation.h
r59046 r61134 98 98 FocusCandidate() 99 99 : node(0) 100 , enclosingScrollableBox(0) 100 101 , distance(maxDistance()) 101 102 , parentDistance(maxDistance()) … … 107 108 FocusCandidate(Node* n) 108 109 : node(n) 110 , enclosingScrollableBox(0) 109 111 , distance(maxDistance()) 110 112 , parentDistance(maxDistance()) … … 115 117 116 118 bool isNull() const { return !node; } 119 bool inScrollableContainer() const { return node && enclosingScrollableBox; } 117 120 Document* document() const { return node ? node->document() : 0; } 118 121 119 122 Node* node; 123 Node* enclosingScrollableBox; 120 124 long long distance; 121 125 long long parentDistance; … … 129 133 bool hasOffscreenRect(Node*); 130 134 bool isInRootDocument(Node*); 135 bool isScrollableContainerNode(Node*); 131 136 132 137 } // namspace WebCore
Note: See TracChangeset
for help on using the changeset viewer.