summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Bruhin <git@the-compiler.org>2016-04-01 06:14:39 +0200
committerFlorian Bruhin <git@the-compiler.org>2016-04-01 06:14:39 +0200
commit70fedf618a6672ad8a93cc54f624bb72d91d2995 (patch)
tree23de928517d11fc45340fdbcbd333fefe818ba0b
parent451ea05393742a14c64a827ac9d3a819af46fa46 (diff)
parentb5636a35317730129618d36a1b486750e9954baf (diff)
downloadqutebrowser-70fedf618a6672ad8a93cc54f624bb72d91d2995.tar.gz
qutebrowser-70fedf618a6672ad8a93cc54f624bb72d91d2995.zip
Merge branch 'paretje-force-current-tab'
-rw-r--r--CHANGELOG.asciidoc2
-rw-r--r--README.asciidoc2
-rw-r--r--doc/help/commands.asciidoc3
-rw-r--r--qutebrowser/browser/hints.py19
-rw-r--r--qutebrowser/browser/webelem.py13
-rw-r--r--tests/integration/data/hints/link_blank.html10
-rw-r--r--tests/integration/data/hints/link_span.html10
-rw-r--r--tests/integration/features/hints.feature26
-rw-r--r--tests/unit/browser/test_webelem.py45
9 files changed, 121 insertions, 9 deletions
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 385f42e97..4ee5b8d26 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -34,6 +34,8 @@ Added
for hints instead of single characters.
- New `--all` argument for `:download-cancel` to cancel all running downloads.
- New `password_fill` userscript to fill passwords using the `pass` executable.
+- New `current` hinting mode which forces opening hints in the current tab
+ (even with `target="_blank"`)
Changed
~~~~~~~
diff --git a/README.asciidoc b/README.asciidoc
index 6cb17e248..11c0783b9 100644
--- a/README.asciidoc
+++ b/README.asciidoc
@@ -165,6 +165,7 @@ Contributors, sorted by the number of commits in descending order:
* Thorsten Wißmann
* Philipp Hansch
* Austin Anderson
+* Kevin Velghe
* Alexey "Averrin" Nabrodov
* avk
* ZDarian
@@ -216,7 +217,6 @@ Contributors, sorted by the number of commits in descending order:
* Samuel Loury
* Matthias Lisin
* Marcel Schilling
-* Kevin Velghe
* Jean-Christophe Petkovich
* Helen Sherwood-Taylor
* HalosGhost
diff --git a/doc/help/commands.asciidoc b/doc/help/commands.asciidoc
index ae7b597d3..d3819741e 100644
--- a/doc/help/commands.asciidoc
+++ b/doc/help/commands.asciidoc
@@ -290,7 +290,8 @@ Start hinting.
* +'target'+: What to do with the selected element.
- - `normal`: Open the link in the current tab.
+ - `normal`: Open the link.
+ - `current`: Open the link in the current tab.
- `tab`: Open the link in a new tab (honoring the
background-tabs setting).
- `tab-fg`: Open the link in a new foreground tab.
diff --git a/qutebrowser/browser/hints.py b/qutebrowser/browser/hints.py
index 26fd02fbc..3b71c0266 100644
--- a/qutebrowser/browser/hints.py
+++ b/qutebrowser/browser/hints.py
@@ -42,10 +42,10 @@ from qutebrowser.misc import guiprocess
ElemTuple = collections.namedtuple('ElemTuple', ['elem', 'label'])
-Target = usertypes.enum('Target', ['normal', 'tab', 'tab_fg', 'tab_bg',
- 'window', 'yank', 'yank_primary', 'run',
- 'fill', 'hover', 'download', 'userscript',
- 'spawn'])
+Target = usertypes.enum('Target', ['normal', 'current', 'tab', 'tab_fg',
+ 'tab_bg', 'window', 'yank', 'yank_primary',
+ 'run', 'fill', 'hover', 'download',
+ 'userscript', 'spawn'])
class WordHintingError(Exception):
@@ -71,7 +71,8 @@ class HintContext:
elems: A mapping from key strings to (elem, label) namedtuples.
baseurl: The URL of the current page.
target: What to do with the opened links.
- normal/tab/tab_fg/tab_bg/window: Get passed to BrowserTab.
+ normal/current/tab/tab_fg/tab_bg/window: Get passed to
+ BrowserTab.
yank/yank_primary: Yank to clipboard/primary selection.
run: Run a command.
fill: Fill commandline with link.
@@ -128,6 +129,7 @@ class HintManager(QObject):
HINT_TEXTS = {
Target.normal: "Follow hint",
+ Target.current: "Follow hint in current tab",
Target.tab: "Follow hint in new tab",
Target.tab_fg: "Follow hint in foreground tab",
Target.tab_bg: "Follow hint in background tab",
@@ -429,6 +431,7 @@ class HintManager(QObject):
"""
target_mapping = {
Target.normal: usertypes.ClickTarget.normal,
+ Target.current: usertypes.ClickTarget.normal,
Target.tab_fg: usertypes.ClickTarget.tab,
Target.tab_bg: usertypes.ClickTarget.tab_bg,
Target.window: usertypes.ClickTarget.window,
@@ -463,6 +466,8 @@ class HintManager(QObject):
QMouseEvent(QEvent.MouseButtonRelease, pos, Qt.LeftButton,
Qt.NoButton, modifiers),
]
+ if context.target == Target.current:
+ elem.remove_blank_target()
for evt in events:
self.mouse_event.emit(evt)
if elem.is_text_input() and elem.is_editable():
@@ -741,7 +746,8 @@ class HintManager(QObject):
target: What to do with the selected element.
- - `normal`: Open the link in the current tab.
+ - `normal`: Open the link.
+ - `current`: Open the link in the current tab.
- `tab`: Open the link in a new tab (honoring the
background-tabs setting).
- `tab-fg`: Open the link in a new foreground tab.
@@ -891,6 +897,7 @@ class HintManager(QObject):
# Handlers which take a QWebElement
elem_handlers = {
Target.normal: self._click,
+ Target.current: self._click,
Target.tab: self._click,
Target.tab_fg: self._click,
Target.tab_bg: self._click,
diff --git a/qutebrowser/browser/webelem.py b/qutebrowser/browser/webelem.py
index 49f4bfc27..df481b98c 100644
--- a/qutebrowser/browser/webelem.py
+++ b/qutebrowser/browser/webelem.py
@@ -285,6 +285,19 @@ class WebElementWrapper(collections.abc.MutableMapping):
tag = self._elem.tagName().lower()
return self.get('role', None) in roles or tag in ('input', 'textarea')
+ def remove_blank_target(self):
+ """Remove target from link."""
+ elem = self._elem
+ for _ in range(5):
+ if elem is None:
+ break
+ tag = elem.tagName().lower()
+ if tag == 'a' or tag == 'area':
+ if elem.attribute('target') == '_blank':
+ elem.setAttribute('target', '_top')
+ break
+ elem = elem.parent()
+
def debug_text(self):
"""Get a text based on an element suitable for debug output."""
self._check_vanished()
diff --git a/tests/integration/data/hints/link_blank.html b/tests/integration/data/hints/link_blank.html
new file mode 100644
index 000000000..f738e61dc
--- /dev/null
+++ b/tests/integration/data/hints/link_blank.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>A link to use hints on</title>
+ </head>
+ <body>
+ <a href="/data/hello.txt" target="_blank">Follow me!</a>
+ </body>
+</html>
diff --git a/tests/integration/data/hints/link_span.html b/tests/integration/data/hints/link_span.html
new file mode 100644
index 000000000..2a5bbfdfd
--- /dev/null
+++ b/tests/integration/data/hints/link_span.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>A link to use hints on</title>
+ </head>
+ <body>
+ <a href="/data/hello.txt" target="_blank"><span style="font-size: large">Follow me!</span></a>
+ </body>
+</html>
diff --git a/tests/integration/features/hints.feature b/tests/integration/features/hints.feature
index d95cdf868..3f6fab924 100644
--- a/tests/integration/features/hints.feature
+++ b/tests/integration/features/hints.feature
@@ -18,3 +18,29 @@ Feature: Using hints
And I run :hint links normal
And I run :follow-hint xyz
Then the error "No hint xyz!" should be shown
+
+ Scenario: Following a hint and force to open in current tab.
+ When I open data/hints/link_blank.html
+ And I run :hint links current
+ And I run :follow-hint a
+ And I wait until data/hello.txt is loaded
+ Then the following tabs should be open:
+ - data/hello.txt (active)
+
+ Scenario: Following a hint and allow to open in new tab.
+ When I open data/hints/link_blank.html
+ And I run :hint links normal
+ And I run :follow-hint a
+ And I wait until data/hello.txt is loaded
+ Then the following tabs should be open:
+ - data/hints/link_blank.html
+ - data/hello.txt (active)
+
+ Scenario: Following a hint to link with sub-element and force to open in current tab.
+ When I open data/hints/link_span.html
+ And I run :tab-close
+ And I run :hint links current
+ And I run :follow-hint a
+ And I wait until data/hello.txt is loaded
+ Then the following tabs should be open:
+ - data/hello.txt (active)
diff --git a/tests/unit/browser/test_webelem.py b/tests/unit/browser/test_webelem.py
index 1457e08ef..459a8b1b1 100644
--- a/tests/unit/browser/test_webelem.py
+++ b/tests/unit/browser/test_webelem.py
@@ -36,7 +36,8 @@ from qutebrowser.browser import webelem
def get_webelem(geometry=None, frame=None, null=False, style=None,
- display='', attributes=None, tagname=None, classes=None):
+ display='', attributes=None, tagname=None, classes=None,
+ parent=None):
"""Factory for WebElementWrapper objects based on a mock.
Args:
@@ -55,6 +56,7 @@ def get_webelem(geometry=None, frame=None, null=False, style=None,
elem.tagName.return_value = tagname
elem.toOuterXml.return_value = '<fakeelem/>'
elem.toPlainText.return_value = 'text'
+ elem.parent.return_value = parent
attribute_dict = {}
if attributes is None:
@@ -326,6 +328,47 @@ class TestWebElementWrapper:
assert elem.debug_text() == expected
+class TestRemoveBlankTarget:
+
+ @pytest.mark.parametrize('tagname', ['a', 'area'])
+ @pytest.mark.parametrize('target', ['_self', '_parent', '_top', ''])
+ def test_keep_target(self, tagname, target):
+ elem = get_webelem(tagname=tagname, attributes={'target': target})
+ elem.remove_blank_target()
+ assert elem['target'] == target
+
+ @pytest.mark.parametrize('tagname', ['a', 'area'])
+ def test_no_target(self, tagname):
+ elem = get_webelem(tagname=tagname)
+ elem.remove_blank_target()
+ assert 'target' not in elem
+
+ @pytest.mark.parametrize('tagname', ['a', 'area'])
+ def test_blank_target(self, tagname):
+ elem = get_webelem(tagname=tagname, attributes={'target': '_blank'})
+ elem.remove_blank_target()
+ assert elem['target'] == '_top'
+
+ @pytest.mark.parametrize('tagname', ['a', 'area'])
+ def test_ancestor_blank_target(self, tagname):
+ elem = get_webelem(tagname=tagname, attributes={'target': '_blank'})
+ elem_child = get_webelem(tagname='img', parent=elem._elem)
+ elem_child._elem.encloseWith(elem._elem)
+ elem_child.remove_blank_target()
+ assert elem['target'] == '_top'
+
+ @pytest.mark.parametrize('depth', [1, 5, 10])
+ def test_no_link(self, depth):
+ elem = [None] * depth
+ elem[0] = get_webelem(tagname='div')
+ for i in range(1, depth):
+ elem[i] = get_webelem(tagname='div', parent=elem[i-1])
+ elem[i]._elem.encloseWith(elem[i-1]._elem)
+ elem[-1].remove_blank_target()
+ for i in range(depth):
+ assert 'target' not in elem[i]
+
+
class TestIsVisible:
@pytest.fixture