Skip to content

Commit

Permalink
Change tab.find_all_elements() to be async
Browse files Browse the repository at this point in the history
  • Loading branch information
The-Compiler committed Aug 5, 2016
1 parent 0169f3a commit d8521f4
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 91 deletions.
5 changes: 3 additions & 2 deletions qutebrowser/browser/browsertab.py
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -603,10 +603,11 @@ def icon(self):
def set_html(self, html, base_url): def set_html(self, html, base_url):
raise NotImplementedError raise NotImplementedError


def find_all_elements(self, selector, *, only_visible=False): def find_all_elements(self, selector, callback, *, only_visible=False):
"""Find all HTML elements matching a given selector. """Find all HTML elements matching a given selector async.
Args: Args:
callback: The callback to be called when the search finished.
selector: The CSS selector to search for. selector: The CSS selector to search for.
only_visible: Only show elements which are visible on screen. only_visible: Only show elements which are visible on screen.
""" """
Expand Down
174 changes: 89 additions & 85 deletions qutebrowser/browser/hints.py
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -603,26 +603,51 @@ def _resolve_url(self, elem, baseurl):
qtutils.ensure_valid(url) qtutils.ensure_valid(url)
return url return url


def _find_prevnext(self, tab, prev=False): def _check_args(self, target, *args):
"""Find a prev/next element in frame.""" """Check the arguments passed to start() and raise if they're wrong.
Args:
target: A Target enum member.
args: Arguments for userscript/download
"""
if not isinstance(target, Target):
raise TypeError("Target {} is no Target member!".format(target))
if target in [Target.userscript, Target.spawn, Target.run,
Target.fill]:
if not args:
raise cmdexc.CommandError(
"'args' is required with target userscript/spawn/run/"
"fill.")
else:
if args:
raise cmdexc.CommandError(
"'args' is only allowed with target userscript/spawn.")

def _filter_matches(self, filterstr, elemstr):
"""Return True if `filterstr` matches `elemstr`."""
# Empty string and None always match
if not filterstr:
return True
filterstr = filterstr.casefold()
elemstr = elemstr.casefold()
# Do multi-word matching
return all(word in elemstr for word in filterstr.split())

def _find_prevnext(self, prev, elems):
"""Find a prev/next element in the given list of elements."""
# First check for <link rel="prev(ious)|next"> # First check for <link rel="prev(ious)|next">
elems = tab.find_all_elements(webelem.SELECTORS[webelem.Group.links])
rel_values = ('prev', 'previous') if prev else ('next') rel_values = ('prev', 'previous') if prev else ('next')
for e in elems: for e in elems:
try: if e.tag_name() != 'link' or 'rel' not in e:
rel_attr = e['rel']
except KeyError:
continue continue
if rel_attr in rel_values: if e['rel'] in rel_values:
log.hints.debug("Found '{}' with rel={}".format( log.hints.debug("Found '{}' with rel={}".format(
e.debug_text(), rel_attr)) e.debug_text(), e['rel']))
return e return e

# Then check for regular links/buttons. # Then check for regular links/buttons.
elems = tab.find_all_elements(
webelem.SELECTORS[webelem.Group.prevnext])
filterfunc = webelem.FILTERS[webelem.Group.prevnext] filterfunc = webelem.FILTERS[webelem.Group.prevnext]
elems = [e for e in elems if filterfunc(e)] elems = [e for e in elems if e.tag_name() != 'link' and filterfunc(e)]

option = 'prev-regexes' if prev else 'next-regexes' option = 'prev-regexes' if prev else 'next-regexes'
if not elems: if not elems:
return None return None
Expand All @@ -640,31 +665,53 @@ def _find_prevnext(self, tab, prev=False):
log.hints.vdebug("No match on '{}'!".format(text)) log.hints.vdebug("No match on '{}'!".format(text))
return None return None


def _check_args(self, target, *args): def follow_prevnext(self, browsertab, baseurl, prev=False, tab=False,
"""Check the arguments passed to start() and raise if they're wrong. background=False, window=False):
"""Click a "previous"/"next" element on the page.
Args: Args:
target: A Target enum member. browsertab: The WebKitTab/WebEngineTab of the page.
args: Arguments for userscript/download baseurl: The base URL of the current tab.
prev: True to open a "previous" link, False to open a "next" link.
tab: True to open in a new tab, False for the current tab.
background: True to open in a background tab.
window: True to open in a new window, False for the current one.
""" """
if not isinstance(target, Target): def _follow_prevnext_cb(elems):
raise TypeError("Target {} is no Target member!".format(target)) elem = self._find_prevnext(prev, elems)
if target in [Target.userscript, Target.spawn, Target.run, word = 'prev' if prev else 'forward'
Target.fill]:
if not args:
raise cmdexc.CommandError(
"'args' is required with target userscript/spawn/run/"
"fill.")
else:
if args:
raise cmdexc.CommandError(
"'args' is only allowed with target userscript/spawn.")


def _init_elements(self): if elem is None:
message.error(self._win_id, "No {} links found!".format(word))
return
url = self._resolve_url(elem, baseurl)
if url is None:
message.error(self._win_id, "No {} links found!".format(word))
return
qtutils.ensure_valid(url)

if window:
from qutebrowser.mainwindow import mainwindow
new_window = mainwindow.MainWindow()
new_window.show()
tabbed_browser = objreg.get('tabbed-browser', scope='window',
window=new_window.win_id)
tabbed_browser.tabopen(url, background=False)
elif tab:
tabbed_browser = objreg.get('tabbed-browser', scope='window',
window=self._win_id)
tabbed_browser.tabopen(url, background=background)
else:
browsertab = objreg.get('tab', scope='tab',
window=self._win_id, tab=self._tab_id)
browsertab.openurl(url)

selector = ', '.join([webelem.SELECTORS[webelem.Group.links],
webelem.SELECTORS[webelem.Group.prevnext]])
browsertab.find_all_elements(selector, _follow_prevnext_cb)

def _start_cb(self, elems):
"""Initialize the elements and labels based on the context set.""" """Initialize the elements and labels based on the context set."""
selector = webelem.SELECTORS[self._context.group]
elems = self._context.tab.find_all_elements(selector,
only_visible=True)
filterfunc = webelem.FILTERS.get(self._context.group, lambda e: True) filterfunc = webelem.FILTERS.get(self._context.group, lambda e: True)
elems = [e for e in elems if filterfunc(e)] elems = [e for e in elems if filterfunc(e)]
if not elems: if not elems:
Expand All @@ -681,52 +728,13 @@ def _init_elements(self):
keyparser = keyparsers[usertypes.KeyMode.hint] keyparser = keyparsers[usertypes.KeyMode.hint]
keyparser.update_bindings(strings) keyparser.update_bindings(strings)


def _filter_matches(self, filterstr, elemstr): self._context.tab.contents_size_changed.connect(
"""Return True if `filterstr` matches `elemstr`.""" self.on_contents_size_changed)
# Empty string and None always match message_bridge = objreg.get('message-bridge', scope='window',
if not filterstr: window=self._win_id)
return True message_bridge.set_text(self._get_text())
filterstr = filterstr.casefold() modeman.enter(self._win_id, usertypes.KeyMode.hint,
elemstr = elemstr.casefold() 'HintManager.start')
# Do multi-word matching
return all(word in elemstr for word in filterstr.split())

def follow_prevnext(self, browsertab, baseurl, prev=False, tab=False,
background=False, window=False):
"""Click a "previous"/"next" element on the page.
Args:
browsertab: The WebKitTab/WebEngineTab of the page.
baseurl: The base URL of the current tab.
prev: True to open a "previous" link, False to open a "next" link.
tab: True to open in a new tab, False for the current tab.
background: True to open in a background tab.
window: True to open in a new window, False for the current one.
"""
from qutebrowser.mainwindow import mainwindow
elem = self._find_prevnext(browsertab, prev)
if elem is None:
raise cmdexc.CommandError("No {} links found!".format(
"prev" if prev else "forward"))
url = self._resolve_url(elem, baseurl)
if url is None:
raise cmdexc.CommandError("No {} links found!".format(
"prev" if prev else "forward"))
qtutils.ensure_valid(url)
if window:
new_window = mainwindow.MainWindow()
new_window.show()
tabbed_browser = objreg.get('tabbed-browser', scope='window',
window=new_window.win_id)
tabbed_browser.tabopen(url, background=False)
elif tab:
tabbed_browser = objreg.get('tabbed-browser', scope='window',
window=self._win_id)
tabbed_browser.tabopen(url, background=background)
else:
tab = objreg.get('tab', scope='tab', window=self._win_id,
tab=self._tab_id)
tab.openurl(url)


@cmdutils.register(instance='hintmanager', scope='tab', name='hint', @cmdutils.register(instance='hintmanager', scope='tab', name='hint',
star_args_optional=True, maxsplit=2, star_args_optional=True, maxsplit=2,
Expand Down Expand Up @@ -817,13 +825,9 @@ def start(self, rapid=False, group=webelem.Group.all, target=Target.normal,
self._context.tab = tab self._context.tab = tab
self._context.args = args self._context.args = args
self._context.group = group self._context.group = group
self._init_elements() selector = webelem.SELECTORS[self._context.group]
tab.contents_size_changed.connect(self.on_contents_size_changed) self._context.tab.find_all_elements(selector, self._start_cb,
message_bridge = objreg.get('message-bridge', scope='window', only_visible=True)
window=self._win_id)
message_bridge.set_text(self._get_text())
modeman.enter(self._win_id, usertypes.KeyMode.hint,
'HintManager.start')


def handle_partial_key(self, keystr): def handle_partial_key(self, keystr):
"""Handle a new partial keypress.""" """Handle a new partial keypress."""
Expand Down
4 changes: 2 additions & 2 deletions qutebrowser/browser/webengine/webenginetab.py
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -411,9 +411,9 @@ def set_html(self, html, base_url):
def clear_ssl_errors(self): def clear_ssl_errors(self):
log.stub() log.stub()


def find_all_elements(self, selector, *, only_visible=False): def find_all_elements(self, selector, callback, *, only_visible=False):
log.stub() log.stub()
return [] callback([])


def _connect_signals(self): def _connect_signals(self):
view = self._widget view = self._widget
Expand Down
4 changes: 2 additions & 2 deletions qutebrowser/browser/webkit/webkittab.py
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -558,7 +558,7 @@ def clear_ssl_errors(self):
def set_html(self, html, base_url): def set_html(self, html, base_url):
self._widget.setHtml(html, base_url) self._widget.setHtml(html, base_url)


def find_all_elements(self, selector, *, only_visible=False): def find_all_elements(self, selector, callback, *, only_visible=False):
mainframe = self._widget.page().mainFrame() mainframe = self._widget.page().mainFrame()
if mainframe is None: if mainframe is None:
raise browsertab.WebTabError("No frame focused!") raise browsertab.WebTabError("No frame focused!")
Expand All @@ -572,7 +572,7 @@ def find_all_elements(self, selector, *, only_visible=False):
if only_visible: if only_visible:
elems = [e for e in elems if e.is_visible(mainframe)] elems = [e for e in elems if e.is_visible(mainframe)]


return elems callback(elems)


@pyqtSlot() @pyqtSlot()
def _on_frame_load_finished(self): def _on_frame_load_finished(self):
Expand Down

0 comments on commit d8521f4

Please sign in to comment.