Merge 2274dd2d39 into 7e3df43463
This commit is contained in:
commit
2cc1b14602
|
|
@ -7,6 +7,7 @@
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import html
|
import html
|
||||||
|
import io as _io
|
||||||
import os.path
|
import os.path
|
||||||
import collections
|
import collections
|
||||||
import functools
|
import functools
|
||||||
|
|
@ -813,7 +814,8 @@ class AbstractDownloadItem(QObject):
|
||||||
if filename is None: # pragma: no cover
|
if filename is None: # pragma: no cover
|
||||||
log.downloads.error("No filename to open the download!")
|
log.downloads.error("No filename to open the download!")
|
||||||
return
|
return
|
||||||
self.pdfjs_requested.emit(os.path.basename(filename),
|
dirname = os.path.basename(os.path.dirname(filename))
|
||||||
|
self.pdfjs_requested.emit(os.path.join(dirname, os.path.basename(filename)),
|
||||||
self.url())
|
self.url())
|
||||||
|
|
||||||
def cancel_for_origin(self) -> bool:
|
def cancel_for_origin(self) -> bool:
|
||||||
|
|
@ -1373,8 +1375,8 @@ class TempDownloadManager:
|
||||||
# Make sure that the filename is not too long
|
# Make sure that the filename is not too long
|
||||||
suggested_name = utils.elide_filename(suggested_name, 50)
|
suggested_name = utils.elide_filename(suggested_name, 50)
|
||||||
# pylint: disable=consider-using-with
|
# pylint: disable=consider-using-with
|
||||||
fobj = tempfile.NamedTemporaryFile(dir=tmpdir.name, delete=False,
|
tmpfiledir = tempfile.mkdtemp(dir=tmpdir.name)
|
||||||
suffix='_' + suggested_name)
|
fobj = _io.open(os.path.join(tmpfiledir, suggested_name), 'w+b')
|
||||||
self.files.append(fobj)
|
self.files.append(fobj)
|
||||||
return fobj
|
return fobj
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -517,8 +517,6 @@ def qute_pdfjs(url: QUrl) -> _HandlerRet:
|
||||||
filename = QUrlQuery(url).queryItemValue('filename')
|
filename = QUrlQuery(url).queryItemValue('filename')
|
||||||
if not filename:
|
if not filename:
|
||||||
raise UrlInvalidError("Missing filename")
|
raise UrlInvalidError("Missing filename")
|
||||||
if '/' in filename or os.sep in filename:
|
|
||||||
raise RequestDeniedError("Path separator in filename.")
|
|
||||||
|
|
||||||
path = _pdf_path(filename)
|
path = _pdf_path(filename)
|
||||||
with open(path, 'rb') as f:
|
with open(path, 'rb') as f:
|
||||||
|
|
|
||||||
|
|
@ -130,6 +130,23 @@ session.lazy_restore:
|
||||||
default: false
|
default: false
|
||||||
desc: Load a restored tab as soon as it takes focus.
|
desc: Load a restored tab as soon as it takes focus.
|
||||||
|
|
||||||
|
session.save_when_close:
|
||||||
|
type: Bool
|
||||||
|
default: True
|
||||||
|
desc: >-
|
||||||
|
Whether to automatically save a session when it is closed.
|
||||||
|
|
||||||
|
session.startup_sessions:
|
||||||
|
type:
|
||||||
|
name: List
|
||||||
|
valtype: String
|
||||||
|
none_ok: true
|
||||||
|
default: null
|
||||||
|
#TODO The default value doesn't seem to actually be set to None, so this doesn't work
|
||||||
|
desc: >-
|
||||||
|
Which sessions to load on startup. None will load the last saved sessions
|
||||||
|
from the sate file, if you want to not load any sessions, pass an empty list.
|
||||||
|
|
||||||
backend:
|
backend:
|
||||||
type:
|
type:
|
||||||
name: String
|
name: String
|
||||||
|
|
@ -2665,6 +2682,7 @@ window.title_format:
|
||||||
- current_title
|
- current_title
|
||||||
- title_sep
|
- title_sep
|
||||||
- id
|
- id
|
||||||
|
- session
|
||||||
- scroll_pos
|
- scroll_pos
|
||||||
- host
|
- host
|
||||||
- backend
|
- backend
|
||||||
|
|
@ -2672,7 +2690,8 @@ window.title_format:
|
||||||
- current_url
|
- current_url
|
||||||
- protocol
|
- protocol
|
||||||
- audio
|
- audio
|
||||||
default: '{perc}{current_title}{title_sep}qutebrowser'
|
#TODO default should not display session, especially if no session is set
|
||||||
|
default: '[{session}] {perc}{current_title}{title_sep}qutebrowser'
|
||||||
desc: |
|
desc: |
|
||||||
Format to use for the window title. The same placeholders like for
|
Format to use for the window title. The same placeholders like for
|
||||||
`tabs.title.format` are defined.
|
`tabs.title.format` are defined.
|
||||||
|
|
|
||||||
|
|
@ -241,6 +241,7 @@ class TabbedBrowser(QWidget):
|
||||||
self._global_marks: MutableMapping[str, tuple[QPoint, QUrl]] = {}
|
self._global_marks: MutableMapping[str, tuple[QPoint, QUrl]] = {}
|
||||||
self.default_window_icon = self._window().windowIcon()
|
self.default_window_icon = self._window().windowIcon()
|
||||||
self.is_private = private
|
self.is_private = private
|
||||||
|
self.session = "_nosession"
|
||||||
self.tab_deque = TabDeque()
|
self.tab_deque = TabDeque()
|
||||||
config.instance.changed.connect(self._on_config_changed)
|
config.instance.changed.connect(self._on_config_changed)
|
||||||
quitter.instance.shutting_down.connect(self.shutdown)
|
quitter.instance.shutting_down.connect(self.shutdown)
|
||||||
|
|
@ -317,6 +318,9 @@ class TabbedBrowser(QWidget):
|
||||||
return
|
return
|
||||||
fields = self.widget.get_tab_fields(idx)
|
fields = self.widget.get_tab_fields(idx)
|
||||||
fields['id'] = self._win_id
|
fields['id'] = self._win_id
|
||||||
|
#TODO update on window session changed
|
||||||
|
#TODO don't leak _nosession into window title
|
||||||
|
fields['session'] = self.session
|
||||||
|
|
||||||
title = title_format.format(**fields)
|
title = title_format.format(**fields)
|
||||||
# prevent hanging WMs and similar issues with giant URLs
|
# prevent hanging WMs and similar issues with giant URLs
|
||||||
|
|
|
||||||
|
|
@ -256,9 +256,35 @@ class SessionManager(QObject):
|
||||||
data['history'].append(item_data)
|
data['history'].append(item_data)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
def _save_window(self, *, win_id=None, with_history=True):
|
||||||
|
"""Get a dict with data for single window and its tabs."""
|
||||||
|
main_window = objreg.get('main-window', scope='window', window=win_id)
|
||||||
|
|
||||||
|
# We could be in the middle of destroying a window here
|
||||||
|
if sip.isdeleted(main_window):
|
||||||
|
return None
|
||||||
|
|
||||||
|
tabbed_browser = objreg.get('tabbed-browser', scope='window', window=win_id)
|
||||||
|
|
||||||
|
win_data: _JsonType = {}
|
||||||
|
win_data['session'] = tabbed_browser.session
|
||||||
|
active_window = objects.qapp.activeWindow()
|
||||||
|
if getattr(active_window, 'win_id', None) == win_id:
|
||||||
|
win_data['active'] = True
|
||||||
|
win_data['geometry'] = bytes(main_window.saveGeometry())
|
||||||
|
win_data['tabs'] = []
|
||||||
|
if tabbed_browser.is_private:
|
||||||
|
win_data['private'] = True
|
||||||
|
for i, tab in enumerate(tabbed_browser.widgets()):
|
||||||
|
active = i == tabbed_browser.widget.currentIndex()
|
||||||
|
win_data['tabs'].append(self._save_tab(tab, active,
|
||||||
|
with_history=with_history))
|
||||||
|
|
||||||
|
return win_data
|
||||||
|
|
||||||
def _save_all(self, *, only_window=None, with_private=False, with_history=True):
|
def _save_all(self, *, only_window=None, with_private=False, with_history=True):
|
||||||
"""Get a dict with data for all windows/tabs."""
|
"""Get a dict with data for all windows/tabs."""
|
||||||
data: _JsonType = {'windows': []}
|
session_data: _JsonType = {}
|
||||||
if only_window is not None:
|
if only_window is not None:
|
||||||
winlist: Iterable[int] = [only_window]
|
winlist: Iterable[int] = [only_window]
|
||||||
else:
|
else:
|
||||||
|
|
@ -267,30 +293,18 @@ class SessionManager(QObject):
|
||||||
for win_id in sorted(winlist):
|
for win_id in sorted(winlist):
|
||||||
tabbed_browser = objreg.get('tabbed-browser', scope='window',
|
tabbed_browser = objreg.get('tabbed-browser', scope='window',
|
||||||
window=win_id)
|
window=win_id)
|
||||||
main_window = objreg.get('main-window', scope='window',
|
|
||||||
window=win_id)
|
|
||||||
|
|
||||||
# We could be in the middle of destroying a window here
|
|
||||||
if sip.isdeleted(main_window):
|
|
||||||
continue
|
|
||||||
|
|
||||||
if tabbed_browser.is_private and not with_private:
|
if tabbed_browser.is_private and not with_private:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
win_data: _JsonType = {}
|
if tabbed_browser.session not in session_data:
|
||||||
active_window = objects.qapp.activeWindow()
|
session_data[tabbed_browser.session] = {'windows': []} # type: _JsonType
|
||||||
if getattr(active_window, 'win_id', None) == win_id:
|
|
||||||
win_data['active'] = True
|
win_data = self._save_window(win_id=win_id, with_history=with_history)
|
||||||
win_data['geometry'] = bytes(main_window.saveGeometry())
|
if win_data is not None:
|
||||||
win_data['tabs'] = []
|
session_data[tabbed_browser.session]['windows'].append(win_data)
|
||||||
if tabbed_browser.is_private:
|
|
||||||
win_data['private'] = True
|
return session_data
|
||||||
for i, tab in enumerate(tabbed_browser.widgets()):
|
|
||||||
active = i == tabbed_browser.widget.currentIndex()
|
|
||||||
win_data['tabs'].append(self._save_tab(tab, active,
|
|
||||||
with_history=with_history))
|
|
||||||
data['windows'].append(win_data)
|
|
||||||
return data
|
|
||||||
|
|
||||||
def _get_session_name(self, name):
|
def _get_session_name(self, name):
|
||||||
"""Helper for save to get the name to save the session to.
|
"""Helper for save to get the name to save the session to.
|
||||||
|
|
@ -308,6 +322,13 @@ class SessionManager(QObject):
|
||||||
name = 'default'
|
name = 'default'
|
||||||
return name
|
return name
|
||||||
|
|
||||||
|
def _dump_session(self, path, data):
|
||||||
|
try:
|
||||||
|
with qtutils.savefile_open(path) as f:
|
||||||
|
utils.yaml_dump(data, f)
|
||||||
|
except (OSError, UnicodeEncodeError, yaml.YAMLError) as e:
|
||||||
|
raise SessionError(e)
|
||||||
|
|
||||||
def save(self, name, last_window=False, load_next_time=False,
|
def save(self, name, last_window=False, load_next_time=False,
|
||||||
only_window=None, with_private=False, with_history=True):
|
only_window=None, with_private=False, with_history=True):
|
||||||
"""Save a named session.
|
"""Save a named session.
|
||||||
|
|
@ -325,35 +346,54 @@ class SessionManager(QObject):
|
||||||
Return:
|
Return:
|
||||||
The name of the saved session.
|
The name of the saved session.
|
||||||
"""
|
"""
|
||||||
name = self._get_session_name(name)
|
|
||||||
path = self._get_session_path(name)
|
|
||||||
|
|
||||||
log.sessions.debug("Saving session {} to {}...".format(name, path))
|
|
||||||
if last_window:
|
if last_window:
|
||||||
data = self._last_window_session
|
session_data = self._last_window_session
|
||||||
if data is None:
|
if session_data is None:
|
||||||
log.sessions.error("last_window_session is None while saving!")
|
log.sessions.error("last_window_session is None while saving!")
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
data = self._save_all(only_window=only_window,
|
session_data = self._save_all(only_window=only_window,
|
||||||
with_private=with_private,
|
with_private=with_private,
|
||||||
with_history=with_history)
|
with_history=with_history)
|
||||||
|
|
||||||
log.sessions.vdebug( # type: ignore[attr-defined]
|
log.sessions.vdebug( # type: ignore[attr-defined]
|
||||||
"Saving data: {}".format(data))
|
"Saving data: {}".format(session_data))
|
||||||
try:
|
if name == default:
|
||||||
with qtutils.savefile_open(path) as f:
|
# save all active sessions by default
|
||||||
utils.yaml_dump(data, f)
|
names = list(session_data.keys())
|
||||||
except (OSError, UnicodeEncodeError, yaml.YAMLError) as e:
|
# don't save sessionless windows (TODO should be configurable)
|
||||||
raise SessionError(e)
|
if '_nosession' in names:
|
||||||
|
names.remove('_nosession')
|
||||||
|
elif os.path.isabs(name):
|
||||||
|
_save_all_to(name)
|
||||||
|
if load_next_time:
|
||||||
|
configfiles.state['general']['session'] = name
|
||||||
|
return [name]
|
||||||
|
else:
|
||||||
|
# could possibly support `:session_save session1 session2`?
|
||||||
|
names = [name]
|
||||||
|
|
||||||
|
# current session is not used atm (could be session of current active window)
|
||||||
|
for s in names:
|
||||||
|
path = self._get_session_path(s)
|
||||||
|
self._dump_session(path, session_data.get(s, { 'windows' : [] }))
|
||||||
|
|
||||||
if load_next_time:
|
if load_next_time:
|
||||||
configfiles.state['general']['session'] = name
|
configfiles.state['general']['session'] = ','.join(names)
|
||||||
return name
|
return names
|
||||||
|
|
||||||
|
def _save_all_to(self, name):
|
||||||
|
session_data = self._save_all()
|
||||||
|
data = {'windows': []} # type: _JsonType
|
||||||
|
for d in session_data.values():
|
||||||
|
data['windows'].extend(d['windows'])
|
||||||
|
path = self._get_session_path(name)
|
||||||
|
self._dump_session(path, data)
|
||||||
|
|
||||||
def _save_autosave(self):
|
def _save_autosave(self):
|
||||||
"""Save the autosave session."""
|
"""Save the autosave session."""
|
||||||
try:
|
try:
|
||||||
self.save('_autosave')
|
self._save_all_to('_autosave')
|
||||||
except SessionError as e:
|
except SessionError as e:
|
||||||
log.sessions.error("Failed to save autosave session: {}".format(e))
|
log.sessions.error("Failed to save autosave session: {}".format(e))
|
||||||
|
|
||||||
|
|
@ -372,6 +412,7 @@ class SessionManager(QObject):
|
||||||
|
|
||||||
def save_last_window_session(self):
|
def save_last_window_session(self):
|
||||||
"""Temporarily save the session for the last closed window."""
|
"""Temporarily save the session for the last closed window."""
|
||||||
|
#TODO needs to be saved to correct session file and not break if current is None
|
||||||
self._last_window_session = self._save_all()
|
self._last_window_session = self._save_all()
|
||||||
|
|
||||||
def _load_tab(self, new_tab, data): # noqa: C901
|
def _load_tab(self, new_tab, data): # noqa: C901
|
||||||
|
|
@ -462,6 +503,7 @@ class SessionManager(QObject):
|
||||||
private=win.get('private', None))
|
private=win.get('private', None))
|
||||||
tabbed_browser = objreg.get('tabbed-browser', scope='window',
|
tabbed_browser = objreg.get('tabbed-browser', scope='window',
|
||||||
window=window.win_id)
|
window=window.win_id)
|
||||||
|
tabbed_browser.session = win.get('session')
|
||||||
tab_to_focus = None
|
tab_to_focus = None
|
||||||
for i, tab in enumerate(win['tabs']):
|
for i, tab in enumerate(win['tabs']):
|
||||||
new_tab = tabbed_browser.tabopen(background=False)
|
new_tab = tabbed_browser.tabopen(background=False)
|
||||||
|
|
@ -566,6 +608,77 @@ def session_load(name: str, *,
|
||||||
log.sessions.debug("Loaded & deleted session {}.".format(name))
|
log.sessions.debug("Loaded & deleted session {}.".format(name))
|
||||||
|
|
||||||
|
|
||||||
|
@cmdutils.register()
|
||||||
|
@cmdutils.argument('name', completion=miscmodels.session)
|
||||||
|
@cmdutils.argument('win_id', value=cmdutils.Value.win_id)
|
||||||
|
def session_window_set(name: str, *,
|
||||||
|
win_id: int = None) -> None:
|
||||||
|
"""Set session for current window
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name: The name of the session.
|
||||||
|
"""
|
||||||
|
if not isinstance(name, Sentinel) and name.startswith('_') and not force:
|
||||||
|
raise cmdutils.CommandError("{} is an internal session, a window "
|
||||||
|
"cannot be assigned to it".format(name))
|
||||||
|
tabbed_browser = objreg.get('tabbed-browser', scope='window',
|
||||||
|
window=win_id)
|
||||||
|
tabbed_browser.session = name
|
||||||
|
#FIXME Probably not meant to call this from here
|
||||||
|
tabbed_browser._update_window_title("session")
|
||||||
|
|
||||||
|
@cmdutils.register()
|
||||||
|
@cmdutils.argument('win_id', value=cmdutils.Value.win_id)
|
||||||
|
def session_window_unset(*, win_id: int = None) -> None:
|
||||||
|
"""Unset session for current window """
|
||||||
|
tabbed_browser = objreg.get('tabbed-browser', scope='window',
|
||||||
|
window=win_id)
|
||||||
|
tabbed_browser.session = "_nosession"
|
||||||
|
#FIXME Probably not meant to call this from here
|
||||||
|
tabbed_browser._update_window_title("session")
|
||||||
|
|
||||||
|
|
||||||
|
@cmdutils.register()
|
||||||
|
@cmdutils.argument('name', completion=miscmodels.session)
|
||||||
|
@cmdutils.argument('win_id', value=cmdutils.Value.win_id)
|
||||||
|
def session_close(name: str = None, *,
|
||||||
|
save: bool = False,
|
||||||
|
nosave: bool = False,
|
||||||
|
force: bool = False,
|
||||||
|
win_id: int = None) -> None:
|
||||||
|
"""Close a session.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name: The name of the session. If not given the session of the
|
||||||
|
current window is closed.
|
||||||
|
save: Save the session before closing it
|
||||||
|
nosave: Don't save the session, overrides config.session.save_when_close
|
||||||
|
"""
|
||||||
|
if save and nosave:
|
||||||
|
raise cmdutils.CommandError("Both --save and --nosave have been given.")
|
||||||
|
|
||||||
|
if name is not None and name.startswith('_') and not force:
|
||||||
|
raise cmdutils.CommandError("{} is an internal session, use --force "
|
||||||
|
"to close anyways.".format(name))
|
||||||
|
elif name is None:
|
||||||
|
tabbed_browser = objreg.get('tabbed-browser', scope='window', window=win_id)
|
||||||
|
name = tabbed_browser.session
|
||||||
|
|
||||||
|
log.sessions.vdebug("Closing session: {}".format(name))
|
||||||
|
|
||||||
|
if not nosave and (save or config.val.session.save_when_close):
|
||||||
|
#TODO also needs to handle private windows if required
|
||||||
|
session_manager.save(name)
|
||||||
|
|
||||||
|
windows = list(objreg.window_registry.values())
|
||||||
|
|
||||||
|
for w in windows:
|
||||||
|
tabbed_browser = objreg.get('tabbed-browser', scope='window',
|
||||||
|
window=w.win_id)
|
||||||
|
if tabbed_browser.session == name:
|
||||||
|
w.close()
|
||||||
|
|
||||||
|
|
||||||
@cmdutils.register()
|
@cmdutils.register()
|
||||||
@cmdutils.argument('name', completion=miscmodels.session)
|
@cmdutils.argument('name', completion=miscmodels.session)
|
||||||
@cmdutils.argument('win_id', value=cmdutils.Value.win_id)
|
@cmdutils.argument('win_id', value=cmdutils.Value.win_id)
|
||||||
|
|
@ -601,18 +714,19 @@ def session_save(name: ArgType = default, *,
|
||||||
assert not name.startswith('_')
|
assert not name.startswith('_')
|
||||||
try:
|
try:
|
||||||
if only_active_window:
|
if only_active_window:
|
||||||
name = session_manager.save(name, only_window=win_id,
|
names = session_manager.save(name, only_window=win_id,
|
||||||
with_private=True,
|
with_private=True,
|
||||||
with_history=not no_history)
|
with_history=not no_history)
|
||||||
else:
|
else:
|
||||||
name = session_manager.save(name, with_private=with_private,
|
names = session_manager.save(name, with_private=with_private,
|
||||||
with_history=not no_history)
|
with_history=not no_history)
|
||||||
except SessionError as e:
|
except SessionError as e:
|
||||||
raise cmdutils.CommandError("Error while saving session: {}".format(e))
|
raise cmdutils.CommandError("Error while saving session: {}".format(e))
|
||||||
if quiet:
|
if quiet:
|
||||||
log.sessions.debug("Saved session {}.".format(name))
|
log.sessions.debug("Saved sessions {}.".format(','.join(names)))
|
||||||
else:
|
else:
|
||||||
message.info("Saved session {}.".format(name))
|
#TODO handle Sentinel
|
||||||
|
message.info("Saved session {}.".format(','.join(names)))
|
||||||
|
|
||||||
|
|
||||||
@cmdutils.register()
|
@cmdutils.register()
|
||||||
|
|
@ -644,26 +758,34 @@ def load_default(name):
|
||||||
Args:
|
Args:
|
||||||
name: The name of the session to load, or None to read state file.
|
name: The name of the session to load, or None to read state file.
|
||||||
"""
|
"""
|
||||||
if name is None and session_manager.exists('_autosave'):
|
if name is not None:
|
||||||
name = '_autosave'
|
names = name.split(',')
|
||||||
elif name is None:
|
elif session_manager.exists('_autosave'):
|
||||||
|
names = ['_autosave']
|
||||||
|
elif config.val.session.startup_sessions is not None:
|
||||||
|
names = config.val.session.startup_sessions
|
||||||
|
if not isinstance(names, list):
|
||||||
|
names = [names]
|
||||||
|
else:
|
||||||
try:
|
try:
|
||||||
name = configfiles.state['general']['session']
|
names = configfiles.state['general']['session'].split(',')
|
||||||
except KeyError:
|
except KeyError:
|
||||||
# No session given as argument and none in the session file ->
|
# No session given as argument and none in the session file ->
|
||||||
# start without loading a session
|
# start without loading a session
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
for name in names:
|
||||||
session_manager.load(name)
|
try:
|
||||||
except SessionNotFoundError:
|
session_manager.load(name)
|
||||||
message.error("Session {} not found!".format(name))
|
except SessionNotFoundError:
|
||||||
except SessionError as e:
|
message.error("Session {} not found!".format(name))
|
||||||
message.error("Failed to load session {}: {}".format(name, e))
|
except SessionError as e:
|
||||||
|
message.error("Failed to load session {}: {}".format(name, e))
|
||||||
try:
|
try:
|
||||||
del configfiles.state['general']['session']
|
del configfiles.state['general']['session']
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
# If this was a _restart session, delete it.
|
# If this was a _restart session, delete it.
|
||||||
|
#TODO don't forget to handle this special session too
|
||||||
if name == '_restart':
|
if name == '_restart':
|
||||||
session_manager.delete('_restart')
|
session_manager.delete('_restart')
|
||||||
|
|
|
||||||
|
|
@ -141,7 +141,7 @@ class TestSaveAll:
|
||||||
# FIXME can this ever actually happen?
|
# FIXME can this ever actually happen?
|
||||||
assert not objreg.window_registry
|
assert not objreg.window_registry
|
||||||
data = sess_man._save_all()
|
data = sess_man._save_all()
|
||||||
assert not data['windows']
|
assert not data
|
||||||
|
|
||||||
@webengine_refactoring_xfail
|
@webengine_refactoring_xfail
|
||||||
def test_no_active_window(self, sess_man, fake_window, stubs,
|
def test_no_active_window(self, sess_man, fake_window, stubs,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue