UNPKG

next-csrf

Version:

CSRF mitigation library for Next.js

322 lines (256 loc) 11.7 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _visible = require('./visible'); var _visible2 = _interopRequireDefault(_visible); var _contextToElement = require('../util/context-to-element'); var _contextToElement2 = _interopRequireDefault(_contextToElement); var _elementMatches = require('../util/element-matches'); var _elementMatches2 = _interopRequireDefault(_elementMatches); var _tabindexValue = require('../util/tabindex-value'); var _tabindexValue2 = _interopRequireDefault(_tabindexValue); var _focusRelevant = require('./focus-relevant'); var _focusRelevant2 = _interopRequireDefault(_focusRelevant); var _getFrameElement = require('../util/get-frame-element'); var _getFrameElement2 = _interopRequireDefault(_getFrameElement); var _platform = require('../util/platform'); var _platform2 = _interopRequireDefault(_platform); var _imageMap = require('../util/image-map'); var _is = require('./is.util'); var _supports2 = require('../supports/supports'); var _supports3 = _interopRequireDefault(_supports2); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } // determine if an element can be focused by keyboard (i.e. is part of the document's sequential focus navigation order) var supports = void 0; // Internet Explorer 11 considers fieldset, table, td focusable, but not tabbable // Internet Explorer 11 considers body to have [tabindex=0], but does not allow tabbing to it var focusableElementsPattern = /^(fieldset|table|td|body)$/; function isTabbableRules() { var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, context = _ref.context, _ref$except = _ref.except, except = _ref$except === undefined ? { flexbox: false, scrollable: false, shadow: false, visible: false, onlyTabbable: false } : _ref$except; if (!supports) { supports = (0, _supports3.default)(); } var element = (0, _contextToElement2.default)({ label: 'is/tabbable', resolveDocument: true, context: context }); if (_platform2.default.is.BLINK && _platform2.default.is.ANDROID && _platform2.default.majorVersion > 42) { // External keyboard support worked fine in CHrome 42, but stopped working in Chrome 45. // The on-screen keyboard does not provide a way to focus the next input element (like iOS does). // That leaves us with no option to advance focus by keyboard, ergo nothing is tabbable (keyboard focusable). return false; } var frameElement = (0, _getFrameElement2.default)(element); if (frameElement) { if (_platform2.default.is.WEBKIT && _platform2.default.is.IOS) { // iOS only does not consider anything from another browsing context keyboard focusable return false; } // iframe[tabindex="-1"] and object[tabindex="-1"] inherit the // tabbable demotion onto elements of their browsing contexts if ((0, _tabindexValue2.default)(frameElement) < 0) { return false; } if (!except.visible && (_platform2.default.is.BLINK || _platform2.default.is.WEBKIT) && !(0, _visible2.default)(frameElement)) { // Blink and WebKit consider elements in hidden browsing contexts focusable, but not tabbable return false; } // Webkit and Blink don't consider anything in <object> tabbable // Blink fixed that fixed in Chrome 54, Opera 41 var frameNodeName = frameElement.nodeName.toLowerCase(); if (frameNodeName === 'object') { var isFixedBlink = _platform2.default.name === 'Chrome' && _platform2.default.majorVersion >= 54 || _platform2.default.name === 'Opera' && _platform2.default.majorVersion >= 41; if (_platform2.default.is.WEBKIT || _platform2.default.is.BLINK && !isFixedBlink) { return false; } } } var nodeName = element.nodeName.toLowerCase(); var _tabindex = (0, _tabindexValue2.default)(element); var tabindex = _tabindex === null ? null : _tabindex >= 0; if (_platform2.default.is.EDGE && _platform2.default.majorVersion >= 14 && frameElement && element.ownerSVGElement && _tabindex < 0) { // Edge 14+ considers <a xlink:href="…" tabindex="-1"> keyboard focusable // if the element is in a nested browsing context return true; } var hasTabbableTabindexOrNone = tabindex !== false; var hasTabbableTabindex = _tabindex !== null && _tabindex >= 0; // NOTE: Firefox 31 considers [contenteditable] to have [tabindex=-1], but allows tabbing to it // fixed in Firefox 40 the latest - https://bugzilla.mozilla.org/show_bug.cgi?id=1185657 if (element.hasAttribute('contenteditable')) { // tabbing can still be disabled by explicitly providing [tabindex="-1"] return hasTabbableTabindexOrNone; } if (focusableElementsPattern.test(nodeName) && tabindex !== true) { return false; } if (_platform2.default.is.WEBKIT && _platform2.default.is.IOS) { // iOS only considers a hand full of elements tabbable (keyboard focusable) // this holds true even with external keyboards var potentiallyTabbable = nodeName === 'input' && element.type === 'text' || element.type === 'password' || nodeName === 'select' || nodeName === 'textarea' || element.hasAttribute('contenteditable'); if (!potentiallyTabbable) { var style = window.getComputedStyle(element, null); potentiallyTabbable = (0, _is.isUserModifyWritable)(style); } if (!potentiallyTabbable) { return false; } } if (nodeName === 'use' && _tabindex !== null) { if (_platform2.default.is.BLINK || _platform2.default.is.WEBKIT && _platform2.default.majorVersion === 9) { // In Chrome and Safari 9 the <use> element is keyboard focusable even for tabindex="-1" return true; } } if ((0, _elementMatches2.default)(element, 'svg a') && element.hasAttribute('xlink:href')) { if (hasTabbableTabindexOrNone) { // in Trident and Gecko SVGElement does not handle the tabIndex property properly return true; } if (element.focus && !supports.focusSvgNegativeTabindexAttribute) { // Firefox 51 and 52 treat any natively tabbable SVG element with // tabindex="-1" as tabbable and everything else as inert // see https://bugzilla.mozilla.org/show_bug.cgi?id=1302340 return true; } } if (nodeName === 'svg' && supports.focusSvgInIframe && hasTabbableTabindexOrNone) { return true; } if (_platform2.default.is.TRIDENT || _platform2.default.is.EDGE) { if (nodeName === 'svg') { if (supports.focusSvg) { // older Internet Explorers consider <svg> keyboard focusable // unless they have focsable="false", but then they wouldn't // be focusable and thus not even reach this filter return true; } // elements that have [focusable] are automatically keyboard focusable regardless of the attribute's value return element.hasAttribute('focusable') || hasTabbableTabindex; } if (element.ownerSVGElement) { if (supports.focusSvgTabindexAttribute && hasTabbableTabindex) { return true; } // elements that have [focusable] are automatically keyboard focusable regardless of the attribute's value return element.hasAttribute('focusable'); } } if (element.tabIndex === undefined) { return Boolean(except.onlyTabbable); } if (nodeName === 'audio') { if (!element.hasAttribute('controls')) { // In Internet Explorer the <audio> element is focusable, but not tabbable, and tabIndex property is wrong return false; } else if (_platform2.default.is.BLINK) { // In Chrome <audio controls tabindex="-1"> remains keyboard focusable return true; } } if (nodeName === 'video') { if (!element.hasAttribute('controls')) { if (_platform2.default.is.TRIDENT || _platform2.default.is.EDGE) { // In Internet Explorer and Edge the <video> element is focusable, but not tabbable, and tabIndex property is wrong return false; } } else if (_platform2.default.is.BLINK || _platform2.default.is.GECKO) { // In Chrome and Firefox <video controls tabindex="-1"> remains keyboard focusable return true; } } if (nodeName === 'object') { if (_platform2.default.is.BLINK || _platform2.default.is.WEBKIT) { // In all Blink and WebKit based browsers <embed> and <object> are never keyboard focusable, even with tabindex="0" set return false; } } if (nodeName === 'iframe') { // In Internet Explorer all iframes are only focusable // In WebKit, Blink and Gecko iframes may be tabbable depending on content. // Since we can't reliably investigate iframe documents because of the // SameOriginPolicy, we're declaring everything only focusable. return false; } if (!except.scrollable && _platform2.default.is.GECKO) { // Firefox considers scrollable containers keyboard focusable, // even though their tabIndex property is -1 var _style = window.getComputedStyle(element, null); if ((0, _is.hasCssOverflowScroll)(_style)) { return hasTabbableTabindexOrNone; } } if (_platform2.default.is.TRIDENT || _platform2.default.is.EDGE) { // IE and Edge degrade <area> to script focusable, if the image // using the <map> has been given tabindex="-1" if (nodeName === 'area') { var img = (0, _imageMap.getImageOfArea)(element); if (img && (0, _tabindexValue2.default)(img) < 0) { return false; } } var _style2 = window.getComputedStyle(element, null); if ((0, _is.isUserModifyWritable)(_style2)) { // prevent being swallowed by the overzealous isScrollableContainer() below return element.tabIndex >= 0; } if (!except.flexbox && (0, _is.hasCssDisplayFlex)(_style2)) { if (_tabindex !== null) { return hasTabbableTabindex; } return isFocusRelevantWithoutFlexbox(element) && isTabbableWithoutFlexbox(element); } // IE considers scrollable containers script focusable only, // even though their tabIndex property is 0 if ((0, _is.isScrollableContainer)(element, nodeName)) { return false; } var parent = element.parentElement; if (parent) { var parentNodeName = parent.nodeName.toLowerCase(); var parentStyle = window.getComputedStyle(parent, null); // IE considers scrollable bodies script focusable only, if ((0, _is.isScrollableContainer)(parent, nodeName, parentNodeName, parentStyle)) { return false; } // Children of focusable elements with display:flex are focusable in IE10-11, // even though their tabIndex property suggests otherwise if ((0, _is.hasCssDisplayFlex)(parentStyle)) { // value of tabindex takes precedence return hasTabbableTabindex; } } } // https://www.w3.org/WAI/PF/aria-practices/#focus_tabindex return element.tabIndex >= 0; } // bind exceptions to an iterator callback isTabbableRules.except = function () { var except = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var isTabbable = function isTabbable(context) { return isTabbableRules({ context: context, except: except }); }; isTabbable.rules = isTabbableRules; return isTabbable; }; var isFocusRelevantWithoutFlexbox = _focusRelevant2.default.rules.except({ flexbox: true }); var isTabbableWithoutFlexbox = isTabbableRules.except({ flexbox: true }); // provide isTabbable(context) as default iterator callback var isTabbable = isTabbableRules.except({}); exports.default = isTabbable; module.exports = exports['default']; //# sourceMappingURL=tabbable.js.map