WebCore/ChangeLog

 12010-11-22 Yael Aharon <yael.aharon@nokia.com>
 2
 3 Reviewed by NOBODY (OOPS!).
 4
 5 Spatial Navigation: Code cleanup
 6 https://bugs.webkit.org/show_bug.cgi?id=49442
 7
 8 Remove code that is not used anymore after http://trac.webkit.org/changeset/72522.
 9 Added const to canBeScrolledIntoView.
 10 No new tests because this is only code cleanup.
 11
 12 * page/FocusController.cpp:
 13 * page/FocusController.h:
 14 * page/SpatialNavigation.cpp:
 15 (WebCore::canBeScrolledIntoView):
 16 * page/SpatialNavigation.h:
 17
1182010-11-22 Yael Aharon <yael.aharon@nokia.com>, Chang Shu <chang.shu@nokia.com>
219
320 Reviewed by Antonio Gomes.
72526

WebCore/page/FocusController.cpp

@@bool FocusController::advanceFocusInDocu
292292 return true;
293293}
294294
295 static void updateFocusCandidateInSameContainer(const FocusCandidate& candidate, FocusCandidate& closest)
296 {
297  if (closest.isNull()) {
298  closest = candidate;
299  return;
300  }
301 
302  if (candidate.alignment == closest.alignment) {
303  if (candidate.distance < closest.distance)
304  closest = candidate;
305  return;
306  }
307 
308  if (candidate.alignment > closest.alignment)
309  closest = candidate;
310 }
311 
312 static void updateFocusCandidateIfCloser(Node* focusedNode, const FocusCandidate& candidate, FocusCandidate& closest)
313 {
314  // First, check the common case: neither candidate nor closest are
315  // inside scrollable content, then no need to care about enclosingScrollableBox
316  // heuristics or parent{Distance,Alignment}, but only distance and alignment.
317  if (!candidate.inScrollableContainer() && !closest.inScrollableContainer()) {
318  updateFocusCandidateInSameContainer(candidate, closest);
319  return;
320  }
321 
322  bool sameContainer = candidate.document() == closest.document() && candidate.enclosingScrollableBox == closest.enclosingScrollableBox;
323 
324  // Second, if candidate and closest are in the same "container" (i.e. {i}frame or any
325  // scrollable block element), we can handle them as common case.
326  if (sameContainer) {
327  updateFocusCandidateInSameContainer(candidate, closest);
328  return;
329  }
330 
331  // Last, we are considering moving to a candidate located in a different enclosing
332  // scrollable box than closest.
333  bool isInInnerDocument = !isInRootDocument(focusedNode);
334 
335  bool sameContainerAsCandidate = isInInnerDocument ? focusedNode->document() == candidate.document() :
336  focusedNode->isDescendantOf(candidate.enclosingScrollableBox);
337 
338  bool sameContainerAsClosest = isInInnerDocument ? focusedNode->document() == closest.document() :
339  focusedNode->isDescendantOf(closest.enclosingScrollableBox);
340 
341  // sameContainerAsCandidate and sameContainerAsClosest are mutually exclusive.
342  ASSERT(!(sameContainerAsCandidate && sameContainerAsClosest));
343 
344  if (sameContainerAsCandidate) {
345  closest = candidate;
346  return;
347  }
348 
349  if (sameContainerAsClosest) {
350  // Nothing to be done.
351  return;
352  }
353 
354  // NOTE: !sameContainerAsCandidate && !sameContainerAsClosest
355  // If distance is shorter, and we are talking about scrollable container,
356  // lets compare parent distance and alignment before anything.
357  if (candidate.distance < closest.distance) {
358  if (candidate.alignment >= closest.parentAlignment
359  || candidate.parentAlignment == closest.parentAlignment) {
360  closest = candidate;
361  return;
362  }
363 
364  } else if (candidate.parentDistance < closest.distance) {
365  if (candidate.parentAlignment >= closest.alignment) {
366  closest = candidate;
367  return;
368  }
369  }
370 }
371 
372 void FocusController::findFocusableNodeInDirection(Node* outer, Node* focusedNode,
373  FocusDirection direction, KeyboardEvent* event,
374  FocusCandidate& closest, const FocusCandidate& candidateParent)
375 {
376  ASSERT(outer);
377  ASSERT(candidateParent.isNull()
378  || candidateParent.node->hasTagName(frameTag)
379  || candidateParent.node->hasTagName(iframeTag)
380  || isScrollableContainerNode(candidateParent.node));
381 
382  // Walk all the child nodes and update closest if we find a nearer node.
383  Node* node = outer;
384  while (node) {
385 
386  // Inner documents case.
387  if (node->isFrameOwnerElement()) {
388  deepFindFocusableNodeInDirection(node, focusedNode, direction, event, closest);
389 
390  // Scrollable block elements (e.g. <div>, etc) case.
391  } else if (isScrollableContainerNode(node)) {
392  deepFindFocusableNodeInDirection(node, focusedNode, direction, event, closest);
393  node = node->traverseNextSibling();
394  continue;
395 
396  } else if (node != focusedNode && node->isKeyboardFocusable(event)) {
397  FocusCandidate candidate(node);
398 
399  // There are two ways to identify we are in a recursive call from deepFindFocusableNodeInDirection
400  // (i.e. processing an element in an iframe, frame or a scrollable block element):
401 
402  // 1) If candidateParent is not null, and it holds the distance and alignment data of the
403  // parent container element itself;
404  // 2) Parent of outer is <frame> or <iframe>;
405  // 3) Parent is any other scrollable block element.
406  if (!candidateParent.isNull()) {
407  candidate.parentAlignment = candidateParent.alignment;
408  candidate.parentDistance = candidateParent.distance;
409  candidate.enclosingScrollableBox = candidateParent.node;
410 
411  } else if (!isInRootDocument(outer)) {
412  if (Document* document = static_cast<Document*>(outer->parentNode()))
413  candidate.enclosingScrollableBox = static_cast<Node*>(document->ownerElement());
414 
415  } else if (isScrollableContainerNode(outer->parentNode()))
416  candidate.enclosingScrollableBox = outer->parentNode();
417 
418  // Get distance and alignment from current candidate.
419  distanceDataForNode(direction, focusedNode, candidate);
420 
421  // Bail out if distance is maximum.
422  if (candidate.distance == maxDistance()) {
423  node = node->traverseNextNode(outer->parentNode());
424  continue;
425  }
426 
427  updateFocusCandidateIfCloser(focusedNode, candidate, closest);
428  }
429 
430  node = node->traverseNextNode(outer->parentNode());
431  }
432 }
433 
434 void FocusController::deepFindFocusableNodeInDirection(Node* container, Node* focusedNode,
435  FocusDirection direction, KeyboardEvent* event,
436  FocusCandidate& closest)
437 {
438  ASSERT(container->hasTagName(frameTag)
439  || container->hasTagName(iframeTag)
440  || isScrollableContainerNode(container));
441 
442  // Track if focusedNode is a descendant of the current container node being processed.
443  bool descendantOfContainer = false;
444  Node* firstChild = 0;
445 
446  // Iframe or Frame.
447  if (container->hasTagName(frameTag) || container->hasTagName(iframeTag)) {
448 
449  HTMLFrameOwnerElement* owner = static_cast<HTMLFrameOwnerElement*>(container);
450  if (!owner->contentFrame())
451  return;
452 
453  Document* innerDocument = owner->contentFrame()->document();
454  if (!innerDocument)
455  return;
456 
457  descendantOfContainer = isNodeDeepDescendantOfDocument(focusedNode, innerDocument);
458  firstChild = innerDocument->firstChild();
459 
460  // Scrollable block elements (e.g. <div>, etc)
461  } else if (isScrollableContainerNode(container)) {
462 
463  firstChild = container->firstChild();
464  descendantOfContainer = focusedNode->isDescendantOf(container);
465  }
466 
467  if (descendantOfContainer) {
468  findFocusableNodeInDirection(firstChild, focusedNode, direction, event, closest);
469  return;
470  }
471 
472  // Check if the current container element itself is a good candidate
473  // to move focus to. If it is, then we traverse its inner nodes.
474  FocusCandidate candidateParent = FocusCandidate(container);
475  distanceDataForNode(direction, focusedNode, candidateParent);
476 
477  // Bail out if distance is maximum.
478  if (candidateParent.distance == maxDistance())
479  return;
480 
481  // FIXME: Consider alignment?
482  if (candidateParent.distance < closest.distance)
483  findFocusableNodeInDirection(firstChild, focusedNode, direction, event, closest, candidateParent);
484 }
485 
486295static bool relinquishesEditingFocus(Node *node)
487296{
488297 ASSERT(node);
72526

WebCore/page/FocusController.h

@@private:
6363 bool advanceFocusDirectionally(FocusDirection, KeyboardEvent*);
6464 bool advanceFocusInDocumentOrder(FocusDirection, KeyboardEvent*, bool initialFocus);
6565
66  void findFocusableNodeInDirection(Node* outter, Node*, FocusDirection, KeyboardEvent*,
67  FocusCandidate& closestFocusCandidate,
68  const FocusCandidate& parentCandidate = FocusCandidate());
69  void deepFindFocusableNodeInDirection(Node* container, Node* focused, FocusDirection, KeyboardEvent*, FocusCandidate&);
70 
7166 bool advanceFocusDirectionallyInContainer(Node* container, const IntRect& startingRect, FocusDirection, KeyboardEvent*);
7267 void findFocusCandidateInContainer(Node* container, const IntRect& startingRect, FocusDirection, KeyboardEvent*, FocusCandidate& closest);
7368
72526

WebCore/page/SpatialNavigation.cpp

4141
4242namespace WebCore {
4343
44 static long long spatialDistance(FocusDirection, const IntRect&, const IntRect&);
45 static IntRect renderRectRelativeToRootDocument(RenderObject*);
4644static RectsAlignment alignmentForRects(FocusDirection, const IntRect&, const IntRect&, const IntSize& viewSize);
4745static bool areRectsFullyAligned(FocusDirection, const IntRect&, const IntRect&);
4846static bool areRectsPartiallyAligned(FocusDirection, const IntRect&, const IntRect&);
4947static bool areRectsMoreThanFullScreenApart(FocusDirection direction, const IntRect& curRect, const IntRect& targetRect, const IntSize& viewSize);
5048static bool isRectInDirection(FocusDirection, const IntRect&, const IntRect&);
5149static void deflateIfOverlapped(IntRect&, IntRect&);
52 static bool checkNegativeCoordsForNode(Node*, const IntRect&);
5350static IntRect rectToAbsoluteCoordinates(Frame* initialFrame, const IntRect& rect);
5451static void entryAndExitPointsForDirection(FocusDirection direction, const IntRect& startingRect, const IntRect& potentialRect, IntPoint& exitPoint, IntPoint& entryPoint);
5552

@@bool isSpatialNavigationEnabled(const Fr
7067 return (frame && frame->settings() && frame->settings()->isSpatialNavigationEnabled());
7168}
7269
73 void distanceDataForNode(FocusDirection direction, Node* start, FocusCandidate& candidate)
74 {
75  RenderObject* startRender = start->renderer();
76  if (!startRender) {
77  candidate.distance = maxDistance();
78  return;
79  }
80 
81  RenderObject* destRender = candidate.node->renderer();
82  if (!destRender) {
83  candidate.distance = maxDistance();
84  return;
85  }
86 
87  IntRect curRect = renderRectRelativeToRootDocument(startRender);
88  IntRect targetRect = renderRectRelativeToRootDocument(destRender);
89 
90  // The bounding rectangle of two consecutive nodes can overlap. In such cases,
91  // deflate both.
92  deflateIfOverlapped(curRect, targetRect);
93 
94  // If empty rects or negative width or height, bail out.
95  if (curRect.isEmpty() || targetRect.isEmpty()
96  || targetRect.width() <= 0 || targetRect.height() <= 0) {
97  candidate.distance = maxDistance();
98  return;
99  }
100 
101  // Negative coordinates can be used if node is scrolled up offscreen.
102  if (!checkNegativeCoordsForNode(start, curRect)) {
103  candidate.distance = maxDistance();
104  return;
105  }
106 
107  if (!checkNegativeCoordsForNode(candidate.node, targetRect)) {
108  candidate.distance = maxDistance();
109  return;
110  }
111 
112  if (!isRectInDirection(direction, curRect, targetRect)) {
113  candidate.distance = maxDistance();
114  return;
115  }
116 
117  // The distance between two nodes is not to be considered alone when evaluating/looking
118  // for the best focus candidate node. Alignment of rects can be also a good point to be
119  // considered in order to make the algorithm to behavior in a more intuitive way.
120  IntSize viewSize = candidate.node->document()->page()->mainFrame()->view()->visibleContentRect().size();
121  candidate.alignment = alignmentForRects(direction, curRect, targetRect, viewSize);
122  candidate.distance = spatialDistance(direction, curRect, targetRect);
123 }
124 
125 // FIXME: This function does not behave correctly with transformed frames.
126 static IntRect renderRectRelativeToRootDocument(RenderObject* render)
127 {
128  ASSERT(render && render->node());
129 
130  IntRect rect = render->node()->getRect();
131 
132  // In cases when the |render|'s associated node is in a scrollable inner
133  // document, we only consider its scrollOffset if it is not offscreen.
134  Node* node = render->node();
135  Document* mainDocument = node->document()->page()->mainFrame()->document();
136  bool considerScrollOffset = !(hasOffscreenRect(node) && node->document() != mainDocument);
137 
138  if (considerScrollOffset) {
139  if (FrameView* frameView = render->node()->document()->view())
140  rect.move(-frameView->scrollOffset());
141  }
142 
143  // Handle nested frames.
144  for (Frame* frame = render->document()->frame(); frame; frame = frame->tree()->parent()) {
145  if (Element* element = static_cast<Element*>(frame->ownerElement())) {
146  do {
147  rect.move(element->offsetLeft(), element->offsetTop());
148  } while ((element = element->offsetParent()));
149  }
150  }
151 
152  return rect;
153 }
154 
15570static RectsAlignment alignmentForRects(FocusDirection direction, const IntRect& curRect, const IntRect& targetRect, const IntSize& viewSize)
15671{
15772 // If we found a node in full alignment, but it is too far away, ignore it.

@@static inline bool rightOf(const IntRect
328243 return a.x() > b.right();
329244}
330245
331 // * a = Current focused node's rect.
332 // * b = Focus candidate node's rect.
333 static long long spatialDistance(FocusDirection direction, const IntRect& a, const IntRect& b)
334 {
335  int x1 = 0, y1 = 0, x2 = 0, y2 = 0;
336 
337  if (direction == FocusDirectionLeft) {
338  // #1 |--|
339  //
340  // #2 |--| |--|
341  //
342  // #3 |--|
343 
344  x1 = a.x();
345  x2 = b.right();
346 
347  if (below(a, b)) {
348  // #1 The a rect is below b.
349  y1 = a.y();
350  y2 = b.bottom();
351  } else if (below(b, a)) {
352  // #3 The b rect is below a.
353  y1 = a.bottom();
354  y2 = b.y();
355  } else {
356  // #2 Both b and a share some common y's.
357  y1 = 0;
358  y2 = 0;
359  }
360  } else if (direction == FocusDirectionRight) {
361  // |--| #1
362  //
363  // |--| |--| #2
364  //
365  // |--| #3
366 
367  x1 = a.right();
368  x2 = b.x();
369 
370  if (below(a, b)) {
371  // #1 The b rect is above a.
372  y1 = a.y();
373  y2 = b.bottom();
374  } else if (below(b, a)) {
375  // #3 The b rect is below a.
376  y1 = a.bottom();
377  y2 = b.y();
378  } else {
379  // #2 Both b and a share some common y's.
380  y1 = 0;
381  y2 = 0;
382  }
383  } else if (direction == FocusDirectionUp) {
384  //
385  // #1 #2 #3
386  //
387  // |--| |--| |--|
388  //
389  // |--|
390 
391  y1 = a.y();
392  y2 = b.bottom();
393 
394  if (rightOf(a, b)) {
395  // #1 The b rect is to the left of a.
396  x1 = a.x();
397  x2 = b.right();
398  } else if (rightOf(b, a)) {
399  // #3 The b rect is to the right of a.
400  x1 = a.right();
401  x2 = b.x();
402  } else {
403  // #2 Both b and a share some common x's.
404  x1 = 0;
405  x2 = 0;
406  }
407  } else if (direction == FocusDirectionDown) {
408  // |--|
409  //
410  // |--| |--| |--|
411  //
412  // #1 #2 #3
413 
414  y1 = a.bottom();
415  y2 = b.y();
416 
417  if (rightOf(a, b)) {
418  // #1 The b rect is to the left of a.
419  x1 = a.x();
420  x2 = b.right();
421  } else if (rightOf(b, a)) {
422  // #3 The b rect is to the right of a
423  x1 = a.right();
424  x2 = b.x();
425  } else {
426  // #2 Both b and a share some common x's.
427  x1 = 0;
428  x2 = 0;
429  }
430  }
431 
432  long long dx = x1 - x2;
433  long long dy = y1 - y2;
434 
435  long long distance = (dx * dx) + (dy * dy);
436 
437  if (distance < 0)
438  distance *= -1;
439 
440  return distance;
441 }
442 
443246static bool isRectInDirection(FocusDirection direction, const IntRect& curRect, const IntRect& targetRect)
444247{
445248 switch (direction) {

@@bool scrollInDirection(Node* container,
572375 return false;
573376}
574377
575 void scrollIntoView(Element* element)
576 {
577  // NOTE: Element's scrollIntoView method could had been used here, but
578  // it is preferable to inflate |element|'s bounding rect a bit before
579  // scrolling it for accurate reason.
580  // Element's scrollIntoView method does not provide this flexibility.
581  IntRect bounds = element->getRect();
582  bounds.inflate(fudgeFactor());
583  element->renderer()->enclosingLayer()->scrollRectToVisible(bounds);
584 }
585 
586 bool isInRootDocument(Node* node)
587 {
588  if (!node)
589  return false;
590 
591  Document* rootDocument = node->document()->page()->mainFrame()->document();
592  return node->document() == rootDocument;
593 }
594 
595378static void deflateIfOverlapped(IntRect& a, IntRect& b)
596379{
597380 if (!a.intersects(b) || a.contains(b) || b.contains(a))

@@static void deflateIfOverlapped(IntRect&
607390 b.inflate(deflateFactor);
608391}
609392
610 static bool checkNegativeCoordsForNode(Node* node, const IntRect& curRect)
611 {
612  ASSERT(node || node->renderer());
613 
614  if (curRect.x() >= 0 && curRect.y() >= 0)
615  return true;
616 
617  bool canBeScrolled = false;
618 
619  RenderObject* renderer = node->renderer();
620  for (; renderer; renderer = renderer->parent()) {
621  if (renderer->isBox() && toRenderBox(renderer)->canBeScrolledAndHasScrollableArea()) {
622  canBeScrolled = true;
623  break;
624  }
625  }
626 
627  return canBeScrolled;
628 }
629 
630393bool isScrollableContainerNode(const Node* node)
631394{
632395 if (!node)

@@bool isScrollableContainerNode(const Nod
640403 return false;
641404}
642405
643 bool isNodeDeepDescendantOfDocument(Node* node, Document* baseDocument)
644 {
645  if (!node || !baseDocument)
646  return false;
647 
648  bool descendant = baseDocument == node->document();
649 
650  Element* currentElement = static_cast<Element*>(node);
651  while (!descendant) {
652  Element* documentOwner = currentElement->document()->ownerElement();
653  if (!documentOwner)
654  break;
655 
656  descendant = documentOwner->document() == baseDocument;
657  currentElement = documentOwner;
658  }
659 
660  return descendant;
661 }
662 
663406Node* scrollableEnclosingBoxOrParentFrameForNodeInDirection(FocusDirection direction, Node* node)
664407{
665408 ASSERT(node);

@@void distanceDataForNode(FocusDirection
877620 candidate.alignment = alignmentForRects(direction, currentRect, nodeRect, viewSize);
878621}
879622
880 bool canBeScrolledIntoView(FocusDirection direction, FocusCandidate& candidate)
 623bool canBeScrolledIntoView(FocusDirection direction, const FocusCandidate& candidate)
881624{
882625 ASSERT(candidate.node && hasOffscreenRect(candidate.node));
883626 IntRect candidateRect = candidate.rect;
72526

WebCore/page/SpatialNavigation.h

@@struct FocusCandidate {
122122 IntRect rect;
123123};
124124
125 void distanceDataForNode(FocusDirection direction, Node* start, FocusCandidate& candidate);
126125bool scrollInDirection(Frame*, FocusDirection);
127126bool scrollInDirection(Node* container, FocusDirection);
128 void scrollIntoView(Element*);
129127bool hasOffscreenRect(Node*, FocusDirection direction = FocusDirectionNone);
130 bool isInRootDocument(Node*);
131128bool isScrollableContainerNode(const Node*);
132129bool isNodeDeepDescendantOfDocument(Node*, Document*);
133130Node* scrollableEnclosingBoxOrParentFrameForNodeInDirection(FocusDirection, Node* node);

@@bool canScrollInDirection(FocusDirection
136133IntRect nodeRectInAbsoluteCoordinates(Node*, bool ignoreBorder = false);
137134IntRect frameRectInAbsoluteCoordinates(Frame*);
138135void distanceDataForNode(FocusDirection, FocusCandidate& current, FocusCandidate& candidate);
139 bool canBeScrolledIntoView(FocusDirection, FocusCandidate&);
 136bool canBeScrolledIntoView(FocusDirection, const FocusCandidate&);
140137IntRect virtualRectForDirection(FocusDirection, const IntRect& startingRect);
141138} // namspace WebCore
142139
72526