Changeset 55543 in webkit
- Timestamp:
- Mar 4, 2010 12:19:44 PM (14 years ago)
- Location:
- trunk/WebCore
- Files:
-
- 2 added
- 13 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/WebCore/Android.mk
r55289 r55543 352 352 page/SecurityOrigin.cpp \ 353 353 page/Settings.cpp \ 354 page/SpatialNavigation.cpp \ 354 355 page/UserContentURLPattern.cpp \ 355 356 page/WindowFeatures.cpp \ -
trunk/WebCore/ChangeLog
r55542 r55543 1 2010-03-02 Antonio Gomes <tonikitoo@webkit.org> 2 3 Reviewed by Simon Fraser, Eric Seidel and Darin Adler. 4 Patch by Antonio Gomes <tonikitoo@webkit.org> 5 Based on the initial work of Marco Barisione <marco.barisione@collabora.co.uk> 6 7 Extend keyboard navigation to allow directional navigation 8 https://bugs.webkit.org/show_bug.cgi?id=18662 9 10 This patch implements the core logic of the 'Spatial Navigation' feature [1]. 11 It improves the accessibility support of WebCore by extending the basic keyboard 12 navigation currently available (based on Tab forward and backward) with the 13 addition of a two-dimensional directional navigation by using Left, Right, Up and 14 Down arrow keys to move to the "nearest" element in the corresponding direction. 15 16 Highlights: 17 * Feature is turned off by default in Settings. Port specific APIs need to be added 18 for toggling it on/off. 19 * Only elements viewed in the current viewport can have focus move to it. If the 20 "nearest" is not in viewport dimensions, then a scroll-in-direction action is 21 performed. 22 23 Known issues (to be covered in follow-up bugs): 24 * Add port specific hooks to each DRT to enable/disable Spatial Navigation. 25 * Support for spatial navigation through form elements (<input>, <select>, etc) 26 is be added. 27 * Make navigation keys customizable. It currently works with arrows keys only 28 (up, down, right and left). 29 * Make it support modifiers (Alt, Ctrl and Shift). 30 * Improve support on scrollable content. 31 32 [1] http://en.wikipedia.org/wiki/Spatial_navigation 33 34 * Android.mk: 35 * GNUmakefile.am: 36 * WebCore.gypi: 37 * WebCore.pro: 38 * WebCore.vcproj/WebCore.vcproj: 39 * page/EventHandler.cpp: 40 (WebCore::EventHandler::defaultKeyboardEventHandler): 41 (WebCore::EventHandler::focusDirectionForKey): 42 (WebCore::EventHandler::defaultArrowEventHandler): 43 * page/EventHandler.h: 44 * page/FocusController.cpp: 45 (WebCore::FocusController::advanceFocus): 46 (WebCore::FocusController::advanceFocusInDocumentOrder): 47 (WebCore::FocusController::advanceFocusDirectionally): 48 (WebCore::updateFocusCandidateIfCloser): 49 (WebCore::FocusController::findFocusableNodeInDirection): 50 (WebCore::FocusController::deepFindFocusableNodeInDirection): 51 * page/FocusController.h: 52 * page/FocusDirection.h: 53 (WebCore::): 54 * page/Settings.cpp: 55 (WebCore::Settings::Settings): 56 (WebCore::Settings::setSpatialNavigationEnabled): 57 * page/Settings.h: 58 (WebCore::Settings::isSpatialNavigationEnabled): 59 * page/SpatialNavigation.cpp: Added. 60 (WebCore::distanceInDirection): 61 (WebCore::renderRectRelativeToRootDocument): 62 (WebCore::alignmentForRects): 63 (WebCore::isHorizontalMove): 64 (WebCore::areRectsFullyAligned): 65 (WebCore::areRectsPartiallyAligned): 66 (WebCore::spatialDistance): 67 (WebCore::isRectInDirection): 68 (WebCore::hasOffscreenRect): 69 (WebCore::scrollInDirection): 70 (WebCore::isInRootDocument): 71 (WebCore::deflateIfOverlapped): 72 * page/SpatialNavigation.h: Added. 73 (WebCore::): 74 (WebCore::FocusCandidate::FocusCandidate): 75 1 76 2010-03-04 Beth Dakin <bdakin@apple.com> 2 77 -
trunk/WebCore/GNUmakefile.am
r55510 r55543 1421 1421 WebCore/page/Settings.cpp \ 1422 1422 WebCore/page/Settings.h \ 1423 WebCore/page/SpatialNavigation.cpp \ 1424 WebCore/page/SpatialNavigation.h \ 1423 1425 WebCore/page/UserContentURLPattern.cpp \ 1424 1426 WebCore/page/UserContentURLPattern.h \ -
trunk/WebCore/WebCore.gypi
r55522 r55543 1884 1884 'page/Settings.cpp', 1885 1885 'page/Settings.h', 1886 'page/SpatialNavigation.h', 1887 'page/SpatialNavigation.cpp', 1886 1888 'page/UserContentURLPattern.cpp', 1887 1889 'page/UserContentURLPattern.h', -
trunk/WebCore/WebCore.pro
r55520 r55543 774 774 page/Screen.cpp \ 775 775 page/Settings.cpp \ 776 page/SpatialNavigation.cpp \ 776 777 page/UserContentURLPattern.cpp \ 777 778 page/WindowFeatures.cpp \ … … 1480 1481 page/SecurityOrigin.h \ 1481 1482 page/Settings.h \ 1483 page/SpatialNavigation.h \ 1482 1484 page/WindowFeatures.h \ 1483 1485 page/WorkerNavigator.h \ -
trunk/WebCore/WebCore.vcproj/WebCore.vcproj
r55542 r55543 20974 20974 </File> 20975 20975 <File 20976 RelativePath="..\page\SpatialNavigation.cpp" 20977 > 20978 </File> 20979 <File 20980 RelativePath="..\page\SpatialNavigation.h" 20981 > 20982 </File> 20983 <File 20976 20984 RelativePath="..\page\UserContentURLPattern.cpp" 20977 20985 > -
trunk/WebCore/page/EventHandler.cpp
r55436 r55543 2175 2175 if (event->keyIdentifier() == "U+0009") 2176 2176 defaultTabEventHandler(event); 2177 2178 // provides KB navigation and selection for enhanced accessibility users 2179 if (AXObjectCache::accessibilityEnhancedUserInterfaceEnabled()) 2180 handleKeyboardSelectionMovement(event); 2177 else { 2178 FocusDirection direction = focusDirectionForKey(event->keyIdentifier()); 2179 if (direction != FocusDirectionNone) 2180 defaultArrowEventHandler(direction, event); 2181 } 2182 2183 // provides KB navigation and selection for enhanced accessibility users 2184 if (AXObjectCache::accessibilityEnhancedUserInterfaceEnabled()) 2185 handleKeyboardSelectionMovement(event); 2181 2186 } 2182 2187 if (event->type() == eventNames().keypressEvent) { … … 2187 2192 defaultSpaceEventHandler(event); 2188 2193 } 2194 } 2195 2196 FocusDirection EventHandler::focusDirectionForKey(const AtomicString& keyIdentifier) const 2197 { 2198 DEFINE_STATIC_LOCAL(AtomicString, Down, ("Down")); 2199 DEFINE_STATIC_LOCAL(AtomicString, Up, ("Up")); 2200 DEFINE_STATIC_LOCAL(AtomicString, Left, ("Left")); 2201 DEFINE_STATIC_LOCAL(AtomicString, Right, ("Right")); 2202 2203 FocusDirection retVal = FocusDirectionNone; 2204 2205 if (keyIdentifier == Down) 2206 retVal = FocusDirectionDown; 2207 else if (keyIdentifier == Up) 2208 retVal = FocusDirectionUp; 2209 else if (keyIdentifier == Left) 2210 retVal = FocusDirectionLeft; 2211 else if (keyIdentifier == Right) 2212 retVal = FocusDirectionRight; 2213 2214 return retVal; 2189 2215 } 2190 2216 … … 2483 2509 #endif 2484 2510 2511 void EventHandler::defaultArrowEventHandler(FocusDirection focusDirection, KeyboardEvent* event) 2512 { 2513 if (event->ctrlKey() || event->metaKey() || event->altGraphKey() || event->shiftKey()) 2514 return; 2515 2516 Page* page = m_frame->page(); 2517 if (!page) 2518 return; 2519 2520 if (!page->settings() || !page->settings()->isSpatialNavigationEnabled()) 2521 return; 2522 2523 // Arrows and other possible directional navigation keys can be used in design 2524 // mode editing. 2525 if (m_frame->document()->inDesignMode()) 2526 return; 2527 2528 if (page->focusController()->advanceFocus(focusDirection, event)) 2529 event->setDefaultHandled(); 2530 } 2531 2485 2532 void EventHandler::defaultTabEventHandler(KeyboardEvent* event) 2486 2533 { -
trunk/WebCore/page/EventHandler.h
r55436 r55543 28 28 29 29 #include "DragActions.h" 30 #include "FocusDirection.h" 30 31 #include "PlatformMouseEvent.h" 31 32 #include "ScrollTypes.h" … … 309 310 void defaultSpaceEventHandler(KeyboardEvent*); 310 311 void defaultTabEventHandler(KeyboardEvent*); 312 void defaultArrowEventHandler(FocusDirection, KeyboardEvent*); 311 313 312 314 #if ENABLE(DRAG_SUPPORT) … … 330 332 331 333 void setFrameWasScrolledByUser(); 334 335 FocusDirection focusDirectionForKey(const AtomicString&) const; 332 336 333 337 bool capturesDragging() const { return m_capturesDragging; } -
trunk/WebCore/page/FocusController.cpp
r55270 r55543 50 50 #include "SelectionController.h" 51 51 #include "Settings.h" 52 #include "SpatialNavigation.h" 52 53 #include "Widget.h" 53 54 #include <wtf/Platform.h> … … 56 57 57 58 using namespace HTMLNames; 59 using namespace std; 58 60 59 61 static inline void dispatchEventsOnWindowAndFocusedNode(Document* document, bool focused) … … 160 162 161 163 bool FocusController::advanceFocus(FocusDirection direction, KeyboardEvent* event, bool initialFocus) 164 { 165 switch (direction) { 166 case FocusDirectionForward: 167 case FocusDirectionBackward: 168 return advanceFocusInDocumentOrder(direction, event, initialFocus); 169 case FocusDirectionLeft: 170 case FocusDirectionRight: 171 case FocusDirectionUp: 172 case FocusDirectionDown: 173 return advanceFocusDirectionally(direction, event); 174 default: 175 ASSERT_NOT_REACHED(); 176 } 177 178 return false; 179 } 180 181 bool FocusController::advanceFocusInDocumentOrder(FocusDirection direction, KeyboardEvent* event, bool initialFocus) 162 182 { 163 183 Frame* frame = focusedOrMainFrame(); … … 265 285 } 266 286 287 bool FocusController::advanceFocusDirectionally(FocusDirection direction, KeyboardEvent* event) 288 { 289 Frame* frame = focusedOrMainFrame(); 290 ASSERT(frame); 291 Document* focusedDocument = frame->document(); 292 if (!focusedDocument) 293 return false; 294 295 Node* focusedNode = focusedDocument->focusedNode(); 296 if (!focusedNode) { 297 // Just move to the first focusable node. 298 FocusDirection tabDirection = (direction == FocusDirectionUp || direction == FocusDirectionLeft) ? 299 FocusDirectionForward : FocusDirectionBackward; 300 // 'initialFocus' is set to true so the chrome is not focused. 301 return advanceFocusInDocumentOrder(tabDirection, event, true); 302 } 303 304 // Move up in the chain of nested frames. 305 frame = frame->tree()->top(); 306 307 FocusCandidate focusCandidate; 308 findFocusableNodeInDirection(frame->document(), focusedNode, direction, event, focusCandidate); 309 310 Node* node = focusCandidate.node; 311 if (!node || !node->isElementNode()) { 312 // FIXME: May need a way to focus a document here. 313 Frame* frame = focusedOrMainFrame(); 314 scrollInDirection(frame, direction); 315 return false; 316 } 317 318 // In order to avoid crazy jump between links that are either far away from each other, 319 // or just not currently visible, lets do a scroll in the given direction and bail out 320 // if |node| element is not in the viewport. 321 if (hasOffscreenRect(node)) { 322 Frame* frame = node->document()->view()->frame(); 323 scrollInDirection(frame, direction); 324 return true; 325 } 326 327 Document* newDocument = node->document(); 328 329 if (newDocument != focusedDocument) { 330 // Focus is going away from the originally focused document, so clear the focused node. 331 focusedDocument->setFocusedNode(0); 332 } 333 334 if (newDocument) 335 setFocusedFrame(newDocument->frame()); 336 337 static_cast<Element*>(node)->focus(false); 338 return true; 339 } 340 341 void updateFocusCandidateIfCloser(Node* focusedNode, Node* candidate, long long distance, FocusCandidate& closestFocusCandidate) 342 { 343 // Bail out if |distance| is bigger than the current closest candidate. 344 if (distance >= closestFocusCandidate.distance) 345 return; 346 347 // If |focusedNode| and |candidate| are in the same document AND 348 // current |closestFocusCandidadte| is not in an {i}frame that is 349 // preferable to get focused. 350 if (focusedNode->document() == candidate->document() 351 && distance < closestFocusCandidate.parentDistance) { 352 closestFocusCandidate.node = candidate; 353 closestFocusCandidate.distance = distance; 354 closestFocusCandidate.parentDistance = cMaxDistance; 355 } else if (focusedNode->document() != candidate->document()) { 356 // If the |focusedNode| is in an inner document and the |candidate| is 357 // in a different document, we only consider to change focus if there is 358 // not another already good focusable candidate in the same document as 359 // |focusedNode|. 360 if (!((isInRootDocument(candidate) && !isInRootDocument(focusedNode)) 361 && closestFocusCandidate.node 362 && focusedNode->document() == closestFocusCandidate.node->document())) { 363 closestFocusCandidate.node = candidate; 364 closestFocusCandidate.distance = distance; 365 } 366 } 367 } 368 369 void FocusController::findFocusableNodeInDirection(Document* document, Node* focusedNode, FocusDirection direction, KeyboardEvent* event, FocusCandidate& closestFocusCandidate) 370 { 371 ASSERT(document); 372 373 // Walk all the child nodes and update focusCandidate if we find a nearer node. 374 for (Node* candidate = document->firstChild(); candidate; candidate = candidate->traverseNextNode()) { 375 // Inner documents case. 376 if (candidate->isFrameOwnerElement()) 377 deepFindFocusableNodeInDirection(focusedNode, candidate, direction, event, closestFocusCandidate); 378 else if (candidate != focusedNode && candidate->isKeyboardFocusable(event)) { 379 long long distance = distanceInDirection(focusedNode, candidate, 380 direction, closestFocusCandidate); 381 updateFocusCandidateIfCloser(focusedNode, candidate, distance, closestFocusCandidate); 382 } 383 } 384 } 385 386 void FocusController::deepFindFocusableNodeInDirection(Node* focusedNode, Node* candidate, FocusDirection direction, KeyboardEvent* event, FocusCandidate& closestFocusCandidate) 387 { 388 HTMLFrameOwnerElement* owner = static_cast<HTMLFrameOwnerElement*>(candidate); 389 if (!owner->contentFrame()) 390 return; 391 392 Document* innerDocument = owner->contentFrame()->document(); 393 if (!innerDocument) 394 return; 395 396 if (innerDocument == focusedNode->document()) 397 findFocusableNodeInDirection(innerDocument, focusedNode, direction, event, closestFocusCandidate); 398 else { 399 // Check if the current {i}frame element itself is a good candidate 400 // to move focus to. If it is, then we traverse its inner nodes. 401 // Lets pass a copy of the best candidate, to not get fooled by a 402 // frame without focusable elements. 403 FocusCandidate focusCandidateCopy = closestFocusCandidate; 404 long long distance = distanceInDirection(focusedNode, candidate, direction, focusCandidateCopy); 405 if (distance < focusCandidateCopy.distance) { 406 focusCandidateCopy.parentAlignment = focusCandidateCopy.alignment; 407 focusCandidateCopy.parentDistance = distance; 408 409 findFocusableNodeInDirection(innerDocument, focusedNode, direction, event, focusCandidateCopy); 410 411 // If we really have an inner closer focus candidate node, take it. 412 if (closestFocusCandidate.node != focusCandidateCopy.node) 413 closestFocusCandidate = focusCandidateCopy; 414 } 415 } 416 } 417 267 418 static bool relinquishesEditingFocus(Node *node) 268 419 { -
trunk/WebCore/page/FocusController.h
r54082 r55543 34 34 namespace WebCore { 35 35 36 class Document; 36 37 class Frame; 37 38 class KeyboardEvent; 38 39 class Node; 39 40 class Page; 41 struct FocusCandidate; 40 42 41 43 class FocusController : public Noncopyable { … … 59 61 60 62 private: 63 bool advanceFocusDirectionally(FocusDirection, KeyboardEvent*); 64 bool advanceFocusInDocumentOrder(FocusDirection, KeyboardEvent*, bool initialFocus); 65 66 void findFocusableNodeInDirection(Document*, Node*, FocusDirection, KeyboardEvent*, FocusCandidate&); 67 void deepFindFocusableNodeInDirection(Node*, Node*, FocusDirection, KeyboardEvent*, FocusCandidate&); 68 61 69 Page* m_page; 62 70 RefPtr<Frame> m_focusedFrame; -
trunk/WebCore/page/FocusDirection.h
r29663 r55543 29 29 namespace WebCore { 30 30 enum FocusDirection { 31 FocusDirectionForward = 0, 32 FocusDirectionBackward 31 FocusDirectionNone = 0, 32 FocusDirectionForward, 33 FocusDirectionBackward, 34 FocusDirectionUp, 35 FocusDirectionDown, 36 FocusDirectionLeft, 37 FocusDirectionRight 33 38 }; 34 39 } -
trunk/WebCore/page/Settings.cpp
r55504 r55543 67 67 , m_pluginAllowedRunTime(numeric_limits<unsigned>::max()) 68 68 , m_zoomMode(ZoomPage) 69 , m_isSpatialNavigationEnabled(false) 69 70 , m_isJavaEnabled(false) 70 71 , m_loadsImagesAutomatically(false) … … 247 248 } 248 249 250 void Settings::setSpatialNavigationEnabled(bool isSpatialNavigationEnabled) 251 { 252 m_isSpatialNavigationEnabled = isSpatialNavigationEnabled; 253 } 254 249 255 void Settings::setJavaEnabled(bool isJavaEnabled) 250 256 { -
trunk/WebCore/page/Settings.h
r55387 r55543 120 120 bool javaScriptCanOpenWindowsAutomatically() const { return m_javaScriptCanOpenWindowsAutomatically; } 121 121 122 void setSpatialNavigationEnabled(bool); 123 bool isSpatialNavigationEnabled() const { return m_isSpatialNavigationEnabled; } 124 122 125 void setJavaEnabled(bool); 123 126 bool isJavaEnabled() const { return m_isJavaEnabled; } … … 312 315 unsigned m_pluginAllowedRunTime; 313 316 ZoomMode m_zoomMode; 317 bool m_isSpatialNavigationEnabled : 1; 314 318 bool m_isJavaEnabled : 1; 315 319 bool m_loadsImagesAutomatically : 1;
Note: See TracChangeset
for help on using the changeset viewer.