From 464d1b5fb62b96cb5cff92a494b4aa93457f6359 Mon Sep 17 00:00:00 2001 From: Simon Lydell Date: Mon, 29 Feb 2016 13:50:48 +0100 Subject: [PATCH] Harden the `f` commands MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit It sucks that you can't trust the DOM. It sucks that on rare occasions, pressing `f` shows no hints, but still enters Hints mode, leaving a “element.href.startsWith is not a function” (or similar) error in the browser console. This commit tries to harden against such cases, by never trusting DOM properties or methods to exist and be of the correct type. CoffeeScripts `?` is awesome for this. --- extension/lib/commands-frame.coffee | 62 ++++++++++++++--------------- extension/lib/commands.coffee | 2 +- extension/lib/utils.coffee | 28 ++++++------- 3 files changed, 46 insertions(+), 46 deletions(-) diff --git a/extension/lib/commands-frame.coffee b/extension/lib/commands-frame.coffee index f5b8612..2a050ea 100644 --- a/extension/lib/commands-frame.coffee +++ b/extension/lib/commands-frame.coffee @@ -133,10 +133,10 @@ helper_follow = ({id, combine = true}, matcher, {vim}) -> # links with this href actually do the same thing. On some pages, such # as startpage.com, actual proper links have the 'onclick' attribute, # so we can’t exclude such links in `utils.isProperLink`. - not element.hasAttribute('onclick') and + not element.hasAttribute?('onclick') and # GitHub’s diff expansion buttons are links with both `href` and # `data-url`. They are JavaScript-powered using the latter attribute. - not element.hasAttribute('data-url') + not element.hasAttribute?('data-url') if href of hrefs parent = hrefs[href] wrapper.parentIndex = parent.elementIndex @@ -159,10 +159,10 @@ commands.follow = helper_follow.bind(null, {id: 'normal'}, switch # Bootstrap. Match these before regular links, because especially slider # “buttons” often get the same hint otherwise. - when element.hasAttribute('data-toggle') or - element.hasAttribute('data-dismiss') or - element.hasAttribute('data-slide') or - element.hasAttribute('data-slide-to') + when element.hasAttribute?('data-toggle') or + element.hasAttribute?('data-dismiss') or + element.hasAttribute?('data-slide') or + element.hasAttribute?('data-slide-to') # Some elements may not be semantic, but _should be_ and still deserve a # good hint. type = 'clickable' @@ -170,19 +170,19 @@ commands.follow = helper_follow.bind(null, {id: 'normal'}, type = 'link' when isTypingElement(element) type = 'text' - when element.getAttribute('role') in CLICKABLE_ARIA_ROLES or + when element.getAttribute?('role') in CLICKABLE_ARIA_ROLES or # - element.hasAttribute('aria-controls') or - element.hasAttribute('aria-pressed') or - element.hasAttribute('aria-checked') or - (element.hasAttribute('aria-haspopup') and - element.getAttribute('role') != 'menu') + element.hasAttribute?('aria-controls') or + element.hasAttribute?('aria-pressed') or + element.hasAttribute?('aria-checked') or + (element.hasAttribute?('aria-haspopup') and + element.getAttribute?('role') != 'menu') type = 'clickable' when element.tabIndex > -1 and # Google Drive Documents. The hint for this element would cover the # real hint that allows you to focus the document to start typing. element.id != 'docs-editor' and - not (isXUL and element.localName.endsWith('box') and + not (isXUL and element.localName?.endsWith?('box') and element.localName != 'checkbox') type = 'clickable' unless isXUL or element.localName in ['a', 'input', 'button'] @@ -190,22 +190,22 @@ commands.follow = helper_follow.bind(null, {id: 'normal'}, when element != vim.state.scrollableElements.largest and vim.state.scrollableElements.has(element) type = 'scrollable' - when element.hasAttribute('onclick') or - element.hasAttribute('onmousedown') or - element.hasAttribute('onmouseup') or - element.hasAttribute('oncommand') or + when element.hasAttribute?('onclick') or + element.hasAttribute?('onmousedown') or + element.hasAttribute?('onmouseup') or + element.hasAttribute?('oncommand') or # Twitter. - element.classList.contains('js-new-tweets-bar') or + element.classList?.contains('js-new-tweets-bar') or # Feedly. - element.hasAttribute('data-app-action') or - element.hasAttribute('data-uri') or - element.hasAttribute('data-page-action') or + element.hasAttribute?('data-app-action') or + element.hasAttribute?('data-uri') or + element.hasAttribute?('data-page-action') or # Google Drive Document. - element.classList.contains('kix-appview-editor') + element.classList?.contains('kix-appview-editor') type = 'clickable' semantic = false # Facebook comment fields. - when element.parentElement?.classList.contains('UFIInputContainer') + when element.parentElement?.classList?.contains('UFIInputContainer') type = 'clickable-special' # Putting markers on `