Move private tab API into an own object

This commit is contained in:
Florian Bruhin 2018-11-28 17:59:27 +01:00
parent d60dff2623
commit 5f5f202098
8 changed files with 127 additions and 98 deletions

View File

@ -734,6 +734,67 @@ class AbstractAudio(QObject):
raise NotImplementedError raise NotImplementedError
class AbstractTabPrivate:
"""Tab-related methods which are only needed in the core.
Those methods are not part of the API which is exposed to extensions, and
should ideally be removed at some point in the future.
"""
def __init__(self, mode_manager: modeman.ModeManager,
tab: 'AbstractTab') -> None:
self._widget = None # type: typing.Optional[QWidget]
self._tab = tab
self._mode_manager = mode_manager
def event_target(self) -> QWidget:
"""Return the widget events should be sent to."""
raise NotImplementedError
def handle_auto_insert_mode(self, ok: bool) -> None:
"""Handle `input.insert_mode.auto_load` after loading finished."""
if not config.val.input.insert_mode.auto_load or not ok:
return
cur_mode = self._mode_manager.mode
if cur_mode == usertypes.KeyMode.insert:
return
def _auto_insert_mode_cb(elem: webelem.AbstractWebElement) -> None:
"""Called from JS after finding the focused element."""
if elem is None:
log.webview.debug("No focused element!")
return
if elem.is_editable():
modeman.enter(self._tab.win_id, usertypes.KeyMode.insert,
'load finished', only_if_normal=True)
self._tab.elements.find_focused(_auto_insert_mode_cb)
def clear_ssl_errors(self) -> None:
raise NotImplementedError
def networkaccessmanager(self) -> typing.Optional[QNetworkAccessManager]:
"""Get the QNetworkAccessManager for this tab.
This is only implemented for QtWebKit.
For QtWebEngine, always returns None.
"""
raise NotImplementedError
def user_agent(self) -> typing.Optional[str]:
"""Get the user agent for this tab.
This is only implemented for QtWebKit.
For QtWebEngine, always returns None.
"""
raise NotImplementedError
def shutdown(self) -> None:
raise NotImplementedError
class AbstractTab(QWidget): class AbstractTab(QWidget):
"""A wrapper over the given widget to hide its API and expose another one. """A wrapper over the given widget to hide its API and expose another one.
@ -785,10 +846,7 @@ class AbstractTab(QWidget):
renderer_process_terminated = pyqtSignal(TerminationStatus, int) renderer_process_terminated = pyqtSignal(TerminationStatus, int)
predicted_navigation = pyqtSignal(QUrl) predicted_navigation = pyqtSignal(QUrl)
def __init__(self, *, def __init__(self, *, win_id: int, private: bool,
win_id: int,
mode_manager: modeman.ModeManager,
private: bool,
parent: QWidget = None) -> None: parent: QWidget = None) -> None:
self.is_private = private self.is_private = private
self.win_id = win_id self.win_id = win_id
@ -806,7 +864,6 @@ class AbstractTab(QWidget):
self._widget = None # type: typing.Optional[QWidget] self._widget = None # type: typing.Optional[QWidget]
self._progress = 0 self._progress = 0
self._has_ssl_errors = False self._has_ssl_errors = False
self._mode_manager = mode_manager
self._load_status = usertypes.LoadStatus.none self._load_status = usertypes.LoadStatus.none
self._mouse_event_filter = mouse.MouseEventFilter( self._mouse_event_filter = mouse.MouseEventFilter(
self, parent=self) self, parent=self)
@ -833,6 +890,7 @@ class AbstractTab(QWidget):
self.action._widget = widget self.action._widget = widget
self.elements._widget = widget self.elements._widget = widget
self.audio._widget = widget self.audio._widget = widget
self.private_api._widget = widget
self.settings._settings = widget.settings() self.settings._settings = widget.settings()
self._install_event_filter() self._install_event_filter()
@ -849,10 +907,6 @@ class AbstractTab(QWidget):
self._load_status = val self._load_status = val
self.load_status_changed.emit(val.name) self.load_status_changed.emit(val.name)
def event_target(self) -> QWidget:
"""Return the widget events should be sent to."""
raise NotImplementedError
def send_event(self, evt: QEvent) -> None: def send_event(self, evt: QEvent) -> None:
"""Send the given event to the underlying widget. """Send the given event to the underlying widget.
@ -865,7 +919,7 @@ class AbstractTab(QWidget):
raise utils.Unreachable("Can't re-use an event which was already " raise utils.Unreachable("Can't re-use an event which was already "
"posted!") "posted!")
recipient = self.event_target() recipient = self.private_api.event_target()
if recipient is None: if recipient is None:
# https://github.com/qutebrowser/qutebrowser/issues/3888 # https://github.com/qutebrowser/qutebrowser/issues/3888
log.webview.warning("Unable to find event target!") log.webview.warning("Unable to find event target!")
@ -925,26 +979,6 @@ class AbstractTab(QWidget):
navigation.url.errorString())) navigation.url.errorString()))
navigation.accepted = False navigation.accepted = False
def handle_auto_insert_mode(self, ok: bool) -> None:
"""Handle `input.insert_mode.auto_load` after loading finished."""
if not config.val.input.insert_mode.auto_load or not ok:
return
cur_mode = self._mode_manager.mode
if cur_mode == usertypes.KeyMode.insert:
return
def _auto_insert_mode_cb(elem: webelem.AbstractWebElement) -> None:
"""Called from JS after finding the focused element."""
if elem is None:
log.webview.debug("No focused element!")
return
if elem.is_editable():
modeman.enter(self.win_id, usertypes.KeyMode.insert,
'load finished', only_if_normal=True)
self.elements.find_focused(_auto_insert_mode_cb)
@pyqtSlot(bool) @pyqtSlot(bool)
def _on_load_finished(self, ok: bool) -> None: def _on_load_finished(self, ok: bool) -> None:
assert self._widget is not None assert self._widget is not None
@ -1010,9 +1044,6 @@ class AbstractTab(QWidget):
def stop(self) -> None: def stop(self) -> None:
raise NotImplementedError raise NotImplementedError
def clear_ssl_errors(self) -> None:
raise NotImplementedError
def key_press(self, def key_press(self,
key: Qt.Key, key: Qt.Key,
modifier: Qt.KeyboardModifier = Qt.NoModifier) -> None: modifier: Qt.KeyboardModifier = Qt.NoModifier) -> None:
@ -1048,9 +1079,6 @@ class AbstractTab(QWidget):
""" """
raise NotImplementedError raise NotImplementedError
def shutdown(self) -> None:
raise NotImplementedError
def title(self) -> str: def title(self) -> str:
raise NotImplementedError raise NotImplementedError
@ -1060,22 +1088,6 @@ class AbstractTab(QWidget):
def set_html(self, html: str, base_url: QUrl = QUrl()) -> None: def set_html(self, html: str, base_url: QUrl = QUrl()) -> None:
raise NotImplementedError raise NotImplementedError
def networkaccessmanager(self) -> typing.Optional[QNetworkAccessManager]:
"""Get the QNetworkAccessManager for this tab.
This is only implemented for QtWebKit.
For QtWebEngine, always returns None.
"""
raise NotImplementedError
def user_agent(self) -> typing.Optional[str]:
"""Get the user agent for this tab.
This is only implemented for QtWebKit.
For QtWebEngine, always returns None.
"""
raise NotImplementedError
def __repr__(self) -> str: def __repr__(self) -> str:
try: try:
qurl = self.url() qurl = self.url()

View File

@ -1516,7 +1516,7 @@ class CommandDispatcher:
else: else:
download_manager.get_mhtml(tab, target) download_manager.get_mhtml(tab, target)
else: else:
qnam = tab.networkaccessmanager() qnam = tab.private_api.networkaccessmanager()
suggested_fn = downloads.suggested_fn_from_title( suggested_fn = downloads.suggested_fn_from_title(
self._current_url().path(), tab.title() self._current_url().path(), tab.title()
@ -2165,7 +2165,7 @@ class CommandDispatcher:
debug=True, backend=usertypes.Backend.QtWebKit) debug=True, backend=usertypes.Backend.QtWebKit)
def debug_clear_ssl_errors(self): def debug_clear_ssl_errors(self):
"""Clear remembered SSL error answers.""" """Clear remembered SSL error answers."""
self._current_widget().clear_ssl_errors() self._current_widget().private_api.clear_ssl_errors()
@cmdutils.register(instance='command-dispatcher', scope='window') @cmdutils.register(instance='command-dispatcher', scope='window')
def edit_url(self, url=None, bg=False, tab=False, window=False, def edit_url(self, url=None, bg=False, tab=False, window=False,

View File

@ -304,7 +304,7 @@ class HintActions:
raise HintingError("No suitable link found for this element.") raise HintingError("No suitable link found for this element.")
prompt = False if context.rapid else None prompt = False if context.rapid else None
qnam = context.tab.networkaccessmanager() qnam = context.tab.private_api.networkaccessmanager()
user_agent = context.tab.user_agent() user_agent = context.tab.user_agent()
# FIXME:qtwebengine do this with QtWebEngine downloads? # FIXME:qtwebengine do this with QtWebEngine downloads?

View File

@ -240,7 +240,7 @@ class MouseEventFilter(QObject):
evtype = event.type() evtype = event.type()
if evtype not in self._handlers: if evtype not in self._handlers:
return False return False
if obj is not self._tab.event_target(): if obj is not self._tab.private_api.event_target():
log.mouse.debug("Ignoring {} to {}".format( log.mouse.debug("Ignoring {} to {}".format(
event.__class__.__name__, obj)) event.__class__.__name__, obj))
return False return False

View File

@ -1038,6 +1038,28 @@ class _WebEngineScripts(QObject):
page_scripts.insert(new_script) page_scripts.insert(new_script)
class WebEngineTabPrivate(browsertab.AbstractTabPrivate):
"""QtWebEngine-related methods which aren't part of the public API."""
def networkaccessmanager(self):
return None
def user_agent(self):
return None
def clear_ssl_errors(self):
raise browsertab.UnsupportedOperationError
def event_target(self):
return self._widget.render_widget()
def shutdown(self):
self._tab.shutting_down.emit()
self._tab.action.exit_fullscreen()
self._widget.shutdown()
class WebEngineTab(browsertab.AbstractTab): class WebEngineTab(browsertab.AbstractTab):
"""A QtWebEngine tab in the browser. """A QtWebEngine tab in the browser.
@ -1051,8 +1073,7 @@ class WebEngineTab(browsertab.AbstractTab):
_load_finished_fake = pyqtSignal(bool) _load_finished_fake = pyqtSignal(bool)
def __init__(self, *, win_id, mode_manager, private, 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, private=private, 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) private=private)
self.history = WebEngineHistory(tab=self) self.history = WebEngineHistory(tab=self)
@ -1065,6 +1086,8 @@ class WebEngineTab(browsertab.AbstractTab):
self.elements = WebEngineElements(tab=self) self.elements = WebEngineElements(tab=self)
self.action = WebEngineAction(tab=self) self.action = WebEngineAction(tab=self)
self.audio = WebEngineAudio(tab=self, parent=self) self.audio = WebEngineAudio(tab=self, parent=self)
self.private_api = WebEngineTabPrivate(mode_manager=mode_manager,
tab=self)
self._permissions = _WebEnginePermissions(tab=self, parent=self) self._permissions = _WebEnginePermissions(tab=self, parent=self)
self._scripts = _WebEngineScripts(tab=self, parent=self) self._scripts = _WebEngineScripts(tab=self, parent=self)
# We're assigning settings in _set_widget # We're assigning settings in _set_widget
@ -1146,11 +1169,6 @@ class WebEngineTab(browsertab.AbstractTab):
else: else:
self._widget.page().runJavaScript(code, world_id, callback) self._widget.page().runJavaScript(code, world_id, callback)
def shutdown(self):
self.shutting_down.emit()
self.action.exit_fullscreen()
self._widget.shutdown()
def reload(self, *, force=False): def reload(self, *, force=False):
if force: if force:
action = QWebEnginePage.ReloadAndBypassCache action = QWebEnginePage.ReloadAndBypassCache
@ -1175,15 +1193,6 @@ class WebEngineTab(browsertab.AbstractTab):
# percent encoded content is 2 megabytes minus 30 bytes. # percent encoded content is 2 megabytes minus 30 bytes.
self._widget.setHtml(html, base_url) self._widget.setHtml(html, base_url)
def networkaccessmanager(self):
return None
def user_agent(self):
return None
def clear_ssl_errors(self):
raise browsertab.UnsupportedOperationError
def key_press(self, key, modifier=Qt.NoModifier): def key_press(self, key, modifier=Qt.NoModifier):
press_evt = QKeyEvent(QEvent.KeyPress, key, modifier, 0, 0, 0) press_evt = QKeyEvent(QEvent.KeyPress, key, modifier, 0, 0, 0)
release_evt = QKeyEvent(QEvent.KeyRelease, key, modifier, release_evt = QKeyEvent(QEvent.KeyRelease, key, modifier,
@ -1485,6 +1494,3 @@ class WebEngineTab(browsertab.AbstractTab):
self.audio._connect_signals() self.audio._connect_signals()
self._permissions.connect_signals() self._permissions.connect_signals()
self._scripts.connect_signals() self._scripts.connect_signals()
def event_target(self):
return self._widget.render_widget()

View File

@ -657,13 +657,33 @@ class WebKitAudio(browsertab.AbstractAudio):
return False return False
class WebKitTabPrivate(browsertab.AbstractTabPrivate):
"""QtWebKit-related methods which aren't part of the public API."""
def networkaccessmanager(self):
return self._widget.page().networkAccessManager()
def user_agent(self):
page = self._widget.page()
return page.userAgentForUrl(self._tab.url())
def clear_ssl_errors(self):
self.networkaccessmanager().clear_all_ssl_errors()
def event_target(self):
return self._widget
def shutdown(self):
self._widget.shutdown()
class WebKitTab(browsertab.AbstractTab): class WebKitTab(browsertab.AbstractTab):
"""A QtWebKit tab in the browser.""" """A QtWebKit tab in the browser."""
def __init__(self, *, win_id, mode_manager, private, 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, private=private, parent=parent)
private=private, parent=parent)
widget = webview.WebView(win_id=win_id, tab_id=self.tab_id, widget = webview.WebView(win_id=win_id, tab_id=self.tab_id,
private=private, tab=self) private=private, tab=self)
if private: if private:
@ -678,6 +698,8 @@ class WebKitTab(browsertab.AbstractTab):
self.elements = WebKitElements(tab=self) self.elements = WebKitElements(tab=self)
self.action = WebKitAction(tab=self) self.action = WebKitAction(tab=self)
self.audio = WebKitAudio(tab=self, parent=self) self.audio = WebKitAudio(tab=self, parent=self)
self.private_api = WebKitTabPrivate(mode_manager=mode_manager,
tab=self)
# We're assigning settings in _set_widget # We're assigning settings in _set_widget
self.settings = webkitsettings.WebKitSettings(settings=None) self.settings = webkitsettings.WebKitSettings(settings=None)
self._set_widget(widget) self._set_widget(widget)
@ -720,9 +742,6 @@ class WebKitTab(browsertab.AbstractTab):
def icon(self): def icon(self):
return self._widget.icon() return self._widget.icon()
def shutdown(self):
self._widget.shutdown()
def reload(self, *, force=False): def reload(self, *, force=False):
if force: if force:
action = QWebPage.ReloadAndBypassCache action = QWebPage.ReloadAndBypassCache
@ -736,9 +755,6 @@ class WebKitTab(browsertab.AbstractTab):
def title(self): def title(self):
return self._widget.title() return self._widget.title()
def clear_ssl_errors(self):
self.networkaccessmanager().clear_all_ssl_errors()
def key_press(self, key, modifier=Qt.NoModifier): def key_press(self, key, modifier=Qt.NoModifier):
press_evt = QKeyEvent(QEvent.KeyPress, key, modifier, 0, 0, 0) press_evt = QKeyEvent(QEvent.KeyPress, key, modifier, 0, 0, 0)
release_evt = QKeyEvent(QEvent.KeyRelease, key, modifier, release_evt = QKeyEvent(QEvent.KeyRelease, key, modifier,
@ -755,17 +771,11 @@ class WebKitTab(browsertab.AbstractTab):
def set_html(self, html, base_url=QUrl()): def set_html(self, html, base_url=QUrl()):
self._widget.setHtml(html, base_url) self._widget.setHtml(html, base_url)
def networkaccessmanager(self):
return self._widget.page().networkAccessManager()
def user_agent(self):
page = self._widget.page()
return page.userAgentForUrl(self.url())
@pyqtSlot() @pyqtSlot()
def _on_load_started(self): def _on_load_started(self):
super()._on_load_started() super()._on_load_started()
self.networkaccessmanager().netrc_used = False nam = self._widget.page().networkAccessManager()
nam.netrc_used = False
# Make sure the icon is cleared when navigating to a page without one. # Make sure the icon is cleared when navigating to a page without one.
self.icon_changed.emit(QIcon()) self.icon_changed.emit(QIcon())
@ -847,6 +857,3 @@ class WebKitTab(browsertab.AbstractTab):
frame.contentsSizeChanged.connect(self._on_contents_size_changed) frame.contentsSizeChanged.connect(self._on_contents_size_changed)
frame.initialLayoutCompleted.connect(self._on_history_trigger) frame.initialLayoutCompleted.connect(self._on_history_trigger)
page.navigation_request.connect(self._on_navigation_request) page.navigation_request.connect(self._on_navigation_request)
def event_target(self):
return self._widget

View File

@ -753,7 +753,7 @@ class TabbedBrowser(QWidget):
self.widget.update_tab_title(idx) self.widget.update_tab_title(idx)
if idx == self.widget.currentIndex(): if idx == self.widget.currentIndex():
self._update_window_title() self._update_window_title()
tab.handle_auto_insert_mode(ok) tab.private_api.handle_auto_insert_mode(ok)
@pyqtSlot() @pyqtSlot()
def on_scroll_pos_changed(self): def on_scroll_pos_changed(self):

View File

@ -241,6 +241,12 @@ class FakeWebTabAudio(browsertab.AbstractAudio):
return False return False
class FakeWebTabPrivate:
def shutdown(self):
pass
class FakeWebTab(browsertab.AbstractTab): class FakeWebTab(browsertab.AbstractTab):
"""Fake AbstractTab to use in tests.""" """Fake AbstractTab to use in tests."""
@ -258,6 +264,7 @@ class FakeWebTab(browsertab.AbstractTab):
can_go_forward=can_go_forward) can_go_forward=can_go_forward)
self.scroller = FakeWebTabScroller(self, scroll_pos_perc) self.scroller = FakeWebTabScroller(self, scroll_pos_perc)
self.audio = FakeWebTabAudio(self) self.audio = FakeWebTabAudio(self)
self.private_api = FakeWebTabPrivate()
wrapped = QWidget() wrapped = QWidget()
self._layout.wrap(self, wrapped) self._layout.wrap(self, wrapped)
@ -274,9 +281,6 @@ class FakeWebTab(browsertab.AbstractTab):
def load_status(self): def load_status(self):
return self._load_status return self._load_status
def shutdown(self):
pass
def icon(self): def icon(self):
return QIcon() return QIcon()