diff options
Diffstat (limited to 'chromium/chrome/browser/resources/chromeos/chromevox/common/focuser.js')
-rw-r--r-- | chromium/chrome/browser/resources/chromeos/chromevox/common/focuser.js | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/focuser.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/focuser.js new file mode 100644 index 00000000000..f0a29d87c43 --- /dev/null +++ b/chromium/chrome/browser/resources/chromeos/chromevox/common/focuser.js @@ -0,0 +1,120 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @fileoverview Implements the setFocus function. + */ + +goog.provide('cvox.Focuser'); + +goog.require('cvox.ChromeVoxEventSuspender'); +goog.require('cvox.DomUtil'); + + +/** + * Sets the browser focus to the targetNode or its closest ancestor that is + * focusable. + * + * @param {Node} targetNode The node to move the browser focus to. + * @param {boolean=} opt_focusDescendants Whether or not we check descendants + * of the target node to see if they are focusable. If true, sets focus on the + * first focusable descendant. If false, only sets focus on the targetNode or + * its closest ancestor. Default is false. + */ +cvox.Focuser.setFocus = function(targetNode, opt_focusDescendants) { + // Save the selection because Chrome will lose it if there's a focus or blur. + var sel = window.getSelection(); + var range; + if (sel.rangeCount > 0) { + range = sel.getRangeAt(0); + } + // Blur the currently-focused element if the target node is not a descendant. + if (document.activeElement && + !cvox.DomUtil.isDescendantOfNode(targetNode, document.activeElement)) { + document.activeElement.blur(); + } + + // Video elements should always be focusable. + if (targetNode && (targetNode.constructor == HTMLVideoElement)) { + if (!cvox.DomUtil.isFocusable(targetNode)) { + targetNode.setAttribute('tabIndex', 0); + } + } + + if (opt_focusDescendants && !cvox.DomUtil.isFocusable(targetNode)) { + var focusableDescendant = cvox.DomUtil.findFocusableDescendant(targetNode); + if (focusableDescendant) { + targetNode = focusableDescendant; + } + } else { + // Search up the parent chain until a focusable node is found. + while (targetNode && !cvox.DomUtil.isFocusable(targetNode)) { + targetNode = targetNode.parentNode; + } + } + + // If we found something focusable, focus it - otherwise, blur it. + if (cvox.DomUtil.isFocusable(targetNode)) { + // Don't let the instance of ChromeVox in the parent focus iframe children + // - instead, let the instance of ChromeVox in the iframe focus itself to + // avoid getting trapped in iframes that have no ChromeVox in them. + // This self focusing is performed by calling window.focus() in + // cvox.NavigationManager.prototype.addInterframeListener_ + if (targetNode.tagName != 'IFRAME') { + // setTimeout must be used because there's a bug (in Chrome, I think) + // with .focus() which causes the page to be redrawn incorrectly if + // not in setTimeout. + if (cvox.ChromeVoxEventSuspender.areEventsSuspended()) { + if (cvox.Focuser.shouldEnterSuspendEvents_(targetNode)) { + cvox.ChromeVoxEventSuspender.enterSuspendEvents(); + } + window.setTimeout(function() { + targetNode.focus(); + cvox.ChromeVoxEventSuspender.exitSuspendEvents(); + }, 0); + } + else { + window.setTimeout(function() { + targetNode.focus(); + }, 0); + } + } + } else if (document.activeElement && + document.activeElement.tagName != 'BODY') { + document.activeElement.blur(); + } + + // Restore the selection, unless the focused item is a text box. + if (cvox.DomUtil.isInputTypeText(targetNode)) { + targetNode.select(); + } else if (range) { + sel.removeAllRanges(); + sel.addRange(range); + } +}; + +/** + * Rules for whether or not enterSuspendEvents should be called. + * In general, we should not enterSuspendEvents if the targetNode will get some + * special handlers attached when a focus event is received for it; otherwise, + * the special handlers will not get attached. + * + * @param {Node} targetNode The node that is being focused. + * @return {boolean} True if enterSuspendEvents should be called. + */ +cvox.Focuser.shouldEnterSuspendEvents_ = function(targetNode){ + if (targetNode.constructor && targetNode.constructor == HTMLVideoElement) { + return false; + } + if (targetNode.hasAttribute) { + switch (targetNode.getAttribute('type')) { + case 'time': + case 'date': + case 'week': + case 'month': + return false; + } + } + return true; +}; |