Merge branch 'new-private-browsing'

This commit is contained in:
Florian Bruhin 2017-05-16 06:32:15 +02:00
commit 086139110d
48 changed files with 593 additions and 680 deletions

View File

@ -544,7 +544,8 @@ For `increment` and `decrement`, the number to change the URL by. For `up`, the
[[open]] [[open]]
=== open === open
Syntax: +:open [*--implicit*] [*--bg*] [*--tab*] [*--window*] [*--secure*] ['url']+ Syntax: +:open [*--implicit*] [*--bg*] [*--tab*] [*--window*] [*--secure*] [*--private*]
['url']+
Open a URL in the current/[count]th tab. Open a URL in the current/[count]th tab.
@ -560,6 +561,7 @@ If the URL contains newlines, each line gets opened in its own tab.
* +*-t*+, +*--tab*+: Open in a new tab. * +*-t*+, +*--tab*+: Open in a new tab.
* +*-w*+, +*--window*+: Open in a new window. * +*-w*+, +*--window*+: Open in a new window.
* +*-s*+, +*--secure*+: Force HTTPS. * +*-s*+, +*--secure*+: Force HTTPS.
* +*-p*+, +*--private*+: Open a new window in private browsing mode.
==== count ==== count
The tab index to open the URL in. The tab index to open the URL in.
@ -743,6 +745,7 @@ Load a session.
[[session-save]] [[session-save]]
=== session-save === session-save
Syntax: +:session-save [*--current*] [*--quiet*] [*--force*] [*--only-active-window*] Syntax: +:session-save [*--current*] [*--quiet*] [*--force*] [*--only-active-window*]
[*--with-private*]
['name']+ ['name']+
Save a session. Save a session.
@ -756,6 +759,7 @@ Save a session.
* +*-q*+, +*--quiet*+: Don't show confirmation message. * +*-q*+, +*--quiet*+: Don't show confirmation message.
* +*-f*+, +*--force*+: Force saving internal sessions (starting with an underline). * +*-f*+, +*--force*+: Force saving internal sessions (starting with an underline).
* +*-o*+, +*--only-active-window*+: Saves only tabs of the currently active window. * +*-o*+, +*--only-active-window*+: Saves only tabs of the currently active window.
* +*-p*+, +*--with-private*+: Include private windows.
[[set]] [[set]]
=== set === set

View File

@ -18,7 +18,7 @@
|<<general-auto-save-interval,auto-save-interval>>|How often (in milliseconds) to auto-save config/cookies/etc. |<<general-auto-save-interval,auto-save-interval>>|How often (in milliseconds) to auto-save config/cookies/etc.
|<<general-editor,editor>>|The editor (and arguments) to use for the `open-editor` command. |<<general-editor,editor>>|The editor (and arguments) to use for the `open-editor` command.
|<<general-editor-encoding,editor-encoding>>|Encoding to use for editor. |<<general-editor-encoding,editor-encoding>>|Encoding to use for editor.
|<<general-private-browsing,private-browsing>>|Do not record visited pages in the history or store web page icons. |<<general-private-browsing,private-browsing>>|Open new windows in private browsing mode which does not record visited pages.
|<<general-developer-extras,developer-extras>>|Enable extra tools for Web developers. |<<general-developer-extras,developer-extras>>|Enable extra tools for Web developers.
|<<general-print-element-backgrounds,print-element-backgrounds>>|Whether the background color and images are also drawn when the page is printed. |<<general-print-element-backgrounds,print-element-backgrounds>>|Whether the background color and images are also drawn when the page is printed.
|<<general-xss-auditing,xss-auditing>>|Whether load requests should be monitored for cross-site scripting attempts. |<<general-xss-auditing,xss-auditing>>|Whether load requests should be monitored for cross-site scripting attempts.
@ -221,10 +221,14 @@
|<<colors-completion.scrollbar.bg,completion.scrollbar.bg>>|Color of the scrollbar in completion view |<<colors-completion.scrollbar.bg,completion.scrollbar.bg>>|Color of the scrollbar in completion view
|<<colors-statusbar.fg,statusbar.fg>>|Foreground color of the statusbar. |<<colors-statusbar.fg,statusbar.fg>>|Foreground color of the statusbar.
|<<colors-statusbar.bg,statusbar.bg>>|Background color of the statusbar. |<<colors-statusbar.bg,statusbar.bg>>|Background color of the statusbar.
|<<colors-statusbar.fg.private,statusbar.fg.private>>|Foreground color of the statusbar in private browsing mode.
|<<colors-statusbar.bg.private,statusbar.bg.private>>|Background color of the statusbar in private browsing mode.
|<<colors-statusbar.fg.insert,statusbar.fg.insert>>|Foreground color of the statusbar in insert mode. |<<colors-statusbar.fg.insert,statusbar.fg.insert>>|Foreground color of the statusbar in insert mode.
|<<colors-statusbar.bg.insert,statusbar.bg.insert>>|Background color of the statusbar in insert mode. |<<colors-statusbar.bg.insert,statusbar.bg.insert>>|Background color of the statusbar in insert mode.
|<<colors-statusbar.fg.command,statusbar.fg.command>>|Foreground color of the statusbar in command mode. |<<colors-statusbar.fg.command,statusbar.fg.command>>|Foreground color of the statusbar in command mode.
|<<colors-statusbar.bg.command,statusbar.bg.command>>|Background color of the statusbar in command mode. |<<colors-statusbar.bg.command,statusbar.bg.command>>|Background color of the statusbar in command mode.
|<<colors-statusbar.fg.command.private,statusbar.fg.command.private>>|Foreground color of the statusbar in private browsing + command mode.
|<<colors-statusbar.bg.command.private,statusbar.bg.command.private>>|Background color of the statusbar in private browsing + command mode.
|<<colors-statusbar.fg.caret,statusbar.fg.caret>>|Foreground color of the statusbar in caret mode. |<<colors-statusbar.fg.caret,statusbar.fg.caret>>|Foreground color of the statusbar in caret mode.
|<<colors-statusbar.bg.caret,statusbar.bg.caret>>|Background color of the statusbar in caret mode. |<<colors-statusbar.bg.caret,statusbar.bg.caret>>|Background color of the statusbar in caret mode.
|<<colors-statusbar.fg.caret-selection,statusbar.fg.caret-selection>>|Foreground color of the statusbar in caret mode with a selection |<<colors-statusbar.fg.caret-selection,statusbar.fg.caret-selection>>|Foreground color of the statusbar in caret mode with a selection
@ -395,7 +399,7 @@ Default: +pass:[utf-8]+
[[general-private-browsing]] [[general-private-browsing]]
=== private-browsing === private-browsing
Do not record visited pages in the history or store web page icons. Open new windows in private browsing mode which does not record visited pages.
Valid values: Valid values:
@ -404,8 +408,6 @@ Valid values:
Default: +pass:[false]+ Default: +pass:[false]+
This setting is only available with the QtWebKit backend.
[[general-developer-extras]] [[general-developer-extras]]
=== developer-extras === developer-extras
Enable extra tools for Web developers. Enable extra tools for Web developers.
@ -1906,6 +1908,18 @@ Background color of the statusbar.
Default: +pass:[black]+ Default: +pass:[black]+
[[colors-statusbar.fg.private]]
=== statusbar.fg.private
Foreground color of the statusbar in private browsing mode.
Default: +pass:[${statusbar.fg}]+
[[colors-statusbar.bg.private]]
=== statusbar.bg.private
Background color of the statusbar in private browsing mode.
Default: +pass:[#666666]+
[[colors-statusbar.fg.insert]] [[colors-statusbar.fg.insert]]
=== statusbar.fg.insert === statusbar.fg.insert
Foreground color of the statusbar in insert mode. Foreground color of the statusbar in insert mode.
@ -1930,6 +1944,18 @@ Background color of the statusbar in command mode.
Default: +pass:[${statusbar.bg}]+ Default: +pass:[${statusbar.bg}]+
[[colors-statusbar.fg.command.private]]
=== statusbar.fg.command.private
Foreground color of the statusbar in private browsing + command mode.
Default: +pass:[${statusbar.fg.private}]+
[[colors-statusbar.bg.command.private]]
=== statusbar.bg.command.private
Background color of the statusbar in private browsing + command mode.
Default: +pass:[${statusbar.bg.private}]+
[[colors-statusbar.fg.caret]] [[colors-statusbar.fg.caret]]
=== statusbar.fg.caret === statusbar.fg.caret
Foreground color of the statusbar in caret mode. Foreground color of the statusbar in caret mode.

View File

@ -195,7 +195,7 @@ def _process_args(args):
session_manager = objreg.get('session-manager') session_manager = objreg.get('session-manager')
if not session_manager.did_load: if not session_manager.did_load:
log.init.debug("Initializing main window...") log.init.debug("Initializing main window...")
window = mainwindow.MainWindow() window = mainwindow.MainWindow(private=None)
if not args.nowindow: if not args.nowindow:
window.show() window.show()
qApp.setActiveWindow(window) qApp.setActiveWindow(window)

View File

@ -35,11 +35,12 @@ from qutebrowser.browser import mouse, hints
tab_id_gen = itertools.count(0) tab_id_gen = itertools.count(0)
def create(win_id, parent=None): def create(win_id, private, parent=None):
"""Get a QtWebKit/QtWebEngine tab object. """Get a QtWebKit/QtWebEngine tab object.
Args: Args:
win_id: The window ID where the tab will be shown. win_id: The window ID where the tab will be shown.
private: Whether the tab is a private/off the record tab.
parent: The Qt parent to set. parent: The Qt parent to set.
""" """
# Importing modules here so we don't depend on QtWebEngine without the # Importing modules here so we don't depend on QtWebEngine without the
@ -51,7 +52,8 @@ def create(win_id, parent=None):
else: else:
from qutebrowser.browser.webkit import webkittab from qutebrowser.browser.webkit import webkittab
tab_class = webkittab.WebKitTab tab_class = webkittab.WebKitTab
return tab_class(win_id=win_id, mode_manager=mode_manager, parent=parent) return tab_class(win_id=win_id, mode_manager=mode_manager, private=private,
parent=parent)
def init(): def init():
@ -542,6 +544,7 @@ class AbstractTab(QWidget):
Attributes: Attributes:
history: The AbstractHistory for the current tab. history: The AbstractHistory for the current tab.
registry: The ObjectRegistry associated with this tab. registry: The ObjectRegistry associated with this tab.
private: Whether private browsing is turned on for this tab.
_load_status: loading status of this page _load_status: loading status of this page
Accessible via load_status() method. Accessible via load_status() method.
@ -581,7 +584,8 @@ class AbstractTab(QWidget):
fullscreen_requested = pyqtSignal(bool) fullscreen_requested = pyqtSignal(bool)
renderer_process_terminated = pyqtSignal(TerminationStatus, int) renderer_process_terminated = pyqtSignal(TerminationStatus, int)
def __init__(self, win_id, mode_manager, parent=None): def __init__(self, *, win_id, mode_manager, private, parent=None):
self.private = private
self.win_id = win_id self.win_id = win_id
self.tab_id = next(tab_id_gen) self.tab_id = next(tab_id_gen)
super().__init__(parent) super().__init__(parent)

View File

@ -67,10 +67,10 @@ class CommandDispatcher:
def __repr__(self): def __repr__(self):
return utils.get_repr(self) return utils.get_repr(self)
def _new_tabbed_browser(self): def _new_tabbed_browser(self, private):
"""Get a tabbed-browser from a new window.""" """Get a tabbed-browser from a new window."""
from qutebrowser.mainwindow import mainwindow from qutebrowser.mainwindow import mainwindow
new_window = mainwindow.MainWindow() new_window = mainwindow.MainWindow(private=private)
new_window.show() new_window.show()
return new_window.tabbed_browser return new_window.tabbed_browser
@ -110,7 +110,7 @@ class CommandDispatcher:
return widget return widget
def _open(self, url, tab=False, background=False, window=False, def _open(self, url, tab=False, background=False, window=False,
explicit=True): explicit=True, private=None):
"""Helper function to open a page. """Helper function to open a page.
Args: Args:
@ -118,12 +118,17 @@ class CommandDispatcher:
tab: Whether to open in a new tab. tab: Whether to open in a new tab.
background: Whether to open in the background. background: Whether to open in the background.
window: Whether to open in a new window window: Whether to open in a new window
private: If opening a new window, open it in private browsing mode.
If not given, inherit the current window's mode.
""" """
urlutils.raise_cmdexc_if_invalid(url) urlutils.raise_cmdexc_if_invalid(url)
tabbed_browser = self._tabbed_browser tabbed_browser = self._tabbed_browser
cmdutils.check_exclusive((tab, background, window), 'tbw') cmdutils.check_exclusive((tab, background, window, private), 'tbwp')
if window: if private is None:
tabbed_browser = self._new_tabbed_browser() private = self._tabbed_browser.private
if window or private:
tabbed_browser = self._new_tabbed_browser(private)
tabbed_browser.tabopen(url) tabbed_browser.tabopen(url)
elif tab: elif tab:
tabbed_browser.tabopen(url, background=False, explicit=explicit) tabbed_browser.tabopen(url, background=False, explicit=explicit)
@ -228,7 +233,8 @@ class CommandDispatcher:
@cmdutils.argument('url', completion=usertypes.Completion.url) @cmdutils.argument('url', completion=usertypes.Completion.url)
@cmdutils.argument('count', count=True) @cmdutils.argument('count', count=True)
def openurl(self, url=None, implicit=False, def openurl(self, url=None, implicit=False,
bg=False, tab=False, window=False, count=None, secure=False): bg=False, tab=False, window=False, count=None, secure=False,
private=False):
"""Open a URL in the current/[count]th tab. """Open a URL in the current/[count]th tab.
If the URL contains newlines, each line gets opened in its own tab. If the URL contains newlines, each line gets opened in its own tab.
@ -242,6 +248,7 @@ class CommandDispatcher:
clicking on a link). clicking on a link).
count: The tab index to open the URL in, or None. count: The tab index to open the URL in, or None.
secure: Force HTTPS. secure: Force HTTPS.
private: Open a new window in private browsing mode.
""" """
if url is None: if url is None:
urls = [config.get('general', 'default-page')] urls = [config.get('general', 'default-page')]
@ -254,8 +261,10 @@ class CommandDispatcher:
if not window and i > 0: if not window and i > 0:
tab = False tab = False
bg = True bg = True
if tab or bg or window:
self._open(cur_url, tab, bg, window, not implicit) if tab or bg or window or private:
self._open(cur_url, tab, bg, window, explicit=not implicit,
private=private)
else: else:
curtab = self._cntwidget(count) curtab = self._cntwidget(count)
if curtab is None: if curtab is None:
@ -430,7 +439,8 @@ class CommandDispatcher:
# The new tab could be in a new tabbed_browser (e.g. because of # The new tab could be in a new tabbed_browser (e.g. because of
# tabs-are-windows being set) # tabs-are-windows being set)
if window: if window:
new_tabbed_browser = self._new_tabbed_browser() new_tabbed_browser = self._new_tabbed_browser(
private=self._tabbed_browser.private)
else: else:
new_tabbed_browser = self._tabbed_browser new_tabbed_browser = self._tabbed_browser
newtab = new_tabbed_browser.tabopen(background=bg) newtab = new_tabbed_browser.tabopen(background=bg)

View File

@ -27,7 +27,6 @@ from PyQt5.QtCore import pyqtSignal, pyqtSlot, QUrl, QObject
from qutebrowser.commands import cmdutils from qutebrowser.commands import cmdutils
from qutebrowser.utils import (utils, objreg, standarddir, log, qtutils, from qutebrowser.utils import (utils, objreg, standarddir, log, qtutils,
usertypes, message) usertypes, message)
from qutebrowser.config import config
from qutebrowser.misc import lineparser, objects from qutebrowser.misc import lineparser, objects
@ -280,8 +279,6 @@ class WebHistory(QObject):
(hidden in completion) (hidden in completion)
atime: Override the atime used to add the entry atime: Override the atime used to add the entry
""" """
if config.get('general', 'private-browsing'):
return
if not url.isValid(): # pragma: no cover if not url.isValid(): # pragma: no cover
# the no cover pragma is a WORKAROUND for this not being covered in # the no cover pragma is a WORKAROUND for this not being covered in
# old Qt versions. # old Qt versions.

View File

@ -127,17 +127,19 @@ def prevnext(*, browsertab, win_id, baseurl, prev=False,
return return
qtutils.ensure_valid(url) qtutils.ensure_valid(url)
cur_tabbed_browser = objreg.get('tabbed-browser', scope='window',
window=win_id)
if window: if window:
from qutebrowser.mainwindow import mainwindow from qutebrowser.mainwindow import mainwindow
new_window = mainwindow.MainWindow() new_window = mainwindow.MainWindow(
private=cur_tabbed_browser.private)
new_window.show() new_window.show()
tabbed_browser = objreg.get('tabbed-browser', scope='window', tabbed_browser = objreg.get('tabbed-browser', scope='window',
window=new_window.win_id) window=new_window.win_id)
tabbed_browser.tabopen(url, background=False) tabbed_browser.tabopen(url, background=False)
elif tab: elif tab:
tabbed_browser = objreg.get('tabbed-browser', scope='window', cur_tabbed_browser.tabopen(url, background=background)
window=win_id)
tabbed_browser.tabopen(url, background=background)
else: else:
browsertab.openurl(url) browsertab.openurl(url)

View File

@ -366,7 +366,7 @@ class DownloadManager(downloads.AbstractDownloadManager):
def __init__(self, win_id, parent=None): def __init__(self, win_id, parent=None):
super().__init__(parent) super().__init__(parent)
self._networkmanager = networkmanager.NetworkManager( self._networkmanager = networkmanager.NetworkManager(
win_id, None, self) win_id=win_id, tab_id=None, private=False, parent=self)
@pyqtSlot('QUrl') @pyqtSlot('QUrl')
def get(self, url, *, user_agent=None, **kwargs): def get(self, url, *, user_agent=None, **kwargs):

View File

@ -216,8 +216,10 @@ def get_tab(win_id, target):
win_id = win_id win_id = win_id
bg_tab = True bg_tab = True
elif target == usertypes.ClickTarget.window: elif target == usertypes.ClickTarget.window:
tabbed_browser = objreg.get('tabbed-browser', scope='window',
window=win_id)
from qutebrowser.mainwindow import mainwindow from qutebrowser.mainwindow import mainwindow
window = mainwindow.MainWindow() window = mainwindow.MainWindow(private=tabbed_browser.private)
window.show() window.show()
win_id = window.win_id win_id = window.win_id
bg_tab = False bg_tab = False

View File

@ -364,15 +364,16 @@ class AbstractWebElement(collections.abc.MutableMapping):
self._click_fake_event(click_target) self._click_fake_event(click_target)
return return
tabbed_browser = objreg.get('tabbed-browser', scope='window',
window=self._tab.win_id)
if click_target in [usertypes.ClickTarget.tab, if click_target in [usertypes.ClickTarget.tab,
usertypes.ClickTarget.tab_bg]: usertypes.ClickTarget.tab_bg]:
background = click_target == usertypes.ClickTarget.tab_bg background = click_target == usertypes.ClickTarget.tab_bg
tabbed_browser = objreg.get('tabbed-browser', scope='window',
window=self._tab.win_id)
tabbed_browser.tabopen(url, background=background) tabbed_browser.tabopen(url, background=background)
elif click_target == usertypes.ClickTarget.window: elif click_target == usertypes.ClickTarget.window:
from qutebrowser.mainwindow import mainwindow from qutebrowser.mainwindow import mainwindow
window = mainwindow.MainWindow() window = mainwindow.MainWindow(private=tabbed_browser.private)
window.show() window.show()
window.tabbed_browser.tabopen(url) window.tabbed_browser.tabopen(url)
else: else:

View File

@ -38,6 +38,12 @@ from qutebrowser.utils import (objreg, utils, standarddir, javascript, log,
qtutils) qtutils)
# The default QWebEngineProfile
default_profile = None
# The QWebEngineProfile used for private (off-the-record) windows
private_profile = None
class Attribute(websettings.Attribute): class Attribute(websettings.Attribute):
"""A setting set via QWebEngineSettings::setAttribute.""" """A setting set via QWebEngineSettings::setAttribute."""
@ -67,7 +73,7 @@ class StaticSetter(websettings.StaticSetter):
GLOBAL_SETTINGS = QWebEngineSettings.globalSettings GLOBAL_SETTINGS = QWebEngineSettings.globalSettings
class ProfileSetter(websettings.Base): class DefaultProfileSetter(websettings.Base):
"""A setting set on the QWebEngineProfile.""" """A setting set on the QWebEngineProfile."""
@ -78,16 +84,16 @@ class ProfileSetter(websettings.Base):
def get(self, settings=None): def get(self, settings=None):
utils.unused(settings) utils.unused(settings)
getter = getattr(QWebEngineProfile.defaultProfile(), self._getter) getter = getattr(default_profile, self._getter)
return getter() return getter()
def _set(self, value, settings=None): def _set(self, value, settings=None):
utils.unused(settings) utils.unused(settings)
setter = getattr(QWebEngineProfile.defaultProfile(), self._setter) setter = getattr(default_profile, self._setter)
setter(value) setter(value)
class PersistentCookiePolicy(ProfileSetter): class PersistentCookiePolicy(DefaultProfileSetter):
"""The cookies -> store setting is different from other settings.""" """The cookies -> store setting is different from other settings."""
@ -141,19 +147,27 @@ def _init_stylesheet(profile):
profile.scripts().insert(script) profile.scripts().insert(script)
def _init_profile(profile):
"""Initialize settings set on the QWebEngineProfile."""
profile.setCachePath(os.path.join(standarddir.cache(), 'webengine'))
profile.setPersistentStoragePath(
os.path.join(standarddir.data(), 'webengine'))
def update_settings(section, option): def update_settings(section, option):
"""Update global settings when qwebsettings changed.""" """Update global settings when qwebsettings changed."""
websettings.update_mappings(MAPPINGS, section, option) websettings.update_mappings(MAPPINGS, section, option)
profile = QWebEngineProfile.defaultProfile()
if section == 'ui' and option in ['hide-scrollbar', 'user-stylesheet']: if section == 'ui' and option in ['hide-scrollbar', 'user-stylesheet']:
_init_stylesheet(profile) _init_stylesheet(default_profile)
_init_stylesheet(private_profile)
def _init_profiles():
"""Init the two used QWebEngineProfiles."""
global default_profile, private_profile
default_profile = QWebEngineProfile.defaultProfile()
default_profile.setCachePath(
os.path.join(standarddir.cache(), 'webengine'))
default_profile.setPersistentStoragePath(
os.path.join(standarddir.data(), 'webengine'))
_init_stylesheet(default_profile)
private_profile = QWebEngineProfile()
assert private_profile.isOffTheRecord()
_init_stylesheet(private_profile)
def init(args): def init(args):
@ -173,9 +187,7 @@ def init(args):
else: else:
log.misc.debug("Imported PyOpenGL as workaround") log.misc.debug("Imported PyOpenGL as workaround")
profile = QWebEngineProfile.defaultProfile() _init_profiles()
_init_profile(profile)
_init_stylesheet(profile)
# We need to do this here as a WORKAROUND for # We need to do this here as a WORKAROUND for
# https://bugreports.qt.io/browse/QTBUG-58650 # https://bugreports.qt.io/browse/QTBUG-58650
if not qtutils.version_check('5.9'): if not qtutils.version_check('5.9'):
@ -282,7 +294,7 @@ MAPPINGS = {
'local-storage': 'local-storage':
Attribute(QWebEngineSettings.LocalStorageEnabled), Attribute(QWebEngineSettings.LocalStorageEnabled),
'cache-size': 'cache-size':
ProfileSetter(getter='httpCacheMaximumSize', DefaultProfileSetter(getter='httpCacheMaximumSize',
setter='setHttpCacheMaximumSize') setter='setHttpCacheMaximumSize')
}, },
'general': { 'general': {

View File

@ -27,14 +27,14 @@ from PyQt5.QtGui import QKeyEvent
from PyQt5.QtNetwork import QAuthenticator from PyQt5.QtNetwork import QAuthenticator
# pylint: disable=no-name-in-module,import-error,useless-suppression # pylint: disable=no-name-in-module,import-error,useless-suppression
from PyQt5.QtWidgets import QApplication from PyQt5.QtWidgets import QApplication
from PyQt5.QtWebEngineWidgets import (QWebEnginePage, QWebEngineScript, from PyQt5.QtWebEngineWidgets import QWebEnginePage, QWebEngineScript
QWebEngineProfile)
# pylint: enable=no-name-in-module,import-error,useless-suppression # pylint: enable=no-name-in-module,import-error,useless-suppression
from qutebrowser.browser import browsertab, mouse, shared from qutebrowser.browser import browsertab, mouse, shared
from qutebrowser.browser.webengine import (webview, webengineelem, tabhistory, from qutebrowser.browser.webengine import (webview, webengineelem, tabhistory,
interceptor, webenginequtescheme, interceptor, webenginequtescheme,
webenginedownloads) webenginedownloads,
webenginesettings)
from qutebrowser.misc import miscwidgets from qutebrowser.misc import miscwidgets
from qutebrowser.utils import (usertypes, qtutils, log, javascript, utils, from qutebrowser.utils import (usertypes, qtutils, log, javascript, utils,
objreg, jinja, debug) objreg, jinja, debug)
@ -50,21 +50,23 @@ def init():
# https://www.riverbankcomputing.com/pipermail/pyqt/2016-September/038075.html # https://www.riverbankcomputing.com/pipermail/pyqt/2016-September/038075.html
global _qute_scheme_handler global _qute_scheme_handler
app = QApplication.instance() app = QApplication.instance()
profile = QWebEngineProfile.defaultProfile()
log.init.debug("Initializing qute://* handler...") log.init.debug("Initializing qute://* handler...")
_qute_scheme_handler = webenginequtescheme.QuteSchemeHandler(parent=app) _qute_scheme_handler = webenginequtescheme.QuteSchemeHandler(parent=app)
_qute_scheme_handler.install(profile) _qute_scheme_handler.install(webenginesettings.default_profile)
_qute_scheme_handler.install(webenginesettings.private_profile)
log.init.debug("Initializing request interceptor...") log.init.debug("Initializing request interceptor...")
host_blocker = objreg.get('host-blocker') host_blocker = objreg.get('host-blocker')
req_interceptor = interceptor.RequestInterceptor( req_interceptor = interceptor.RequestInterceptor(
host_blocker, parent=app) host_blocker, parent=app)
req_interceptor.install(profile) req_interceptor.install(webenginesettings.default_profile)
req_interceptor.install(webenginesettings.private_profile)
log.init.debug("Initializing QtWebEngine downloads...") log.init.debug("Initializing QtWebEngine downloads...")
download_manager = webenginedownloads.DownloadManager(parent=app) download_manager = webenginedownloads.DownloadManager(parent=app)
download_manager.install(profile) download_manager.install(webenginesettings.default_profile)
download_manager.install(webenginesettings.private_profile)
objreg.register('webengine-download-manager', download_manager) objreg.register('webengine-download-manager', download_manager)
@ -521,10 +523,11 @@ class WebEngineTab(browsertab.AbstractTab):
"""A QtWebEngine tab in the browser.""" """A QtWebEngine tab in the browser."""
def __init__(self, win_id, mode_manager, parent=None): def __init__(self, *, win_id, mode_manager, private, parent=None):
super().__init__(win_id=win_id, mode_manager=mode_manager, super().__init__(win_id=win_id, mode_manager=mode_manager,
parent=parent) private=private, parent=parent)
widget = webview.WebEngineView(tabdata=self.data, win_id=win_id) widget = webview.WebEngineView(tabdata=self.data, win_id=win_id,
private=private)
self.history = WebEngineHistory(self) self.history = WebEngineHistory(self)
self.scroller = WebEngineScroller(self, parent=self) self.scroller = WebEngineScroller(self, parent=self)
self.caret = WebEngineCaret(win_id=win_id, mode_manager=mode_manager, self.caret = WebEngineCaret(win_id=win_id, mode_manager=mode_manager,

View File

@ -28,7 +28,7 @@ from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEnginePage
# pylint: enable=no-name-in-module,import-error,useless-suppression # pylint: enable=no-name-in-module,import-error,useless-suppression
from qutebrowser.browser import shared from qutebrowser.browser import shared
from qutebrowser.browser.webengine import certificateerror from qutebrowser.browser.webengine import certificateerror, webenginesettings
from qutebrowser.config import config from qutebrowser.config import config
from qutebrowser.utils import (log, debug, usertypes, jinja, urlutils, message, from qutebrowser.utils import (log, debug, usertypes, jinja, urlutils, message,
objreg) objreg)
@ -38,13 +38,19 @@ class WebEngineView(QWebEngineView):
"""Custom QWebEngineView subclass with qutebrowser-specific features.""" """Custom QWebEngineView subclass with qutebrowser-specific features."""
def __init__(self, tabdata, win_id, parent=None): def __init__(self, *, tabdata, win_id, private, parent=None):
super().__init__(parent) super().__init__(parent)
self._win_id = win_id self._win_id = win_id
self._tabdata = tabdata self._tabdata = tabdata
theme_color = self.style().standardPalette().color(QPalette.Base) theme_color = self.style().standardPalette().color(QPalette.Base)
page = WebEnginePage(theme_color=theme_color, parent=self) if private:
profile = webenginesettings.private_profile
assert profile.isOffTheRecord()
else:
profile = webenginesettings.default_profile
page = WebEnginePage(theme_color=theme_color, profile=profile,
parent=self)
self.setPage(page) self.setPage(page)
def shutdown(self): def shutdown(self):
@ -124,8 +130,8 @@ class WebEnginePage(QWebEnginePage):
certificate_error = pyqtSignal() certificate_error = pyqtSignal()
shutting_down = pyqtSignal() shutting_down = pyqtSignal()
def __init__(self, theme_color, parent=None): def __init__(self, *, theme_color, profile, parent=None):
super().__init__(parent) super().__init__(profile, parent)
self._is_shutting_down = False self._is_shutting_down = False
self.featurePermissionRequested.connect( self.featurePermissionRequested.connect(
self._on_feature_permission_requested) self._on_feature_permission_requested)

View File

@ -21,8 +21,7 @@
import os.path import os.path
from PyQt5.QtCore import pyqtSlot from PyQt5.QtNetwork import QNetworkDiskCache
from PyQt5.QtNetwork import QNetworkDiskCache, QNetworkCacheMetaData
from qutebrowser.config import config from qutebrowser.config import config
from qutebrowser.utils import utils, objreg, qtutils from qutebrowser.utils import utils, objreg, qtutils
@ -30,24 +29,21 @@ from qutebrowser.utils import utils, objreg, qtutils
class DiskCache(QNetworkDiskCache): class DiskCache(QNetworkDiskCache):
"""Disk cache which sets correct cache dir and size. """Disk cache which sets correct cache dir and size."""
Attributes:
_activated: Whether the cache should be used.
_cache_dir: The base directory for cache files (standarddir.cache())
"""
def __init__(self, cache_dir, parent=None): def __init__(self, cache_dir, parent=None):
super().__init__(parent) super().__init__(parent)
self._cache_dir = cache_dir assert not config.get('general', 'private-browsing')
self._maybe_activate() self.setCacheDirectory(os.path.join(cache_dir, 'http'))
objreg.get('config').changed.connect(self.on_config_changed) self._set_cache_size()
objreg.get('config').changed.connect(self._set_cache_size)
def __repr__(self): def __repr__(self):
return utils.get_repr(self, size=self.cacheSize(), return utils.get_repr(self, size=self.cacheSize(),
maxsize=self.maximumCacheSize(), maxsize=self.maximumCacheSize(),
path=self.cacheDirectory()) path=self.cacheDirectory())
@config.change_filter('storage', 'cache-size')
def _set_cache_size(self): def _set_cache_size(self):
"""Set the cache size based on the config.""" """Set the cache size based on the config."""
size = config.get('storage', 'cache-size') size = config.get('storage', 'cache-size')
@ -58,128 +54,3 @@ class DiskCache(QNetworkDiskCache):
not qtutils.version_check('5.9')): # pragma: no cover not qtutils.version_check('5.9')): # pragma: no cover
size = 0 size = 0
self.setMaximumCacheSize(size) self.setMaximumCacheSize(size)
def _maybe_activate(self):
"""Activate/deactivate the cache based on the config."""
if config.get('general', 'private-browsing'):
self._activated = False
else:
self._activated = True
self.setCacheDirectory(os.path.join(self._cache_dir, 'http'))
self._set_cache_size()
@pyqtSlot(str, str)
def on_config_changed(self, section, option):
"""Update cache size/activated if the config was changed."""
if (section, option) == ('storage', 'cache-size'):
self._set_cache_size()
elif (section, option) == ('general', # pragma: no branch
'private-browsing'):
self._maybe_activate()
def cacheSize(self):
"""Return the current size taken up by the cache.
Return:
An int.
"""
if self._activated:
return super().cacheSize()
else:
return 0
def fileMetaData(self, filename):
"""Return the QNetworkCacheMetaData for the cache file filename.
Args:
filename: The file name as a string.
Return:
A QNetworkCacheMetaData object.
"""
if self._activated:
return super().fileMetaData(filename)
else:
return QNetworkCacheMetaData()
def data(self, url):
"""Return the data associated with url.
Args:
url: A QUrl.
return:
A QIODevice or None.
"""
if self._activated:
return super().data(url)
else:
return None
def insert(self, device):
"""Insert the data in device and the prepared meta data into the cache.
Args:
device: A QIODevice.
"""
if self._activated:
super().insert(device)
else:
return None
def metaData(self, url):
"""Return the meta data for the url url.
Args:
url: A QUrl.
Return:
A QNetworkCacheMetaData object.
"""
if self._activated:
return super().metaData(url)
else:
return QNetworkCacheMetaData()
def prepare(self, meta_data):
"""Return the device that should be populated with the data.
Args:
meta_data: A QNetworkCacheMetaData object.
Return:
A QIODevice or None.
"""
if self._activated:
return super().prepare(meta_data)
else:
return None
def remove(self, url):
"""Remove the cache entry for url.
Return:
True on success, False otherwise.
"""
if self._activated:
return super().remove(url)
else:
return False
def updateMetaData(self, meta_data):
"""Update the cache meta date for the meta_data's url to meta_data.
Args:
meta_data: A QNetworkCacheMetaData object.
"""
if self._activated:
super().updateMetaData(meta_data)
else:
return
def clear(self):
"""Remove all items from the cache."""
if self._activated:
super().clear()
else:
return

View File

@ -128,6 +128,7 @@ class NetworkManager(QNetworkAccessManager):
_tab_id: The tab ID this NetworkManager is associated with. _tab_id: The tab ID this NetworkManager is associated with.
_rejected_ssl_errors: A {QUrl: [SslError]} dict of rejected errors. _rejected_ssl_errors: A {QUrl: [SslError]} dict of rejected errors.
_accepted_ssl_errors: A {QUrl: [SslError]} dict of accepted errors. _accepted_ssl_errors: A {QUrl: [SslError]} dict of accepted errors.
_private: Whether we're in private browsing mode.
Signals: Signals:
shutting_down: Emitted when the QNAM is shutting down. shutting_down: Emitted when the QNAM is shutting down.
@ -135,7 +136,7 @@ class NetworkManager(QNetworkAccessManager):
shutting_down = pyqtSignal() shutting_down = pyqtSignal()
def __init__(self, win_id, tab_id, parent=None): def __init__(self, *, win_id, tab_id, private, parent=None):
log.init.debug("Initializing NetworkManager") log.init.debug("Initializing NetworkManager")
with log.disable_qt_msghandler(): with log.disable_qt_msghandler():
# WORKAROUND for a hang when a message is printed - See: # WORKAROUND for a hang when a message is printed - See:
@ -146,11 +147,12 @@ class NetworkManager(QNetworkAccessManager):
self._win_id = win_id self._win_id = win_id
self._tab_id = tab_id self._tab_id = tab_id
self._requests = [] self._requests = []
self._private = private
self._scheme_handlers = { self._scheme_handlers = {
'qute': webkitqutescheme.QuteSchemeHandler(win_id), 'qute': webkitqutescheme.QuteSchemeHandler(win_id),
'file': filescheme.FileSchemeHandler(win_id), 'file': filescheme.FileSchemeHandler(win_id),
} }
self._set_cookiejar(private=config.get('general', 'private-browsing')) self._set_cookiejar()
self._set_cache() self._set_cache()
self.sslErrors.connect(self.on_ssl_errors) self.sslErrors.connect(self.on_ssl_errors)
self._rejected_ssl_errors = collections.defaultdict(list) self._rejected_ssl_errors = collections.defaultdict(list)
@ -158,15 +160,10 @@ class NetworkManager(QNetworkAccessManager):
self.authenticationRequired.connect(self.on_authentication_required) self.authenticationRequired.connect(self.on_authentication_required)
self.proxyAuthenticationRequired.connect( self.proxyAuthenticationRequired.connect(
self.on_proxy_authentication_required) self.on_proxy_authentication_required)
objreg.get('config').changed.connect(self.on_config_changed)
def _set_cookiejar(self, private=False): def _set_cookiejar(self):
"""Set the cookie jar of the NetworkManager correctly. """Set the cookie jar of the NetworkManager correctly."""
if self._private:
Args:
private: Whether we're currently in private browsing mode.
"""
if private:
cookie_jar = objreg.get('ram-cookie-jar') cookie_jar = objreg.get('ram-cookie-jar')
else: else:
cookie_jar = objreg.get('cookie-jar') cookie_jar = objreg.get('cookie-jar')
@ -178,11 +175,9 @@ class NetworkManager(QNetworkAccessManager):
cookie_jar.setParent(app) cookie_jar.setParent(app)
def _set_cache(self): def _set_cache(self):
"""Set the cache of the NetworkManager correctly. """Set the cache of the NetworkManager correctly."""
if self._private:
We can't switch the whole cache in private mode because QNAM would return
delete the old cache.
"""
# We have a shared cache - we restore its parent so we don't take # We have a shared cache - we restore its parent so we don't take
# ownership of it. # ownership of it.
app = QCoreApplication.instance() app = QCoreApplication.instance()
@ -324,17 +319,6 @@ class NetworkManager(QNetworkAccessManager):
authenticator.setPassword(answer.password) authenticator.setPassword(answer.password)
_proxy_auth_cache[proxy_id] = answer _proxy_auth_cache[proxy_id] = answer
@config.change_filter('general', 'private-browsing')
def on_config_changed(self):
"""Set cookie jar when entering/leaving private browsing mode."""
private_browsing = config.get('general', 'private-browsing')
if private_browsing:
# switched from normal mode to private mode
self._set_cookiejar(private=True)
else:
# switched from private mode to normal mode
self._set_cookiejar()
@pyqtSlot() @pyqtSlot()
def on_adopted_download_destroyed(self): def on_adopted_download_destroyed(self):
"""Check if we can clean up if an adopted download was destroyed. """Check if we can clean up if an adopted download was destroyed.

View File

@ -29,7 +29,7 @@ import os.path
from PyQt5.QtWebKit import QWebSettings from PyQt5.QtWebKit import QWebSettings
from qutebrowser.config import config, websettings from qutebrowser.config import config, websettings
from qutebrowser.utils import standarddir, objreg, urlutils, qtutils, message from qutebrowser.utils import standarddir, objreg, urlutils, qtutils
from qutebrowser.browser import shared from qutebrowser.browser import shared
@ -88,21 +88,9 @@ def _set_user_stylesheet():
QWebSettings.globalSettings().setUserStyleSheetUrl(url) QWebSettings.globalSettings().setUserStyleSheetUrl(url)
def _init_private_browsing():
if config.get('general', 'private-browsing'):
if qtutils.is_qtwebkit_ng():
message.warning("Private browsing is not fully implemented by "
"QtWebKit-NG!")
QWebSettings.setIconDatabasePath('')
else:
QWebSettings.setIconDatabasePath(standarddir.cache())
def update_settings(section, option): def update_settings(section, option):
"""Update global settings when qwebsettings changed.""" """Update global settings when qwebsettings changed."""
if (section, option) == ('general', 'private-browsing'): if section == 'ui' and option in ['hide-scrollbar', 'user-stylesheet']:
_init_private_browsing()
elif section == 'ui' and option in ['hide-scrollbar', 'user-stylesheet']:
_set_user_stylesheet() _set_user_stylesheet()
websettings.update_mappings(MAPPINGS, section, option) websettings.update_mappings(MAPPINGS, section, option)
@ -113,8 +101,7 @@ def init(_args):
cache_path = standarddir.cache() cache_path = standarddir.cache()
data_path = standarddir.data() data_path = standarddir.data()
_init_private_browsing() QWebSettings.setIconDatabasePath(standarddir.cache())
QWebSettings.setOfflineWebApplicationCachePath( QWebSettings.setOfflineWebApplicationCachePath(
os.path.join(cache_path, 'application-cache')) os.path.join(cache_path, 'application-cache'))
QWebSettings.globalSettings().setLocalStoragePath( QWebSettings.globalSettings().setLocalStoragePath(
@ -122,6 +109,13 @@ def init(_args):
QWebSettings.setOfflineStoragePath( QWebSettings.setOfflineStoragePath(
os.path.join(data_path, 'offline-storage')) os.path.join(data_path, 'offline-storage'))
if (config.get('general', 'private-browsing') and
not qtutils.version_check('5.4.2')):
# WORKAROUND for https://codereview.qt-project.org/#/c/108936/
# Won't work when private browsing is not enabled globally, but that's
# the best we can do...
QWebSettings.setIconDatabasePath('')
websettings.init_mappings(MAPPINGS) websettings.init_mappings(MAPPINGS)
_set_user_stylesheet() _set_user_stylesheet()
objreg.get('config').changed.connect(update_settings) objreg.get('config').changed.connect(update_settings)
@ -254,8 +248,6 @@ MAPPINGS = {
setter=QWebSettings.setOfflineWebApplicationCacheQuota), setter=QWebSettings.setOfflineWebApplicationCacheQuota),
}, },
'general': { 'general': {
'private-browsing':
Attribute(QWebSettings.PrivateBrowsingEnabled),
'developer-extras': 'developer-extras':
Attribute(QWebSettings.DeveloperExtrasEnabled), Attribute(QWebSettings.DeveloperExtrasEnabled),
'print-element-backgrounds': 'print-element-backgrounds':

View File

@ -629,10 +629,13 @@ class WebKitTab(browsertab.AbstractTab):
"""A QtWebKit tab in the browser.""" """A QtWebKit tab in the browser."""
def __init__(self, win_id, mode_manager, parent=None): def __init__(self, *, win_id, mode_manager, private, parent=None):
super().__init__(win_id=win_id, mode_manager=mode_manager, super().__init__(win_id=win_id, mode_manager=mode_manager,
parent=parent) private=private, parent=parent)
widget = webview.WebView(win_id, self.tab_id, tab=self) widget = webview.WebView(win_id=win_id, tab_id=self.tab_id,
private=private, tab=self)
if private:
self._make_private(widget)
self.history = WebKitHistory(self) self.history = WebKitHistory(self)
self.scroller = WebKitScroller(self, parent=self) self.scroller = WebKitScroller(self, parent=self)
self.caret = WebKitCaret(win_id=win_id, mode_manager=mode_manager, self.caret = WebKitCaret(win_id=win_id, mode_manager=mode_manager,
@ -649,6 +652,10 @@ class WebKitTab(browsertab.AbstractTab):
def _install_event_filter(self): def _install_event_filter(self):
self._widget.installEventFilter(self._mouse_event_filter) self._widget.installEventFilter(self._mouse_event_filter)
def _make_private(self, widget):
settings = widget.settings()
settings.setAttribute(QWebSettings.PrivateBrowsingEnabled, True)
def openurl(self, url): def openurl(self, url):
self._openurl_prepare(url) self._openurl_prepare(url)
self._widget.openurl(url) self._widget.openurl(url)

View File

@ -59,7 +59,7 @@ class BrowserPage(QWebPage):
shutting_down = pyqtSignal() shutting_down = pyqtSignal()
reloading = pyqtSignal(QUrl) reloading = pyqtSignal(QUrl)
def __init__(self, win_id, tab_id, tabdata, parent=None): def __init__(self, win_id, tab_id, tabdata, private, parent=None):
super().__init__(parent) super().__init__(parent)
self._win_id = win_id self._win_id = win_id
self._tabdata = tabdata self._tabdata = tabdata
@ -72,7 +72,7 @@ class BrowserPage(QWebPage):
self.error_occurred = False self.error_occurred = False
self.open_target = usertypes.ClickTarget.normal self.open_target = usertypes.ClickTarget.normal
self._networkmanager = networkmanager.NetworkManager( self._networkmanager = networkmanager.NetworkManager(
win_id, tab_id, self) win_id=win_id, tab_id=tab_id, private=private, parent=self)
self.setNetworkAccessManager(self._networkmanager) self.setNetworkAccessManager(self._networkmanager)
self.setForwardUnsupportedContent(True) self.setForwardUnsupportedContent(True)
self.reloading.connect(self._networkmanager.clear_rejected_ssl_errors) self.reloading.connect(self._networkmanager.clear_rejected_ssl_errors)

View File

@ -55,7 +55,7 @@ class WebView(QWebView):
scroll_pos_changed = pyqtSignal(int, int) scroll_pos_changed = pyqtSignal(int, int)
shutting_down = pyqtSignal() shutting_down = pyqtSignal()
def __init__(self, win_id, tab_id, tab, parent=None): def __init__(self, *, win_id, tab_id, tab, private, parent=None):
super().__init__(parent) super().__init__(parent)
if sys.platform == 'darwin' and qtutils.version_check('5.4'): if sys.platform == 'darwin' and qtutils.version_check('5.4'):
# WORKAROUND for https://bugreports.qt.io/browse/QTBUG-42948 # WORKAROUND for https://bugreports.qt.io/browse/QTBUG-42948
@ -71,7 +71,8 @@ class WebView(QWebView):
self._set_bg_color() self._set_bg_color()
self._tab_id = tab_id self._tab_id = tab_id
page = webpage.BrowserPage(self.win_id, self._tab_id, tab.data, page = webpage.BrowserPage(win_id=self.win_id, tab_id=self._tab_id,
tabdata=tab.data, private=private,
parent=self) parent=self)
try: try:

View File

@ -185,10 +185,9 @@ def data(readonly=False):
"Encoding to use for editor."), "Encoding to use for editor."),
('private-browsing', ('private-browsing',
SettingValue(typ.Bool(), 'false', SettingValue(typ.Bool(), 'false'),
backends=[usertypes.Backend.QtWebKit]), "Open new windows in private browsing mode which does not record "
"Do not record visited pages in the history or store web page " "visited pages."),
"icons."),
('developer-extras', ('developer-extras',
SettingValue(typ.Bool(), 'false', SettingValue(typ.Bool(), 'false',
@ -1125,6 +1124,14 @@ def data(readonly=False):
SettingValue(typ.QssColor(), 'black'), SettingValue(typ.QssColor(), 'black'),
"Background color of the statusbar."), "Background color of the statusbar."),
('statusbar.fg.private',
SettingValue(typ.QssColor(), '${statusbar.fg}'),
"Foreground color of the statusbar in private browsing mode."),
('statusbar.bg.private',
SettingValue(typ.QssColor(), '#666666'),
"Background color of the statusbar in private browsing mode."),
('statusbar.fg.insert', ('statusbar.fg.insert',
SettingValue(typ.QssColor(), '${statusbar.fg}'), SettingValue(typ.QssColor(), '${statusbar.fg}'),
"Foreground color of the statusbar in insert mode."), "Foreground color of the statusbar in insert mode."),
@ -1141,6 +1148,16 @@ def data(readonly=False):
SettingValue(typ.QssColor(), '${statusbar.bg}'), SettingValue(typ.QssColor(), '${statusbar.bg}'),
"Background color of the statusbar in command mode."), "Background color of the statusbar in command mode."),
('statusbar.fg.command.private',
SettingValue(typ.QssColor(), '${statusbar.fg.private}'),
"Foreground color of the statusbar in private browsing + command "
"mode."),
('statusbar.bg.command.private',
SettingValue(typ.QssColor(), '${statusbar.bg.private}'),
"Background color of the statusbar in private browsing + command "
"mode."),
('statusbar.fg.caret', ('statusbar.fg.caret',
SettingValue(typ.QssColor(), '${statusbar.fg}'), SettingValue(typ.QssColor(), '${statusbar.fg}'),
"Foreground color of the statusbar in caret mode."), "Foreground color of the statusbar in caret mode."),

View File

@ -81,7 +81,7 @@ def get_window(via_ipc, force_window=False, force_tab=False,
# Otherwise, or if no window was found, create a new one # Otherwise, or if no window was found, create a new one
if window is None: if window is None:
window = MainWindow() window = MainWindow(private=None)
window.show() window.show()
raise_window = True raise_window = True
@ -127,13 +127,15 @@ class MainWindow(QWidget):
_vbox: The main QVBoxLayout. _vbox: The main QVBoxLayout.
_commandrunner: The main CommandRunner instance. _commandrunner: The main CommandRunner instance.
_overlays: Widgets shown as overlay for the current webpage. _overlays: Widgets shown as overlay for the current webpage.
_private: Whether the window is in private browsing mode.
""" """
def __init__(self, geometry=None, parent=None): def __init__(self, *, private, geometry=None, parent=None):
"""Create a new main window. """Create a new main window.
Args: Args:
geometry: The geometry to load, as a bytes-object (or None). geometry: The geometry to load, as a bytes-object (or None).
private: Whether the window is in private browsing mode.
parent: The parent the window should get. parent: The parent the window should get.
""" """
super().__init__(parent) super().__init__(parent)
@ -161,7 +163,14 @@ class MainWindow(QWidget):
self._init_downloadmanager() self._init_downloadmanager()
self._downloadview = downloadview.DownloadView(self.win_id) self._downloadview = downloadview.DownloadView(self.win_id)
self.tabbed_browser = tabbedbrowser.TabbedBrowser(self.win_id) if config.get('general', 'private-browsing'):
# This setting always trumps what's passed in.
private = True
else:
private = bool(private)
self._private = private
self.tabbed_browser = tabbedbrowser.TabbedBrowser(win_id=self.win_id,
private=private)
objreg.register('tabbed-browser', self.tabbed_browser, scope='window', objreg.register('tabbed-browser', self.tabbed_browser, scope='window',
window=self.win_id) window=self.win_id)
self._init_command_dispatcher() self._init_command_dispatcher()
@ -169,7 +178,8 @@ class MainWindow(QWidget):
# We need to set an explicit parent for StatusBar because it does some # We need to set an explicit parent for StatusBar because it does some
# show/hide magic immediately which would mean it'd show up as a # show/hide magic immediately which would mean it'd show up as a
# window. # window.
self.status = bar.StatusBar(self.win_id, parent=self) self.status = bar.StatusBar(win_id=self.win_id, private=private,
parent=self)
self._add_widgets() self._add_widgets()
self._downloadview.show() self._downloadview.show()
@ -196,15 +206,7 @@ class MainWindow(QWidget):
self._messageview = messageview.MessageView(parent=self) self._messageview = messageview.MessageView(parent=self)
self._add_overlay(self._messageview, self._messageview.update_geometry) self._add_overlay(self._messageview, self._messageview.update_geometry)
if geometry is not None: self._init_geometry(geometry)
self._load_geometry(geometry)
elif self.win_id == 0:
self._load_state_geometry()
else:
self._set_default_geometry()
log.init.debug("Initial main window geometry: {}".format(
self.geometry()))
self._connect_signals() self._connect_signals()
# When we're here the statusbar might not even really exist yet, so # When we're here the statusbar might not even really exist yet, so
@ -215,6 +217,17 @@ class MainWindow(QWidget):
objreg.get("app").new_window.emit(self) objreg.get("app").new_window.emit(self)
def _init_geometry(self, geometry):
"""Initialize the window geometry or load it from disk."""
if geometry is not None:
self._load_geometry(geometry)
elif self.win_id == 0:
self._load_state_geometry()
else:
self._set_default_geometry()
log.init.debug("Initial main window geometry: {}".format(
self.geometry()))
def _add_overlay(self, widget, signal, *, centered=False, padding=0): def _add_overlay(self, widget, signal, *, centered=False, padding=0):
self._overlays.append((widget, signal, centered, padding)) self._overlays.append((widget, signal, centered, padding))
@ -446,17 +459,15 @@ class MainWindow(QWidget):
message_bridge.s_maybe_reset_text.connect(status.txt.maybe_reset_text) message_bridge.s_maybe_reset_text.connect(status.txt.maybe_reset_text)
# statusbar # statusbar
tabs.current_tab_changed.connect(status.prog.on_tab_changed) tabs.current_tab_changed.connect(status.on_tab_changed)
tabs.cur_progress.connect(status.prog.setValue) tabs.cur_progress.connect(status.prog.setValue)
tabs.cur_load_finished.connect(status.prog.hide) tabs.cur_load_finished.connect(status.prog.hide)
tabs.cur_load_started.connect(status.prog.on_load_started) tabs.cur_load_started.connect(status.prog.on_load_started)
tabs.current_tab_changed.connect(status.percentage.on_tab_changed)
tabs.cur_scroll_perc_changed.connect(status.percentage.set_perc) tabs.cur_scroll_perc_changed.connect(status.percentage.set_perc)
tabs.tab_index_changed.connect(status.tabindex.on_tab_index_changed) tabs.tab_index_changed.connect(status.tabindex.on_tab_index_changed)
tabs.current_tab_changed.connect(status.url.on_tab_changed)
tabs.cur_url_changed.connect(status.url.set_url) tabs.cur_url_changed.connect(status.url.set_url)
tabs.cur_link_hovered.connect(status.url.set_hover_url) tabs.cur_link_hovered.connect(status.url.set_hover_url)
tabs.cur_load_status_changed.connect(status.url.on_load_status_changed) tabs.cur_load_status_changed.connect(status.url.on_load_status_changed)

View File

@ -22,6 +22,7 @@
from PyQt5.QtCore import pyqtSignal, pyqtSlot, pyqtProperty, Qt, QSize, QTimer from PyQt5.QtCore import pyqtSignal, pyqtSlot, pyqtProperty, Qt, QSize, QTimer
from PyQt5.QtWidgets import QWidget, QHBoxLayout, QStackedLayout, QSizePolicy from PyQt5.QtWidgets import QWidget, QHBoxLayout, QStackedLayout, QSizePolicy
from qutebrowser.browser import browsertab
from qutebrowser.config import config, style from qutebrowser.config import config, style
from qutebrowser.utils import usertypes, log, objreg, utils from qutebrowser.utils import usertypes, log, objreg, utils
from qutebrowser.mainwindow.statusbar import (command, progress, keystring, from qutebrowser.mainwindow.statusbar import (command, progress, keystring,
@ -29,7 +30,85 @@ from qutebrowser.mainwindow.statusbar import (command, progress, keystring,
from qutebrowser.mainwindow.statusbar import text as textwidget from qutebrowser.mainwindow.statusbar import text as textwidget
CaretMode = usertypes.enum('CaretMode', ['off', 'on', 'selection']) class ColorFlags:
"""Flags which change the appearance of the statusbar.
Attributes:
prompt: If we're currently in prompt-mode.
insert: If we're currently in insert mode.
command: If we're currently in command mode.
mode: The current caret mode (CaretMode.off/.on/.selection).
private: Whether this window is in private browsing mode.
"""
CaretMode = usertypes.enum('CaretMode', ['off', 'on', 'selection'])
def __init__(self):
self.prompt = False
self.insert = False
self.command = False
self.caret = self.CaretMode.off
self.private = False
def to_stringlist(self):
"""Get a string list of set flags used in the stylesheet.
This also combines flags in ways they're used in the sheet.
"""
strings = []
if self.prompt:
strings.append('prompt')
if self.insert:
strings.append('insert')
if self.command:
strings.append('command')
if self.private:
strings.append('private')
if self.private and self.command:
strings.append('private-command')
if self.caret == self.CaretMode.on:
strings.append('caret')
elif self.caret == self.CaretMode.selection:
strings.append('caret-selection')
else:
assert self.caret == self.CaretMode.off
return strings
def _generate_stylesheet():
flags = [
('private', 'statusbar.{}.private'),
('caret', 'statusbar.{}.caret'),
('caret-selection', 'statusbar.{}.caret-selection'),
('prompt', 'prompts.{}'),
('insert', 'statusbar.{}.insert'),
('command', 'statusbar.{}.command'),
('private-command', 'statusbar.{}.command.private'),
]
stylesheet = """
QWidget#StatusBar,
QWidget#StatusBar QLabel,
QWidget#StatusBar QLineEdit {
font: {{ font['statusbar'] }};
background-color: {{ color['statusbar.bg'] }};
color: {{ color['statusbar.fg'] }};
}
"""
for flag, option in flags:
stylesheet += """
QWidget#StatusBar[color_flags~="%s"],
QWidget#StatusBar[color_flags~="%s"] QLabel,
QWidget#StatusBar[color_flags~="%s"] QLineEdit {
color: {{ color['%s'] }};
background-color: {{ color['%s'] }};
}
""" % (flag, flag, flag, # flake8: disable=S001
option.format('fg'), option.format('bg'))
return stylesheet
class StatusBar(QWidget): class StatusBar(QWidget):
@ -49,27 +128,6 @@ class StatusBar(QWidget):
_page_fullscreen: Whether the webpage (e.g. a video) is shown _page_fullscreen: Whether the webpage (e.g. a video) is shown
fullscreen. fullscreen.
Class attributes:
_prompt_active: If we're currently in prompt-mode.
For some reason we need to have this as class attribute
so pyqtProperty works correctly.
_insert_active: If we're currently in insert mode.
For some reason we need to have this as class attribute
so pyqtProperty works correctly.
_command_active: If we're currently in command mode.
For some reason we need to have this as class
attribute so pyqtProperty works correctly.
_caret_mode: The current caret mode (off/on/selection).
For some reason we need to have this as class attribute
so pyqtProperty works correctly.
Signals: Signals:
resized: Emitted when the statusbar has resized, so the completion resized: Emitted when the statusbar has resized, so the completion
widget can adjust its size to it. widget can adjust its size to it.
@ -82,59 +140,11 @@ class StatusBar(QWidget):
resized = pyqtSignal('QRect') resized = pyqtSignal('QRect')
moved = pyqtSignal('QPoint') moved = pyqtSignal('QPoint')
_severity = None _severity = None
_prompt_active = False _color_flags = []
_insert_active = False
_command_active = False
_caret_mode = CaretMode.off
STYLESHEET = """ STYLESHEET = _generate_stylesheet()
QWidget#StatusBar, def __init__(self, *, win_id, private, parent=None):
QWidget#StatusBar QLabel,
QWidget#StatusBar QLineEdit {
font: {{ font['statusbar'] }};
background-color: {{ color['statusbar.bg'] }};
color: {{ color['statusbar.fg'] }};
}
QWidget#StatusBar[caret_mode="on"],
QWidget#StatusBar[caret_mode="on"] QLabel,
QWidget#StatusBar[caret_mode="on"] QLineEdit {
color: {{ color['statusbar.fg.caret'] }};
background-color: {{ color['statusbar.bg.caret'] }};
}
QWidget#StatusBar[caret_mode="selection"],
QWidget#StatusBar[caret_mode="selection"] QLabel,
QWidget#StatusBar[caret_mode="selection"] QLineEdit {
color: {{ color['statusbar.fg.caret-selection'] }};
background-color: {{ color['statusbar.bg.caret-selection'] }};
}
QWidget#StatusBar[prompt_active="true"],
QWidget#StatusBar[prompt_active="true"] QLabel,
QWidget#StatusBar[prompt_active="true"] QLineEdit {
color: {{ color['prompts.fg'] }};
background-color: {{ color['prompts.bg'] }};
}
QWidget#StatusBar[insert_active="true"],
QWidget#StatusBar[insert_active="true"] QLabel,
QWidget#StatusBar[insert_active="true"] QLineEdit {
color: {{ color['statusbar.fg.insert'] }};
background-color: {{ color['statusbar.bg.insert'] }};
}
QWidget#StatusBar[command_active="true"],
QWidget#StatusBar[command_active="true"] QLabel,
QWidget#StatusBar[command_active="true"] QLineEdit {
color: {{ color['statusbar.fg.command'] }};
background-color: {{ color['statusbar.bg.command'] }};
}
"""
def __init__(self, win_id, parent=None):
super().__init__(parent) super().__init__(parent)
objreg.register('statusbar', self, scope='window', window=win_id) objreg.register('statusbar', self, scope='window', window=win_id)
self.setObjectName(self.__class__.__name__) self.setObjectName(self.__class__.__name__)
@ -146,6 +156,8 @@ class StatusBar(QWidget):
self._win_id = win_id self._win_id = win_id
self._option = None self._option = None
self._page_fullscreen = False self._page_fullscreen = False
self._color_flags = ColorFlags()
self._color_flags.private = private
self._hbox = QHBoxLayout(self) self._hbox = QHBoxLayout(self)
self.set_hbox_padding() self.set_hbox_padding()
@ -156,7 +168,7 @@ class StatusBar(QWidget):
self._hbox.addLayout(self._stack) self._hbox.addLayout(self._stack)
self._stack.setContentsMargins(0, 0, 0, 0) self._stack.setContentsMargins(0, 0, 0, 0)
self.cmd = command.Command(win_id) self.cmd = command.Command(private=private, win_id=win_id)
self._stack.addWidget(self.cmd) self._stack.addWidget(self.cmd)
objreg.register('status-command', self.cmd, scope='window', objreg.register('status-command', self.cmd, scope='window',
window=win_id) window=win_id)
@ -206,25 +218,10 @@ class StatusBar(QWidget):
padding = config.get('ui', 'statusbar-padding') padding = config.get('ui', 'statusbar-padding')
self._hbox.setContentsMargins(padding.left, 0, padding.right, 0) self._hbox.setContentsMargins(padding.left, 0, padding.right, 0)
@pyqtProperty(bool) @pyqtProperty('QStringList')
def prompt_active(self): def color_flags(self):
"""Getter for self.prompt_active, so it can be used as Qt property.""" """Getter for self.color_flags, so it can be used as Qt property."""
return self._prompt_active return self._color_flags.to_stringlist()
@pyqtProperty(bool)
def command_active(self):
"""Getter for self.command_active, so it can be used as Qt property."""
return self._command_active
@pyqtProperty(bool)
def insert_active(self):
"""Getter for self.insert_active, so it can be used as Qt property."""
return self._insert_active
@pyqtProperty(str)
def caret_mode(self):
"""Getter for self._caret_mode, so it can be used as Qt property."""
return self._caret_mode.name
def set_mode_active(self, mode, val): def set_mode_active(self, mode, val):
"""Setter for self.{insert,command,caret}_active. """Setter for self.{insert,command,caret}_active.
@ -233,28 +230,28 @@ class StatusBar(QWidget):
updated by Qt properly. updated by Qt properly.
""" """
if mode == usertypes.KeyMode.insert: if mode == usertypes.KeyMode.insert:
log.statusbar.debug("Setting insert_active to {}".format(val)) log.statusbar.debug("Setting insert flag to {}".format(val))
self._insert_active = val self._color_flags.insert = val
if mode == usertypes.KeyMode.command: if mode == usertypes.KeyMode.command:
log.statusbar.debug("Setting command_active to {}".format(val)) log.statusbar.debug("Setting command flag to {}".format(val))
self._command_active = val self._color_flags.command = val
elif mode in [usertypes.KeyMode.prompt, usertypes.KeyMode.yesno]: elif mode in [usertypes.KeyMode.prompt, usertypes.KeyMode.yesno]:
log.statusbar.debug("Setting prompt_active to {}".format(val)) log.statusbar.debug("Setting prompt flag to {}".format(val))
self._prompt_active = val self._color_flags.prompt = val
elif mode == usertypes.KeyMode.caret: elif mode == usertypes.KeyMode.caret:
tab = objreg.get('tabbed-browser', scope='window', tab = objreg.get('tabbed-browser', scope='window',
window=self._win_id).currentWidget() window=self._win_id).currentWidget()
log.statusbar.debug("Setting caret_mode - val {}, selection " log.statusbar.debug("Setting caret flag - val {}, selection "
"{}".format(val, tab.caret.selection_enabled)) "{}".format(val, tab.caret.selection_enabled))
if val: if val:
if tab.caret.selection_enabled: if tab.caret.selection_enabled:
self._set_mode_text("{} selection".format(mode.name)) self._set_mode_text("{} selection".format(mode.name))
self._caret_mode = CaretMode.selection self._color_flags.caret = ColorFlags.CaretMode.selection
else: else:
self._set_mode_text(mode.name) self._set_mode_text(mode.name)
self._caret_mode = CaretMode.on self._color_flags.caret = ColorFlags.CaretMode.on
else: else:
self._caret_mode = CaretMode.off self._color_flags.caret = ColorFlags.CaretMode.off
self.setStyleSheet(style.get_stylesheet(self.STYLESHEET)) self.setStyleSheet(style.get_stylesheet(self.STYLESHEET))
def _set_mode_text(self, mode): def _set_mode_text(self, mode):
@ -314,6 +311,14 @@ class StatusBar(QWidget):
self._page_fullscreen = on self._page_fullscreen = on
self.maybe_hide() self.maybe_hide()
@pyqtSlot(browsertab.AbstractTab)
def on_tab_changed(self, tab):
"""Notify sub-widgets when the tab has been changed."""
self.url.on_tab_changed(tab)
self.prog.on_tab_changed(tab)
self.percentage.on_tab_changed(tab)
assert tab.private == self._color_flags.private
def resizeEvent(self, e): def resizeEvent(self, e):
"""Extend resizeEvent of QWidget to emit a resized signal afterwards. """Extend resizeEvent of QWidget to emit a resized signal afterwards.

View File

@ -54,12 +54,12 @@ class Command(misc.MinimalLineEditMixin, misc.CommandLineEdit):
show_cmd = pyqtSignal() show_cmd = pyqtSignal()
hide_cmd = pyqtSignal() hide_cmd = pyqtSignal()
def __init__(self, win_id, parent=None): def __init__(self, *, win_id, private, parent=None):
misc.CommandLineEdit.__init__(self, parent) misc.CommandLineEdit.__init__(self, parent=parent)
misc.MinimalLineEditMixin.__init__(self) misc.MinimalLineEditMixin.__init__(self)
self._win_id = win_id self._win_id = win_id
if not private:
command_history = objreg.get('command-history') command_history = objreg.get('command-history')
self.history.handle_private_mode = True
self.history.history = command_history.data self.history.history = command_history.data
self.history.changed.connect(command_history.changed) self.history.changed.connect(command_history.changed)
self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Ignored) self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Ignored)

View File

@ -21,7 +21,6 @@
from PyQt5.QtCore import pyqtSlot from PyQt5.QtCore import pyqtSlot
from qutebrowser.browser import browsertab
from qutebrowser.mainwindow.statusbar import textbase from qutebrowser.mainwindow.statusbar import textbase
@ -51,7 +50,6 @@ class Percentage(textbase.TextBase):
else: else:
self.setText('[{:2}%]'.format(y)) self.setText('[{:2}%]'.format(y))
@pyqtSlot(browsertab.AbstractTab)
def on_tab_changed(self, tab): def on_tab_changed(self, tab):
"""Update scroll position when tab changed.""" """Update scroll position when tab changed."""
self.set_perc(*tab.scroller.pos_perc()) self.set_perc(*tab.scroller.pos_perc())

View File

@ -22,7 +22,6 @@
from PyQt5.QtCore import pyqtSlot, QSize from PyQt5.QtCore import pyqtSlot, QSize
from PyQt5.QtWidgets import QProgressBar, QSizePolicy from PyQt5.QtWidgets import QProgressBar, QSizePolicy
from qutebrowser.browser import browsertab
from qutebrowser.config import style from qutebrowser.config import style
from qutebrowser.utils import utils, usertypes from qutebrowser.utils import utils, usertypes
@ -60,7 +59,6 @@ class Progress(QProgressBar):
self.setValue(0) self.setValue(0)
self.show() self.show()
@pyqtSlot(browsertab.AbstractTab)
def on_tab_changed(self, tab): def on_tab_changed(self, tab):
"""Set the correct value when the current tab changed.""" """Set the correct value when the current tab changed."""
if self is None: # pragma: no branch if self is None: # pragma: no branch

View File

@ -21,7 +21,6 @@
from PyQt5.QtCore import pyqtSlot, pyqtProperty, Qt, QUrl from PyQt5.QtCore import pyqtSlot, pyqtProperty, Qt, QUrl
from qutebrowser.browser import browsertab
from qutebrowser.mainwindow.statusbar import textbase from qutebrowser.mainwindow.statusbar import textbase
from qutebrowser.config import style from qutebrowser.config import style
from qutebrowser.utils import usertypes, urlutils from qutebrowser.utils import usertypes, urlutils
@ -165,7 +164,6 @@ class UrlText(textbase.TextBase):
self._hover_url = None self._hover_url = None
self._update_url() self._update_url()
@pyqtSlot(browsertab.AbstractTab)
def on_tab_changed(self, tab): def on_tab_changed(self, tab):
"""Update URL if the tab changed.""" """Update URL if the tab changed."""
self._hover_url = None self._hover_url = None

View File

@ -68,6 +68,7 @@ class TabbedBrowser(tabwidget.TabWidget):
_local_marks: Jump markers local to each page _local_marks: Jump markers local to each page
_global_marks: Jump markers used across all pages _global_marks: Jump markers used across all pages
default_window_icon: The qutebrowser window icon default_window_icon: The qutebrowser window icon
private: Whether private browsing is on for this window.
Signals: Signals:
cur_progress: Progress of the current tab changed (load_progress). cur_progress: Progress of the current tab changed (load_progress).
@ -100,7 +101,7 @@ class TabbedBrowser(tabwidget.TabWidget):
new_tab = pyqtSignal(browsertab.AbstractTab, int) new_tab = pyqtSignal(browsertab.AbstractTab, int)
page_fullscreen_requested = pyqtSignal(bool) page_fullscreen_requested = pyqtSignal(bool)
def __init__(self, win_id, parent=None): def __init__(self, *, win_id, private, parent=None):
super().__init__(win_id, parent) super().__init__(win_id, parent)
self._win_id = win_id self._win_id = win_id
self._tab_insert_idx_left = 0 self._tab_insert_idx_left = 0
@ -118,6 +119,7 @@ class TabbedBrowser(tabwidget.TabWidget):
self._local_marks = {} self._local_marks = {}
self._global_marks = {} self._global_marks = {}
self.default_window_icon = self.window().windowIcon() self.default_window_icon = self.window().windowIcon()
self.private = private
objreg.get('config').changed.connect(self.update_favicons) objreg.get('config').changed.connect(self.update_favicons)
objreg.get('config').changed.connect(self.update_window_title) objreg.get('config').changed.connect(self.update_window_title)
objreg.get('config').changed.connect(self.update_tab_titles) objreg.get('config').changed.connect(self.update_tab_titles)
@ -205,7 +207,9 @@ class TabbedBrowser(tabwidget.TabWidget):
tab.renderer_process_terminated.connect( tab.renderer_process_terminated.connect(
functools.partial(self._on_renderer_process_terminated, tab)) functools.partial(self._on_renderer_process_terminated, tab))
tab.new_tab_requested.connect(self.tabopen) tab.new_tab_requested.connect(self.tabopen)
tab.add_history_item.connect(objreg.get('web-history').add_from_tab) if not self.private:
web_history = objreg.get('web-history')
tab.add_history_item.connect(web_history.add_from_tab)
tab.fullscreen_requested.connect(self.page_fullscreen_requested) tab.fullscreen_requested.connect(self.page_fullscreen_requested)
tab.fullscreen_requested.connect( tab.fullscreen_requested.connect(
self.tabBar().on_page_fullscreen_requested) self.tabBar().on_page_fullscreen_requested)
@ -399,13 +403,14 @@ class TabbedBrowser(tabwidget.TabWidget):
if (config.get('tabs', 'tabs-are-windows') and self.count() > 0 and if (config.get('tabs', 'tabs-are-windows') and self.count() > 0 and
not ignore_tabs_are_windows): not ignore_tabs_are_windows):
from qutebrowser.mainwindow import mainwindow from qutebrowser.mainwindow import mainwindow
window = mainwindow.MainWindow() window = mainwindow.MainWindow(private=self.private)
window.show() window.show()
tabbed_browser = objreg.get('tabbed-browser', scope='window', tabbed_browser = objreg.get('tabbed-browser', scope='window',
window=window.win_id) window=window.win_id)
return tabbed_browser.tabopen(url, background, explicit) return tabbed_browser.tabopen(url, background, explicit)
tab = browsertab.create(win_id=self._win_id, parent=self) tab = browsertab.create(win_id=self._win_id, private=self.private,
parent=self)
self._connect_tab_signals(tab) self._connect_tab_signals(tab)
if idx is None: if idx is None:

View File

@ -21,7 +21,6 @@
from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject
from qutebrowser.config import config
from qutebrowser.utils import usertypes, log from qutebrowser.utils import usertypes, log
@ -44,7 +43,6 @@ class History(QObject):
"""Command history. """Command history.
Attributes: Attributes:
handle_private_mode: Whether to ignore history in private mode.
history: A list of executed commands, with newer commands at the end. history: A list of executed commands, with newer commands at the end.
_tmphist: Temporary history for history browsing (as NeighborList) _tmphist: Temporary history for history browsing (as NeighborList)
@ -54,14 +52,13 @@ class History(QObject):
changed = pyqtSignal() changed = pyqtSignal()
def __init__(self, history=None, parent=None): def __init__(self, *, history=None, parent=None):
"""Constructor. """Constructor.
Args: Args:
history: The initial history to set. history: The initial history to set.
""" """
super().__init__(parent) super().__init__(parent)
self.handle_private_mode = False
self._tmphist = None self._tmphist = None
if history is None: if history is None:
self.history = [] self.history = []
@ -129,9 +126,6 @@ class History(QObject):
Args: Args:
text: The text to append. text: The text to append.
""" """
if (self.handle_private_mode and
config.get('general', 'private-browsing')):
return
if not self.history or text != self.history[-1]: if not self.history or text != self.history[-1]:
self.history.append(text) self.history.append(text)
self.changed.emit() self.changed.emit()

View File

@ -50,7 +50,7 @@ class ConsoleLineEdit(miscwidgets.CommandLineEdit):
Args: Args:
_namespace: The local namespace of the interpreter. _namespace: The local namespace of the interpreter.
""" """
super().__init__(parent) super().__init__(parent=parent)
self.update_font() self.update_font()
objreg.get('config').changed.connect(self.update_font) objreg.get('config').changed.connect(self.update_font)
self._history = cmdhistory.History(parent=self) self._history = cmdhistory.History(parent=self)

View File

@ -69,7 +69,7 @@ class CommandLineEdit(QLineEdit):
_promptlen: The length of the current prompt. _promptlen: The length of the current prompt.
""" """
def __init__(self, parent=None): def __init__(self, *, parent=None):
super().__init__(parent) super().__init__(parent)
self.history = cmdhistory.History(parent=self) self.history = cmdhistory.History(parent=self)
self._validator = _CommandValidator(self) self._validator = _CommandValidator(self)

View File

@ -213,7 +213,7 @@ class SessionManager(QObject):
data['history'].append(item_data) data['history'].append(item_data)
return data return data
def _save_all(self, *, only_window=None): def _save_all(self, *, only_window=None, with_private=False):
"""Get a dict with data for all windows/tabs.""" """Get a dict with data for all windows/tabs."""
data = {'windows': []} data = {'windows': []}
if only_window is not None: if only_window is not None:
@ -221,7 +221,7 @@ class SessionManager(QObject):
else: else:
winlist = objreg.window_registry winlist = objreg.window_registry
for win_id in 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', main_window = objreg.get('main-window', scope='window',
@ -231,12 +231,17 @@ class SessionManager(QObject):
if sip.isdeleted(main_window): if sip.isdeleted(main_window):
continue continue
if tabbed_browser.private and not with_private:
continue
win_data = {} win_data = {}
active_window = QApplication.instance().activeWindow() active_window = QApplication.instance().activeWindow()
if getattr(active_window, 'win_id', None) == win_id: if getattr(active_window, 'win_id', None) == win_id:
win_data['active'] = True win_data['active'] = True
win_data['geometry'] = bytes(main_window.saveGeometry()) win_data['geometry'] = bytes(main_window.saveGeometry())
win_data['tabs'] = [] win_data['tabs'] = []
if tabbed_browser.private:
win_data['private'] = True
for i, tab in enumerate(tabbed_browser.widgets()): for i, tab in enumerate(tabbed_browser.widgets()):
active = i == tabbed_browser.currentIndex() active = i == tabbed_browser.currentIndex()
win_data['tabs'].append(self._save_tab(tab, active)) win_data['tabs'].append(self._save_tab(tab, active))
@ -260,7 +265,7 @@ class SessionManager(QObject):
return name return name
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): only_window=None, with_private=False):
"""Save a named session. """Save a named session.
Args: Args:
@ -270,6 +275,7 @@ class SessionManager(QObject):
instead of the currently open state. instead of the currently open state.
load_next_time: If set, prepares this session to be load next time. load_next_time: If set, prepares this session to be load next time.
only_window: If set, only tabs in the specified window is saved. only_window: If set, only tabs in the specified window is saved.
with_private: Include private windows.
Return: Return:
The name of the saved session. The name of the saved session.
@ -284,7 +290,8 @@ class SessionManager(QObject):
log.sessions.error("last_window_session is None while saving!") log.sessions.error("last_window_session is None while saving!")
return return
else: else:
data = self._save_all(only_window=only_window) data = self._save_all(only_window=only_window,
with_private=with_private)
log.sessions.vdebug("Saving data: {}".format(data)) log.sessions.vdebug("Saving data: {}".format(data))
try: try:
with qtutils.savefile_open(path) as f: with qtutils.savefile_open(path) as f:
@ -379,7 +386,8 @@ class SessionManager(QObject):
raise SessionError(e) raise SessionError(e)
log.sessions.debug("Loading session {} from {}...".format(name, path)) log.sessions.debug("Loading session {} from {}...".format(name, path))
for win in data['windows']: for win in data['windows']:
window = mainwindow.MainWindow(geometry=win['geometry']) window = mainwindow.MainWindow(geometry=win['geometry'],
private=win.get('private', None))
window.show() window.show()
tabbed_browser = objreg.get('tabbed-browser', scope='window', tabbed_browser = objreg.get('tabbed-browser', scope='window',
window=window.win_id) window=window.win_id)
@ -443,8 +451,10 @@ class SessionManager(QObject):
@cmdutils.register(name=['session-save', 'w'], instance='session-manager') @cmdutils.register(name=['session-save', 'w'], instance='session-manager')
@cmdutils.argument('name', completion=usertypes.Completion.sessions) @cmdutils.argument('name', completion=usertypes.Completion.sessions)
@cmdutils.argument('win_id', win_id=True) @cmdutils.argument('win_id', win_id=True)
@cmdutils.argument('with_private', flag='p')
def session_save(self, name: str = default, current=False, quiet=False, def session_save(self, name: str = default, current=False, quiet=False,
force=False, only_active_window=False, win_id=None): force=False, only_active_window=False, with_private=False,
win_id=None):
"""Save a session. """Save a session.
Args: Args:
@ -454,6 +464,7 @@ class SessionManager(QObject):
quiet: Don't show confirmation message. quiet: Don't show confirmation message.
force: Force saving internal sessions (starting with an underline). force: Force saving internal sessions (starting with an underline).
only_active_window: Saves only tabs of the currently active window. only_active_window: Saves only tabs of the currently active window.
with_private: Include private windows.
""" """
if name is not default and name.startswith('_') and not force: if name is not default and name.startswith('_') and not force:
raise cmdexc.CommandError("{} is an internal session, use --force " raise cmdexc.CommandError("{} is an internal session, use --force "
@ -465,9 +476,10 @@ class SessionManager(QObject):
assert not name.startswith('_') assert not name.startswith('_')
try: try:
if only_active_window: if only_active_window:
name = self.save(name, only_window=win_id) name = self.save(name, only_window=win_id,
with_private=with_private)
else: else:
name = self.save(name) name = self.save(name, with_private=with_private)
except SessionError as e: except SessionError as e:
raise cmdexc.CommandError("Error while saving session: {}" raise cmdexc.CommandError("Error while saving session: {}"
.format(e)) .format(e))

View File

@ -54,9 +54,7 @@ def whitelist_generator():
attr) attr)
# PyQt properties # PyQt properties
for attr in ['prompt_active', 'command_active', 'insert_active', yield 'qutebrowser.mainwindow.statusbar.bar.StatusBar.color_flags'
'caret_mode']:
yield 'qutebrowser.mainwindow.statusbar.bar.StatusBar.' + attr
yield 'qutebrowser.mainwindow.statusbar.url.UrlText.urltype' yield 'qutebrowser.mainwindow.statusbar.url.UrlText.urltype'
# Not used yet, but soon (or when debugging) # Not used yet, but soon (or when debugging)

View File

@ -177,21 +177,25 @@ def pdfjs_available():
def open_path(quteproc, httpbin, path): def open_path(quteproc, httpbin, path):
"""Open a URL. """Open a URL.
If used like "When I open ... in a new tab", the URL is opened in a new - If used like "When I open ... in a new tab", the URL is opened in a new
tab. With "... in a new window", it's opened in a new window. With tab.
"... as a URL", it's opened according to new-instance-open-target. - With "... in a new window", it's opened in a new window.
- With "... in a private window" it's opened in a new private window.
- With "... as a URL", it's opened according to new-instance-open-target.
""" """
path = path.replace('(port)', str(httpbin.port)) path = path.replace('(port)', str(httpbin.port))
new_tab = False new_tab = False
new_bg_tab = False new_bg_tab = False
new_window = False new_window = False
private = False
as_url = False as_url = False
wait = True wait = True
new_tab_suffix = ' in a new tab' new_tab_suffix = ' in a new tab'
new_bg_tab_suffix = ' in a new background tab' new_bg_tab_suffix = ' in a new background tab'
new_window_suffix = ' in a new window' new_window_suffix = ' in a new window'
private_suffix = ' in a private window'
do_not_wait_suffix = ' without waiting' do_not_wait_suffix = ' without waiting'
as_url_suffix = ' as a URL' as_url_suffix = ' as a URL'
@ -205,6 +209,9 @@ def open_path(quteproc, httpbin, path):
elif path.endswith(new_window_suffix): elif path.endswith(new_window_suffix):
path = path[:-len(new_window_suffix)] path = path[:-len(new_window_suffix)]
new_window = True new_window = True
elif path.endswith(private_suffix):
path = path[:-len(private_suffix)]
private = True
elif path.endswith(as_url_suffix): elif path.endswith(as_url_suffix):
path = path[:-len(as_url_suffix)] path = path[:-len(as_url_suffix)]
as_url = True as_url = True
@ -215,7 +222,8 @@ def open_path(quteproc, httpbin, path):
break break
quteproc.open_path(path, new_tab=new_tab, new_bg_tab=new_bg_tab, quteproc.open_path(path, new_tab=new_tab, new_bg_tab=new_bg_tab,
new_window=new_window, as_url=as_url, wait=wait) new_window=new_window, private=private, as_url=as_url,
wait=wait)
@bdd.when(bdd.parsers.parse("I set {sect} -> {opt} to {value}")) @bdd.when(bdd.parsers.parse("I set {sect} -> {opt} to {value}"))

View File

@ -569,26 +569,6 @@ Feature: Various utility commands.
And I run :command-accept And I run :command-accept
Then the message "Hello World" should be shown Then the message "Hello World" should be shown
## https://github.com/qutebrowser/qutebrowser/issues/1219
@qtwebengine_todo: private browsing is not implemented yet @qtwebkit_ng_skip: private browsing is not implemented yet
Scenario: Sharing cookies with private browsing
When I set general -> private-browsing to true
And I open cookies/set?qute-test=42 without waiting
And I wait until cookies is loaded
And I open cookies in a new tab
And I set general -> private-browsing to false
Then the cookie qute-test should be set to 42
## https://github.com/qutebrowser/qutebrowser/issues/1742
@qtwebengine_todo: private browsing is not implemented yet @qtwebkit_ng_xfail: private browsing is not implemented yet
Scenario: Private browsing is activated in QtWebKit without restart
When I set general -> private-browsing to true
And I open data/javascript/localstorage.html
And I set general -> private-browsing to false
Then the page should contain the plaintext "Local storage status: not working"
@no_xvfb @no_xvfb
Scenario: :window-only Scenario: :window-only
Given I run :tab-only Given I run :tab-only
@ -681,20 +661,6 @@ Feature: Various utility commands.
And I run :command-accept And I run :command-accept
Then the error "No command given" should be shown Then the error "No command given" should be shown
@qtwebengine_todo: private browsing is not implemented yet @qtwebkit_ng_skip: private browsing is not implemented yet
Scenario: Calling previous command with private-browsing mode
When I run :set-cmd-text :message-info blah
And I run :command-accept
And I set general -> private-browsing to true
And I run :set-cmd-text :message-error "This should only be shown once"
And I run :command-accept
And I wait for the error "This should only be shown once"
And I run :set-cmd-text :
And I run :command-history-prev
And I run :command-accept
And I set general -> private-browsing to false
Then the message "blah" should be shown
## Modes blacklisted for :enter-mode ## Modes blacklisted for :enter-mode
Scenario: Trying to enter command mode with :enter-mode Scenario: Trying to enter command mode with :enter-mode

View File

@ -33,7 +33,7 @@ Feature: Opening pages
Scenario: :open with -t and -b Scenario: :open with -t and -b
When I run :open -t -b foo.bar When I run :open -t -b foo.bar
Then the error "Only one of -t/-b/-w can be given!" should be shown Then the error "Only one of -t/-b/-w/-p can be given!" should be shown
Scenario: Searching with :open Scenario: Searching with :open
When I set general -> auto-search to naive When I set general -> auto-search to naive

View File

@ -0,0 +1,118 @@
# vim: ft=cucumber fileencoding=utf-8 sts=4 sw=4 et:
Feature: Using private browsing
Background:
Given I open about:blank
And I clean up open tabs
@qtwebkit_ng_xfail: private browsing is not implemented yet
Scenario: Opening new tab in private window
When I open about:blank in a private window
And I run :window-only
And I open data/javascript/localstorage.html in a new tab
Then the page should contain the plaintext "Local storage status: not working"
@qtwebkit_ng_xfail: private browsing is not implemented yet
Scenario: Opening new tab in private window with :navigate next
When I open data/navigate in a private window
And I run :window-only
And I run :navigate -t next
And I wait until data/navigate/next.html is loaded
And I open data/javascript/localstorage.html
Then the page should contain the plaintext "Local storage status: not working"
Scenario: Using command history in a new private browsing window
When I run :set-cmd-text :message-info "Hello World"
And I run :command-accept
And I open about:blank in a private window
And I run :set-cmd-text :message-error "This should only be shown once"
And I run :command-accept
And I wait for the error "This should only be shown once"
And I run :close
And I run :set-cmd-text :
And I run :command-history-prev
And I run :command-accept
# Then the error should not be shown again
## https://github.com/qutebrowser/qutebrowser/issues/1219
@qtwebkit_ng_skip: private browsing is not implemented yet
Scenario: Sharing cookies with private browsing
When I open cookies/set?qute-test=42 without waiting in a private window
And I wait until cookies is loaded
And I open cookies in a new tab
And I set general -> private-browsing to false
Then the cookie qute-test should be set to 42
Scenario: Opening private window with :navigate increment
# Private window handled in commands.py
When I open data/numbers/1.txt in a private window
And I run :window-only
And I run :navigate -w increment
And I wait until data/numbers/2.txt is loaded
Then the session should look like:
windows:
- private: True
tabs:
- history:
- url: http://localhost:*/data/numbers/1.txt
- private: True
tabs:
- history:
- url: http://localhost:*/data/numbers/2.txt
Scenario: Opening private window with :navigate next
# Private window handled in navigate.py
When I open data/navigate in a private window
And I run :window-only
And I run :navigate -w next
And I wait until data/navigate/next.html is loaded
Then the session should look like:
windows:
- private: True
tabs:
- history:
- url: http://localhost:*/data/navigate
- private: True
tabs:
- history:
- url: http://localhost:*/data/navigate/next.html
Scenario: Opening private window with :tab-clone
When I open data/hello.txt in a private window
And I run :window-only
And I run :tab-clone -w
And I wait until data/hello.txt is loaded
Then the session should look like:
windows:
- private: True
tabs:
- history:
- url: http://localhost:*/data/hello.txt
- private: True
tabs:
- history:
- url: http://localhost:*/data/hello.txt
Scenario: Opening private window via :click-element
When I open data/click_element.html in a private window
And I run :window-only
And I run :click-element --target window id link
And I wait until data/hello.txt is loaded
Then the session should look like:
windows:
- private: True
tabs:
- history:
- url: http://localhost:*/data/click_element.html
- private: True
tabs:
- history:
- url: http://localhost:*/data/hello.txt
Scenario: Skipping private window when saving session
When I open data/hello.txt in a private window
And I run :session-save (tmpdir)/session.yml
And I wait for "Saved session */session.yml." in the log
Then the file session.yml should not contain "hello.txt"

View File

@ -18,7 +18,6 @@
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. # along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
import sys import sys
import json
import os.path import os.path
import subprocess import subprocess
@ -57,18 +56,6 @@ def update_documentation():
subprocess.call([sys.executable, update_script]) subprocess.call([sys.executable, update_script])
@bdd.then(bdd.parsers.parse('the cookie {name} should be set to {value}'))
def check_cookie(quteproc, name, value):
"""Check if a given cookie is set correctly.
This assumes we're on the httpbin cookies page.
"""
content = quteproc.get_content()
data = json.loads(content)
print(data)
assert data['cookies'][name] == value
@bdd.then(bdd.parsers.parse('the PDF {filename} should exist in the tmpdir')) @bdd.then(bdd.parsers.parse('the PDF {filename} should exist in the tmpdir'))
def pdf_exists(quteproc, tmpdir, filename): def pdf_exists(quteproc, tmpdir, filename):
path = tmpdir / filename path = tmpdir / filename

View File

@ -0,0 +1,41 @@
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
# Copyright 2017 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
#
# This file is part of qutebrowser.
#
# qutebrowser is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# qutebrowser is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
import json
import pytest_bdd as bdd
bdd.scenarios('private.feature')
@bdd.then(bdd.parsers.parse('the cookie {name} should be set to {value}'))
def check_cookie(quteproc, name, value):
"""Check if a given cookie is set correctly.
This assumes we're on the httpbin cookies page.
"""
content = quteproc.get_content()
data = json.loads(content)
print(data)
assert data['cookies'][name] == value
@bdd.then(bdd.parsers.parse('the file {name} should not contain "{text}"'))
def check_not_contain(tmpdir, name, text):
path = tmpdir / name
assert text not in path.read()

View File

@ -76,7 +76,7 @@ Feature: quickmarks and bookmarks
Scenario: Loading a bookmark with -t and -b Scenario: Loading a bookmark with -t and -b
When I run :bookmark-load -t -b about:blank When I run :bookmark-load -t -b about:blank
Then the error "Only one of -t/-b/-w can be given!" should be shown Then the error "Only one of -t/-b/-w/-p can be given!" should be shown
Scenario: Deleting a bookmark which does not exist Scenario: Deleting a bookmark which does not exist
When I run :bookmark-del doesnotexist When I run :bookmark-del doesnotexist
@ -200,7 +200,7 @@ Feature: quickmarks and bookmarks
Scenario: Loading a quickmark with -t and -b Scenario: Loading a quickmark with -t and -b
When I run :quickmark-add http://localhost:(port)/data/numbers/17.txt seventeen When I run :quickmark-add http://localhost:(port)/data/numbers/17.txt seventeen
When I run :quickmark-load -t -b seventeen When I run :quickmark-load -t -b seventeen
Then the error "Only one of -t/-b/-w can be given!" should be shown Then the error "Only one of -t/-b/-w/-p can be given!" should be shown
Scenario: Deleting a quickmark which does not exist Scenario: Deleting a quickmark which does not exist
When I run :quickmark-del doesnotexist When I run :quickmark-del doesnotexist

View File

@ -498,18 +498,20 @@ class QuteProc(testprocess.Process):
self.set_setting(sect, opt, old_value) self.set_setting(sect, opt, old_value)
def open_path(self, path, *, new_tab=False, new_bg_tab=False, def open_path(self, path, *, new_tab=False, new_bg_tab=False,
new_window=False, as_url=False, port=None, https=False, new_window=False, private=False, as_url=False, port=None,
wait=True): https=False, wait=True):
"""Open the given path on the local webserver in qutebrowser.""" """Open the given path on the local webserver in qutebrowser."""
url = self.path_to_url(path, port=port, https=https) url = self.path_to_url(path, port=port, https=https)
self.open_url(url, new_tab=new_tab, new_bg_tab=new_bg_tab, self.open_url(url, new_tab=new_tab, new_bg_tab=new_bg_tab,
new_window=new_window, as_url=as_url, wait=wait) new_window=new_window, private=private, as_url=as_url,
wait=wait)
def open_url(self, url, *, new_tab=False, new_bg_tab=False, def open_url(self, url, *, new_tab=False, new_bg_tab=False,
new_window=False, as_url=False, wait=True): new_window=False, private=False, as_url=False, wait=True):
"""Open the given url in qutebrowser.""" """Open the given url in qutebrowser."""
if new_tab and new_window: if sum(1 for opt in [new_tab, new_bg_tab, new_window, private, as_url]
raise ValueError("new_tab and new_window given!") if opt) > 1:
raise ValueError("Conflicting options given!")
if as_url: if as_url:
self.send_cmd(url, invalid=True) self.send_cmd(url, invalid=True)
@ -519,6 +521,8 @@ class QuteProc(testprocess.Process):
self.send_cmd(':open -b ' + url) self.send_cmd(':open -b ' + url)
elif new_window: elif new_window:
self.send_cmd(':open -w ' + url) self.send_cmd(':open -w ' + url)
elif private:
self.send_cmd(':open -p ' + url)
else: else:
self.send_cmd(':open ' + url) self.send_cmd(':open ' + url)
@ -577,7 +581,7 @@ class QuteProc(testprocess.Process):
"""Save the session and get the parsed session data.""" """Save the session and get the parsed session data."""
with tempfile.TemporaryDirectory() as tmpdir: with tempfile.TemporaryDirectory() as tmpdir:
session = os.path.join(tmpdir, 'session.yml') session = os.path.join(tmpdir, 'session.yml')
self.send_cmd(':session-save "{}"'.format(session)) self.send_cmd(':session-save --with-private "{}"'.format(session))
self.wait_for(category='message', loglevel=logging.INFO, self.wait_for(category='message', loglevel=logging.INFO,
message='Saved session {}.'.format(session)) message='Saved session {}.'.format(session))
with open(session, encoding='utf-8') as f: with open(session, encoding='utf-8') as f:

View File

@ -78,8 +78,8 @@ class Line:
def _render_log(data, threshold=100): def _render_log(data, threshold=100):
"""Shorten the given log without -v and convert to a string.""" """Shorten the given log without -v and convert to a string."""
data = [str(d) for d in data] data = [str(d) for d in data]
is_exception = any('Traceback (most recent call last):' in line is_exception = any('Traceback (most recent call last):' in line or
for line in data) 'Uncaught exception' in line for line in data)
verbose = pytest.config.getoption('--verbose') verbose = pytest.config.getoption('--verbose')
if len(data) > threshold and not verbose and not is_exception: if len(data) > threshold and not verbose and not is_exception:
msg = '[{} lines suppressed, use -v to show]'.format( msg = '[{} lines suppressed, use -v to show]'.format(

View File

@ -267,3 +267,21 @@ def test_launching_with_python2():
assert proc.returncode == 1 assert proc.returncode == 1
error = "At least Python 3.4 is required to run qutebrowser" error = "At least Python 3.4 is required to run qutebrowser"
assert stderr.decode('ascii').startswith(error) assert stderr.decode('ascii').startswith(error)
def test_initial_private_browsing(request, quteproc_new):
"""Make sure the initial window is private when the setting is set."""
args = (_base_args(request.config) +
['--temp-basedir', '-s', 'general', 'private-browsing', 'true'])
quteproc_new.start(args)
quteproc_new.compare_session("""
windows:
- private: True
tabs:
- history:
- url: about:blank
""")
quteproc_new.send_cmd(':quit')
quteproc_new.wait_for_quit()

View File

@ -230,7 +230,7 @@ class FakeWebTab(browsertab.AbstractTab):
scroll_pos_perc=(0, 0), scroll_pos_perc=(0, 0),
load_status=usertypes.LoadStatus.success, load_status=usertypes.LoadStatus.success,
progress=0): progress=0):
super().__init__(win_id=0, mode_manager=None) super().__init__(win_id=0, mode_manager=None, private=False)
self._load_status = load_status self._load_status = load_status
self._title = title self._title = title
self._url = url self._url = url

View File

@ -22,20 +22,11 @@ import pytest
from qutebrowser.browser.webkit.network import networkmanager from qutebrowser.browser.webkit.network import networkmanager
from qutebrowser.browser.webkit import cookies from qutebrowser.browser.webkit import cookies
pytestmark = pytest.mark.usefixtures('cookiejar_and_cache') pytestmark = pytest.mark.usefixtures('cookiejar_and_cache')
class TestPrivateMode: def test_init_with_private_mode(config_stub):
nam = networkmanager.NetworkManager(win_id=0, tab_id=0, private=True)
def test_init_with_private_mode(self, config_stub):
config_stub.data = {'general': {'private-browsing': True}}
nam = networkmanager.NetworkManager(0, 0)
assert isinstance(nam.cookieJar(), cookies.RAMCookieJar)
def test_setting_private_mode_later(self, config_stub):
config_stub.data = {'general': {'private-browsing': False}}
nam = networkmanager.NetworkManager(0, 0)
assert not isinstance(nam.cookieJar(), cookies.RAMCookieJar)
config_stub.data = {'general': {'private-browsing': True}}
nam.on_config_changed()
assert isinstance(nam.cookieJar(), cookies.RAMCookieJar) assert isinstance(nam.cookieJar(), cookies.RAMCookieJar)
assert nam.cache() is None

View File

@ -54,41 +54,6 @@ def test_cache_config_change_cache_size(config_stub, tmpdir):
assert disk_cache.maximumCacheSize() == max_cache_size * 2 assert disk_cache.maximumCacheSize() == max_cache_size * 2
def test_cache_config_enable_private_browsing(config_stub, tmpdir):
"""Change private-browsing config to True and emit signal."""
config_stub.data = {
'storage': {'cache-size': 1024},
'general': {'private-browsing': False}
}
disk_cache = cache.DiskCache(str(tmpdir))
assert disk_cache.cacheSize() == 0
preload_cache(disk_cache)
assert disk_cache.cacheSize() > 0
config_stub.set('general', 'private-browsing', True)
assert disk_cache.cacheSize() == 0
def test_cache_config_disable_private_browsing(config_stub, tmpdir):
"""Change private-browsing config to False and emit signal."""
config_stub.data = {
'storage': {'cache-size': 1024},
'general': {'private-browsing': True}
}
url = 'http://qutebrowser.org'
metadata = QNetworkCacheMetaData()
metadata.setUrl(QUrl(url))
assert metadata.isValid()
disk_cache = cache.DiskCache(str(tmpdir))
assert disk_cache.prepare(metadata) is None
config_stub.set('general', 'private-browsing', False)
content = b'cute'
preload_cache(disk_cache, url, content)
assert disk_cache.data(QUrl(url)).readAll() == content
def test_cache_size_leq_max_cache_size(config_stub, tmpdir): def test_cache_size_leq_max_cache_size(config_stub, tmpdir):
"""Test cacheSize <= MaximumCacheSize when cache is activated.""" """Test cacheSize <= MaximumCacheSize when cache is activated."""
limit = 100 limit = 100
@ -108,16 +73,6 @@ def test_cache_size_leq_max_cache_size(config_stub, tmpdir):
assert disk_cache.cacheSize() < limit + 100 assert disk_cache.cacheSize() < limit + 100
def test_cache_size_deactivated(config_stub, tmpdir):
"""Confirm that the cache size returns 0 when deactivated."""
config_stub.data = {
'storage': {'cache-size': 1024},
'general': {'private-browsing': True}
}
disk_cache = cache.DiskCache(str(tmpdir))
assert disk_cache.cacheSize() == 0
def test_cache_existing_metadata_file(config_stub, tmpdir): def test_cache_existing_metadata_file(config_stub, tmpdir):
"""Test querying existing meta data file from activated cache.""" """Test querying existing meta data file from activated cache."""
config_stub.data = { config_stub.data = {
@ -155,42 +110,6 @@ def test_cache_nonexistent_metadata_file(config_stub, tmpdir):
assert not cache_file.isValid() assert not cache_file.isValid()
def test_cache_deactivated_metadata_file(config_stub, tmpdir):
"""Test querying meta data file when cache is deactivated."""
config_stub.data = {
'storage': {'cache-size': 1024},
'general': {'private-browsing': True}
}
disk_cache = cache.DiskCache(str(tmpdir))
assert disk_cache.fileMetaData("foo") == QNetworkCacheMetaData()
def test_cache_deactivated_private_browsing(config_stub, tmpdir):
"""Test if cache is deactivated in private-browsing mode."""
config_stub.data = {
'storage': {'cache-size': 1024},
'general': {'private-browsing': True}
}
disk_cache = cache.DiskCache(str(tmpdir))
metadata = QNetworkCacheMetaData()
metadata.setUrl(QUrl('http://www.example.com/'))
assert metadata.isValid()
assert disk_cache.prepare(metadata) is None
def test_cache_deactivated_get_data(config_stub, tmpdir):
"""Query some data from a deactivated cache."""
config_stub.data = {
'storage': {'cache-size': 1024},
'general': {'private-browsing': True}
}
disk_cache = cache.DiskCache(str(tmpdir))
url = QUrl('http://www.example.com/')
assert disk_cache.data(url) is None
def test_cache_get_nonexistent_data(config_stub, tmpdir): def test_cache_get_nonexistent_data(config_stub, tmpdir):
"""Test querying some data that was never inserted.""" """Test querying some data that was never inserted."""
config_stub.data = { config_stub.data = {
@ -203,18 +122,6 @@ def test_cache_get_nonexistent_data(config_stub, tmpdir):
assert disk_cache.data(QUrl('http://qutebrowser.org')) is None assert disk_cache.data(QUrl('http://qutebrowser.org')) is None
def test_cache_deactivated_remove_data(config_stub, tmpdir):
"""Test removing some data from a deactivated cache."""
config_stub.data = {
'storage': {'cache-size': 1024},
'general': {'private-browsing': True}
}
disk_cache = cache.DiskCache(str(tmpdir))
url = QUrl('http://www.example.com/')
assert not disk_cache.remove(url)
def test_cache_insert_data(config_stub, tmpdir): def test_cache_insert_data(config_stub, tmpdir):
"""Test if entries inserted into the cache are actually there.""" """Test if entries inserted into the cache are actually there."""
config_stub.data = { config_stub.data = {
@ -232,28 +139,6 @@ def test_cache_insert_data(config_stub, tmpdir):
assert disk_cache.data(QUrl(url)).readAll() == content assert disk_cache.data(QUrl(url)).readAll() == content
def test_cache_deactivated_insert_data(config_stub, tmpdir):
"""Insert data when cache is deactivated."""
# First create QNetworkDiskCache just to get a valid QIODevice from it
url = 'http://qutebrowser.org'
disk_cache = QNetworkDiskCache()
disk_cache.setCacheDirectory(str(tmpdir))
metadata = QNetworkCacheMetaData()
metadata.setUrl(QUrl(url))
device = disk_cache.prepare(metadata)
assert device is not None
# Now create a deactivated DiskCache and insert the valid device created
# above (there probably is a better way to get a valid QIODevice...)
config_stub.data = {
'storage': {'cache-size': 1024},
'general': {'private-browsing': True}
}
deactivated_cache = cache.DiskCache(str(tmpdir))
assert deactivated_cache.insert(device) is None
def test_cache_remove_data(config_stub, tmpdir): def test_cache_remove_data(config_stub, tmpdir):
"""Test if a previously inserted entry can be removed from the cache.""" """Test if a previously inserted entry can be removed from the cache."""
config_stub.data = { config_stub.data = {
@ -285,16 +170,6 @@ def test_cache_clear_activated(config_stub, tmpdir):
assert disk_cache.cacheSize() == 0 assert disk_cache.cacheSize() == 0
def test_cache_clear_deactivated(config_stub, tmpdir):
"""Test method clear() on deactivated cache."""
config_stub.data = {
'storage': {'cache-size': 1024},
'general': {'private-browsing': True}
}
disk_cache = cache.DiskCache(str(tmpdir))
assert disk_cache.clear() is None
def test_cache_metadata(config_stub, tmpdir): def test_cache_metadata(config_stub, tmpdir):
"""Ensure that DiskCache.metaData() returns exactly what was inserted.""" """Ensure that DiskCache.metaData() returns exactly what was inserted."""
config_stub.data = { config_stub.data = {
@ -313,18 +188,6 @@ def test_cache_metadata(config_stub, tmpdir):
assert disk_cache.metaData(QUrl(url)) == metadata assert disk_cache.metaData(QUrl(url)) == metadata
def test_cache_deactivated_metadata(config_stub, tmpdir):
"""Test querying metaData() on not activated cache."""
config_stub.data = {
'storage': {'cache-size': 1024},
'general': {'private-browsing': True}
}
url = 'http://qutebrowser.org'
disk_cache = cache.DiskCache(str(tmpdir))
assert disk_cache.metaData(QUrl(url)) == QNetworkCacheMetaData()
def test_cache_update_metadata(config_stub, tmpdir): def test_cache_update_metadata(config_stub, tmpdir):
"""Test updating the meta data for an existing cache entry.""" """Test updating the meta data for an existing cache entry."""
config_stub.data = { config_stub.data = {
@ -343,21 +206,6 @@ def test_cache_update_metadata(config_stub, tmpdir):
assert disk_cache.metaData(QUrl(url)) == metadata assert disk_cache.metaData(QUrl(url)) == metadata
def test_cache_deactivated_update_metadata(config_stub, tmpdir):
"""Test updating the meta data when cache is not activated."""
config_stub.data = {
'storage': {'cache-size': 1024},
'general': {'private-browsing': True}
}
url = 'http://qutebrowser.org'
disk_cache = cache.DiskCache(str(tmpdir))
metadata = QNetworkCacheMetaData()
metadata.setUrl(QUrl(url))
assert metadata.isValid()
assert disk_cache.updateMetaData(metadata) is None
def test_cache_full(config_stub, tmpdir): def test_cache_full(config_stub, tmpdir):
"""Do a sanity test involving everything.""" """Do a sanity test involving everything."""
config_stub.data = { config_stub.data = {
@ -385,3 +233,10 @@ def test_cache_full(config_stub, tmpdir):
assert disk_cache.metaData(QUrl(url)).lastModified() == soon assert disk_cache.metaData(QUrl(url)).lastModified() == soon
assert disk_cache.data(QUrl(url)).readAll() == content assert disk_cache.data(QUrl(url)).readAll() == content
def test_private_browsing(config_stub, tmpdir):
"""Make sure the cache asserts with private browsing."""
config_stub.data = {'general': {'private-browsing': True}}
with pytest.raises(AssertionError):
cache.DiskCache(str(tmpdir))

View File

@ -38,18 +38,13 @@ class FakeWebHistory:
self.history_dict = history_dict self.history_dict = history_dict
@pytest.fixture(autouse=True)
def prerequisites(config_stub, fake_save_manager):
"""Make sure everything is ready to initialize a WebHistory."""
config_stub.data = {'general': {'private-browsing': False}}
@pytest.fixture() @pytest.fixture()
def hist(tmpdir): def hist(tmpdir, fake_save_manager):
return history.WebHistory(hist_dir=str(tmpdir), hist_name='history') return history.WebHistory(hist_dir=str(tmpdir), hist_name='history')
def test_async_read_twice(monkeypatch, qtbot, tmpdir, caplog): def test_async_read_twice(monkeypatch, qtbot, tmpdir, caplog,
fake_save_manager):
(tmpdir / 'filled-history').write('\n'.join([ (tmpdir / 'filled-history').write('\n'.join([
'12345 http://example.com/ title', '12345 http://example.com/ title',
'67890 http://example.com/', '67890 http://example.com/',
@ -88,34 +83,6 @@ def test_adding_item_during_async_read(qtbot, hist, redirect):
assert list(hist.history_dict.values()) == [expected] assert list(hist.history_dict.values()) == [expected]
def test_private_browsing(qtbot, tmpdir, fake_save_manager, config_stub):
"""Make sure no data is saved at all with private browsing."""
config_stub.data = {'general': {'private-browsing': True}}
private_hist = history.WebHistory(hist_dir=str(tmpdir),
hist_name='history')
# Before initial read
with qtbot.assertNotEmitted(private_hist.add_completion_item), \
qtbot.assertNotEmitted(private_hist.item_added):
private_hist.add_url(QUrl('http://www.example.com/'))
assert not private_hist._temp_history
# read
with qtbot.assertNotEmitted(private_hist.add_completion_item), \
qtbot.assertNotEmitted(private_hist.item_added):
with qtbot.waitSignals([private_hist.async_read_done], order='strict'):
list(private_hist.async_read())
# after read
with qtbot.assertNotEmitted(private_hist.add_completion_item), \
qtbot.assertNotEmitted(private_hist.item_added):
private_hist.add_url(QUrl('http://www.example.com/'))
assert not private_hist._temp_history
assert not private_hist._new_history
assert not private_hist.history_dict
def test_iter(hist): def test_iter(hist):
list(hist.async_read()) list(hist.async_read())
@ -257,7 +224,7 @@ def test_add_item_redirect(qtbot, hist):
assert hist.history_dict[url] == entry assert hist.history_dict[url] == entry
def test_add_item_redirect_update(qtbot, tmpdir): def test_add_item_redirect_update(qtbot, tmpdir, fake_save_manager):
"""A redirect update added should override a non-redirect one.""" """A redirect update added should override a non-redirect one."""
url = 'http://www.example.com/' url = 'http://www.example.com/'

View File

@ -146,7 +146,7 @@ def test_previtem_index_error(hist):
def test_append_private_mode(hist, config_stub): def test_append_private_mode(hist, config_stub):
"""Test append in private mode.""" """Test append in private mode."""
hist.handle_private_mode = True hist._private = True
# We want general.private-browsing set to True # We want general.private-browsing set to True
config_stub.data = CONFIG_PRIVATE config_stub.data = CONFIG_PRIVATE
hist.append('new item') hist.append('new item')

View File

@ -34,7 +34,7 @@ class TestCommandLineEdit:
@pytest.fixture @pytest.fixture
def cmd_edit(self, qtbot): def cmd_edit(self, qtbot):
"""Fixture to initialize a CommandLineEdit.""" """Fixture to initialize a CommandLineEdit."""
cmd_edit = miscwidgets.CommandLineEdit(None) cmd_edit = miscwidgets.CommandLineEdit()
cmd_edit.set_prompt(':') cmd_edit.set_prompt(':')
qtbot.add_widget(cmd_edit) qtbot.add_widget(cmd_edit)
assert cmd_edit.text() == '' assert cmd_edit.text() == ''