From cf17af147e0b25680a4463714258baaaa0ef5821 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 23 Jun 2014 14:19:56 +0200 Subject: [PATCH] Fix and clean up insertmode checks --- qutebrowser/utils/webelem.py | 67 +++++++++++++++++++++++++++++++--- qutebrowser/widgets/webview.py | 54 ++++----------------------- 2 files changed, 69 insertions(+), 52 deletions(-) diff --git a/qutebrowser/utils/webelem.py b/qutebrowser/utils/webelem.py index 4173d0404..04e3d328a 100644 --- a/qutebrowser/utils/webelem.py +++ b/qutebrowser/utils/webelem.py @@ -30,6 +30,7 @@ Module attributes: from PyQt5.QtCore import QRect, QUrl from PyQt5.QtWebKit import QWebElement +import qutebrowser.utils.log as log from qutebrowser.utils.usertypes import enum @@ -43,17 +44,21 @@ SELECTORS = { '[role=option], [role=button], img'), Group.links: 'a', Group.images: 'img', - Group.editable: ('input[type=text], input[type=email], input[type=url], ' - 'input[type=tel], input[type=number], ' - 'input[type=password], input[type=search], textarea'), + # This doesn't contain all the groups where we should switch to insert mode + # - it is just used when opening the external editor. + Group.editable_focused: ('input[type=text]:focus, ' + 'input[type=email]:focus, ' + 'input[type=url]:focus, ' + 'input[type=tel]:focus, ' + 'input[type=number]:focus, ' + 'input[type=password]:focus, ' + 'input[type=search]:focus, ' + 'textarea:focus'), Group.url: '[src], [href]', Group.prevnext_rel: 'link, [role=link]', Group.prevnext: 'a, button, [role=button]', } -SELECTORS[Group.editable_focused] = ', '.join( - [sel.strip() + ':focus' for sel in SELECTORS[Group.editable].split(',')]) - FILTERS = { Group.links: (lambda e: e.hasAttribute('href') and QUrl(e.attribute('href')).scheme() != 'javascript'), @@ -171,3 +176,53 @@ def get_child_frames(startframe): new_frames += frame.childFrames() frames = new_frames return results + + +def is_editable(elem): + """Check whether we should switch to insert mode for this element. + + FIXME: add tests + + Args: + elem: The QWebElement to check. + + Return: + True if we should switch to insert mode, False otherwise. + """ + # Beginnings of div-classes which are actually some kind of editor. + div_classes = ['CodeMirror', # Javascript editor over a textarea + 'kix-'] # Google Docs editor + tag = elem.tagName().lower() + if tag == 'input': + objtype = elem.attribute('type') + if objtype in ['text', 'email', 'url', 'tel', 'number', 'password', + 'search', '']: + return is_writable(elem) + elif tag == 'textarea': + return is_writable(elem) + elif tag in ('embed', 'applet', 'select'): + # Flash/Java/... + return config.get('input', 'insert-mode-on-plugins') + elif tag == 'object': + # Could be Flash/Java/..., could be image/audio/... + if not elem.hasAttribute('type'): + log.webview.debug(" without type clicked...") + return False + objtype = elem.attribute('type') + if (objtype.startswith('application/') or + elem.hasAttribute('classid')): + # Let's hope flash/java stuff has an application/* mimetype OR + # at least a classid attribute. Oh, and let's hope images/... + # DON'T have a classid attribute. HTML sucks. + log.webview.debug(" clicked.".format(objtype)) + return config.get('input', 'insert-mode-on-plugins') + elif tag == 'div': + log.webview.debug("div with classes {} clicked!".format( + elem.classes())) + for klass in elem.classes(): + if any([klass.startswith(e) for e in div_classes]): + return True + elif tag == 'span': + log.webview.debug("span with classes {} clicked!".format( + elem.classes())) + return False diff --git a/qutebrowser/widgets/webview.py b/qutebrowser/widgets/webview.py index f1385fa51..fa561182c 100644 --- a/qutebrowser/widgets/webview.py +++ b/qutebrowser/widgets/webview.py @@ -179,47 +179,6 @@ class WebView(QWebView): log.destroy.debug("Everything destroyed, calling callback") self._shutdown_callback() - def _is_editable(self, hitresult): - """Check if a hit result needs keyboard focus. - - Args: - hitresult: A QWebHitTestResult - """ - # Beginnings of div-classes which are actually some kind of editor. - div_classes = ['CodeMirror', # Javascript editor over a textarea - 'kix-'] # Google Docs editor - elem = hitresult.element() - tag = elem.tagName().lower() - if hitresult.isContentEditable() and webelem.is_writable(elem): - # text fields and the like - return True - elif tag in ('embed', 'applet', 'select'): - # Flash/Java/... - return config.get('input', 'insert-mode-on-plugins') - elif tag == 'object': - # Could be Flash/Java/..., could be image/audio/... - if not elem.hasAttribute('type'): - log.mouse.debug(" without type clicked...") - return False - objtype = elem.attribute('type') - if (objtype.startswith('application/') or - elem.hasAttribute('classid')): - # Let's hope flash/java stuff has an application/* mimetype OR - # at least a classid attribute. Oh, and let's hope images/... - # DON'T have a classid attribute. HTML sucks. - log.mouse.debug(" clicked.".format(objtype)) - return config.get('input', 'insert-mode-on-plugins') - elif tag == 'div': - log.webview.debug("div with classes {} clicked!".format( - elem.classes())) - for klass in elem.classes(): - if any([klass.startswith(e) for e in div_classes]): - return True - elif tag == 'span': - log.webview.debug("span with classes {} clicked!".format( - elem.classes())) - return False - def _mousepress_backforward(self, e): """Handle back/forward mouse button presses. @@ -258,9 +217,11 @@ class WebView(QWebView): # relative to the QWebView, not to the frame. This makes no sense to # me, but it works this way. hitresult = frame.hitTestContent(pos) + elem = hitresult.element() if hitresult.isNull(): log.mouse.debug("Hitresult is null!") - elif self._is_editable(hitresult): + elif ((hitresult.isContentEditable() and webelem.is_writable(elem)) or + webelem.is_editable(elem)): log.mouse.debug("Clicked editable element!") modeman.enter('insert', 'click') else: @@ -453,10 +414,11 @@ class WebView(QWebView): if modeman.instance().mode == 'insert' or not ok: return frame = self.page().currentFrame() - elem = frame.findFirstElement( - webelem.SELECTORS[webelem.Group.editable_focused]) - log.modes.debug("focus element: {}".format(not elem.isNull())) - if not elem.isNull(): + elem = frame.findFirstElement(':focus') + log.modes.debug("focus element: {}".format(elem.toOuterXml())) + if elem.isNull(): + log.webview.debug("Focused element is null!") + elif webelem.is_editable(elem): modeman.enter('insert', 'load finished') @pyqtSlot(str)