Merge branch 'paretje-force-current-tab'
This commit is contained in:
commit
70fedf618a
@ -34,6 +34,8 @@ Added
|
|||||||
for hints instead of single characters.
|
for hints instead of single characters.
|
||||||
- New `--all` argument for `:download-cancel` to cancel all running downloads.
|
- New `--all` argument for `:download-cancel` to cancel all running downloads.
|
||||||
- New `password_fill` userscript to fill passwords using the `pass` executable.
|
- 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
|
Changed
|
||||||
~~~~~~~
|
~~~~~~~
|
||||||
|
@ -165,6 +165,7 @@ Contributors, sorted by the number of commits in descending order:
|
|||||||
* Thorsten Wißmann
|
* Thorsten Wißmann
|
||||||
* Philipp Hansch
|
* Philipp Hansch
|
||||||
* Austin Anderson
|
* Austin Anderson
|
||||||
|
* Kevin Velghe
|
||||||
* Alexey "Averrin" Nabrodov
|
* Alexey "Averrin" Nabrodov
|
||||||
* avk
|
* avk
|
||||||
* ZDarian
|
* ZDarian
|
||||||
@ -216,7 +217,6 @@ Contributors, sorted by the number of commits in descending order:
|
|||||||
* Samuel Loury
|
* Samuel Loury
|
||||||
* Matthias Lisin
|
* Matthias Lisin
|
||||||
* Marcel Schilling
|
* Marcel Schilling
|
||||||
* Kevin Velghe
|
|
||||||
* Jean-Christophe Petkovich
|
* Jean-Christophe Petkovich
|
||||||
* Helen Sherwood-Taylor
|
* Helen Sherwood-Taylor
|
||||||
* HalosGhost
|
* HalosGhost
|
||||||
|
@ -290,7 +290,8 @@ Start hinting.
|
|||||||
|
|
||||||
* +'target'+: What to do with the selected element.
|
* +'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
|
- `tab`: Open the link in a new tab (honoring the
|
||||||
background-tabs setting).
|
background-tabs setting).
|
||||||
- `tab-fg`: Open the link in a new foreground tab.
|
- `tab-fg`: Open the link in a new foreground tab.
|
||||||
|
@ -42,10 +42,10 @@ from qutebrowser.misc import guiprocess
|
|||||||
ElemTuple = collections.namedtuple('ElemTuple', ['elem', 'label'])
|
ElemTuple = collections.namedtuple('ElemTuple', ['elem', 'label'])
|
||||||
|
|
||||||
|
|
||||||
Target = usertypes.enum('Target', ['normal', 'tab', 'tab_fg', 'tab_bg',
|
Target = usertypes.enum('Target', ['normal', 'current', 'tab', 'tab_fg',
|
||||||
'window', 'yank', 'yank_primary', 'run',
|
'tab_bg', 'window', 'yank', 'yank_primary',
|
||||||
'fill', 'hover', 'download', 'userscript',
|
'run', 'fill', 'hover', 'download',
|
||||||
'spawn'])
|
'userscript', 'spawn'])
|
||||||
|
|
||||||
|
|
||||||
class WordHintingError(Exception):
|
class WordHintingError(Exception):
|
||||||
@ -71,7 +71,8 @@ class HintContext:
|
|||||||
elems: A mapping from key strings to (elem, label) namedtuples.
|
elems: A mapping from key strings to (elem, label) namedtuples.
|
||||||
baseurl: The URL of the current page.
|
baseurl: The URL of the current page.
|
||||||
target: What to do with the opened links.
|
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.
|
yank/yank_primary: Yank to clipboard/primary selection.
|
||||||
run: Run a command.
|
run: Run a command.
|
||||||
fill: Fill commandline with link.
|
fill: Fill commandline with link.
|
||||||
@ -128,6 +129,7 @@ class HintManager(QObject):
|
|||||||
|
|
||||||
HINT_TEXTS = {
|
HINT_TEXTS = {
|
||||||
Target.normal: "Follow hint",
|
Target.normal: "Follow hint",
|
||||||
|
Target.current: "Follow hint in current tab",
|
||||||
Target.tab: "Follow hint in new tab",
|
Target.tab: "Follow hint in new tab",
|
||||||
Target.tab_fg: "Follow hint in foreground tab",
|
Target.tab_fg: "Follow hint in foreground tab",
|
||||||
Target.tab_bg: "Follow hint in background tab",
|
Target.tab_bg: "Follow hint in background tab",
|
||||||
@ -429,6 +431,7 @@ class HintManager(QObject):
|
|||||||
"""
|
"""
|
||||||
target_mapping = {
|
target_mapping = {
|
||||||
Target.normal: usertypes.ClickTarget.normal,
|
Target.normal: usertypes.ClickTarget.normal,
|
||||||
|
Target.current: usertypes.ClickTarget.normal,
|
||||||
Target.tab_fg: usertypes.ClickTarget.tab,
|
Target.tab_fg: usertypes.ClickTarget.tab,
|
||||||
Target.tab_bg: usertypes.ClickTarget.tab_bg,
|
Target.tab_bg: usertypes.ClickTarget.tab_bg,
|
||||||
Target.window: usertypes.ClickTarget.window,
|
Target.window: usertypes.ClickTarget.window,
|
||||||
@ -463,6 +466,8 @@ class HintManager(QObject):
|
|||||||
QMouseEvent(QEvent.MouseButtonRelease, pos, Qt.LeftButton,
|
QMouseEvent(QEvent.MouseButtonRelease, pos, Qt.LeftButton,
|
||||||
Qt.NoButton, modifiers),
|
Qt.NoButton, modifiers),
|
||||||
]
|
]
|
||||||
|
if context.target == Target.current:
|
||||||
|
elem.remove_blank_target()
|
||||||
for evt in events:
|
for evt in events:
|
||||||
self.mouse_event.emit(evt)
|
self.mouse_event.emit(evt)
|
||||||
if elem.is_text_input() and elem.is_editable():
|
if elem.is_text_input() and elem.is_editable():
|
||||||
@ -741,7 +746,8 @@ class HintManager(QObject):
|
|||||||
|
|
||||||
target: What to do with the selected element.
|
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
|
- `tab`: Open the link in a new tab (honoring the
|
||||||
background-tabs setting).
|
background-tabs setting).
|
||||||
- `tab-fg`: Open the link in a new foreground tab.
|
- `tab-fg`: Open the link in a new foreground tab.
|
||||||
@ -891,6 +897,7 @@ class HintManager(QObject):
|
|||||||
# Handlers which take a QWebElement
|
# Handlers which take a QWebElement
|
||||||
elem_handlers = {
|
elem_handlers = {
|
||||||
Target.normal: self._click,
|
Target.normal: self._click,
|
||||||
|
Target.current: self._click,
|
||||||
Target.tab: self._click,
|
Target.tab: self._click,
|
||||||
Target.tab_fg: self._click,
|
Target.tab_fg: self._click,
|
||||||
Target.tab_bg: self._click,
|
Target.tab_bg: self._click,
|
||||||
|
@ -285,6 +285,19 @@ class WebElementWrapper(collections.abc.MutableMapping):
|
|||||||
tag = self._elem.tagName().lower()
|
tag = self._elem.tagName().lower()
|
||||||
return self.get('role', None) in roles or tag in ('input', 'textarea')
|
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):
|
def debug_text(self):
|
||||||
"""Get a text based on an element suitable for debug output."""
|
"""Get a text based on an element suitable for debug output."""
|
||||||
self._check_vanished()
|
self._check_vanished()
|
||||||
|
10
tests/integration/data/hints/link_blank.html
Normal file
10
tests/integration/data/hints/link_blank.html
Normal file
@ -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>
|
10
tests/integration/data/hints/link_span.html
Normal file
10
tests/integration/data/hints/link_span.html
Normal file
@ -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>
|
@ -18,3 +18,29 @@ Feature: Using hints
|
|||||||
And I run :hint links normal
|
And I run :hint links normal
|
||||||
And I run :follow-hint xyz
|
And I run :follow-hint xyz
|
||||||
Then the error "No hint xyz!" should be shown
|
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)
|
||||||
|
@ -36,7 +36,8 @@ from qutebrowser.browser import webelem
|
|||||||
|
|
||||||
|
|
||||||
def get_webelem(geometry=None, frame=None, null=False, style=None,
|
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.
|
"""Factory for WebElementWrapper objects based on a mock.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -55,6 +56,7 @@ def get_webelem(geometry=None, frame=None, null=False, style=None,
|
|||||||
elem.tagName.return_value = tagname
|
elem.tagName.return_value = tagname
|
||||||
elem.toOuterXml.return_value = '<fakeelem/>'
|
elem.toOuterXml.return_value = '<fakeelem/>'
|
||||||
elem.toPlainText.return_value = 'text'
|
elem.toPlainText.return_value = 'text'
|
||||||
|
elem.parent.return_value = parent
|
||||||
|
|
||||||
attribute_dict = {}
|
attribute_dict = {}
|
||||||
if attributes is None:
|
if attributes is None:
|
||||||
@ -326,6 +328,47 @@ class TestWebElementWrapper:
|
|||||||
assert elem.debug_text() == expected
|
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:
|
class TestIsVisible:
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
Loading…
Reference in New Issue
Block a user