greasemonkey: run scripts on subframes on webkit
Use the `QWebPage.frameCreated` signal to get notifications of subframes and connect the javascript injection triggering signals on those frames too. I had to add a `url = url() or requestedUrl()` bit in there because the inject_userjs method was getting called to early or something when frame.url() wasn't set or was set to the previous page so we were passing the wrong url to greasemonkey.scripts_for(). I ran into a bizarre (I maybe it is completely obvious and I just don't see it) issue where the signals attached to the main frame that were connected to a partial function with the main frame as an argument were not getting emitted, or at least those partial functions were not being called. I worked around it by using None to mean defaulting to the main frame in a couple of places.
This commit is contained in:
parent
4c3461038d
commit
9aeb5775c1
@ -50,7 +50,6 @@ class GreasemonkeyScript:
|
|||||||
self.namespace = None
|
self.namespace = None
|
||||||
self.run_at = None
|
self.run_at = None
|
||||||
self.script_meta = None
|
self.script_meta = None
|
||||||
# Running on subframes is only supported on the qtwebengine backend.
|
|
||||||
self.runs_on_sub_frames = True
|
self.runs_on_sub_frames = True
|
||||||
for name, value in properties:
|
for name, value in properties:
|
||||||
if name == 'name':
|
if name == 'name':
|
||||||
|
@ -86,12 +86,33 @@ class BrowserPage(QWebPage):
|
|||||||
self.on_save_frame_state_requested)
|
self.on_save_frame_state_requested)
|
||||||
self.restoreFrameStateRequested.connect(
|
self.restoreFrameStateRequested.connect(
|
||||||
self.on_restore_frame_state_requested)
|
self.on_restore_frame_state_requested)
|
||||||
self.mainFrame().javaScriptWindowObjectCleared.connect(
|
self.connect_userjs_signals(None)
|
||||||
functools.partial(self.inject_userjs, load='start'))
|
self.frameCreated.connect(self.connect_userjs_signals)
|
||||||
self.mainFrame().initialLayoutCompleted.connect(
|
|
||||||
functools.partial(self.inject_userjs, load='end'))
|
@pyqtSlot('QWebFrame*')
|
||||||
self.mainFrame().loadFinished.connect(
|
def connect_userjs_signals(self, frame_arg):
|
||||||
functools.partial(self.inject_userjs, load='idle'))
|
"""
|
||||||
|
Connect the signals used as triggers for injecting user
|
||||||
|
javascripts into `frame_arg`.
|
||||||
|
"""
|
||||||
|
# If we pass whatever self.mainFrame() or self.currentFrame() returns
|
||||||
|
# at init time into the partial functions which the signals
|
||||||
|
# below call then the signals don't seem to be called at all for
|
||||||
|
# the main frame of the first tab. I have no idea why I am
|
||||||
|
# seeing this behavior. Replace the None in the call to this
|
||||||
|
# function in __init__ with self.mainFrame() and try for
|
||||||
|
# yourself.
|
||||||
|
if frame_arg:
|
||||||
|
frame = frame_arg
|
||||||
|
else:
|
||||||
|
frame = self.mainFrame()
|
||||||
|
|
||||||
|
frame.javaScriptWindowObjectCleared.connect(
|
||||||
|
functools.partial(self.inject_userjs, frame_arg, load='start'))
|
||||||
|
frame.initialLayoutCompleted.connect(
|
||||||
|
functools.partial(self.inject_userjs, frame_arg, load='end'))
|
||||||
|
frame.loadFinished.connect(
|
||||||
|
functools.partial(self.inject_userjs, frame_arg, load='idle'))
|
||||||
|
|
||||||
def javaScriptPrompt(self, frame, js_msg, default):
|
def javaScriptPrompt(self, frame, js_msg, default):
|
||||||
"""Override javaScriptPrompt to use qutebrowser prompts."""
|
"""Override javaScriptPrompt to use qutebrowser prompts."""
|
||||||
@ -290,16 +311,24 @@ class BrowserPage(QWebPage):
|
|||||||
self.error_occurred = False
|
self.error_occurred = False
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def inject_userjs(self, load):
|
def inject_userjs(self, frame, load):
|
||||||
"""Inject user javascripts into the page.
|
"""Inject user javascripts into the page.
|
||||||
|
|
||||||
param: The page load stage to inject the corresponding scripts
|
Args:
|
||||||
for. Support values are "start", "end" and "idle",
|
frame: The QWebFrame to inject the user scripts into, or
|
||||||
corresponding to the allowed values of the `@run-at`
|
None for the main frame.
|
||||||
directive in the greasemonkey metadata spec.
|
load: The page load stage to inject the corresponding
|
||||||
|
scripts for. Support values are "start", "end" and
|
||||||
|
"idle", corresponding to the allowed values of the
|
||||||
|
`@run-at` directive in the greasemonkey metadata spec.
|
||||||
"""
|
"""
|
||||||
|
if not frame:
|
||||||
|
frame = self.mainFrame()
|
||||||
|
url = frame.url()
|
||||||
|
if url.isEmpty():
|
||||||
|
url = frame.requestedUrl()
|
||||||
|
|
||||||
greasemonkey = objreg.get('greasemonkey')
|
greasemonkey = objreg.get('greasemonkey')
|
||||||
url = self.currentFrame().url()
|
|
||||||
scripts = greasemonkey.scripts_for(url)
|
scripts = greasemonkey.scripts_for(url)
|
||||||
|
|
||||||
if load == "start":
|
if load == "start":
|
||||||
@ -309,9 +338,15 @@ class BrowserPage(QWebPage):
|
|||||||
elif load == "idle":
|
elif load == "idle":
|
||||||
toload = scripts.idle
|
toload = scripts.idle
|
||||||
|
|
||||||
|
if url.isEmpty():
|
||||||
|
# This happens during normal usage like with view source but may
|
||||||
|
# also indicate a bug.
|
||||||
|
log.greasemonkey.debug("Not running scripts for frame with no "
|
||||||
|
"url: {}".format(frame))
|
||||||
for script in toload:
|
for script in toload:
|
||||||
log.webview.debug('Running GM script: {}'.format(script.name))
|
if frame is self.mainFrame() or script.runs_on_sub_frames:
|
||||||
self.currentFrame().evaluateJavaScript(script.code())
|
log.webview.debug('Running GM script: {}'.format(script.name))
|
||||||
|
frame.evaluateJavaScript(script.code())
|
||||||
|
|
||||||
@pyqtSlot('QWebFrame*', 'QWebPage::Feature')
|
@pyqtSlot('QWebFrame*', 'QWebPage::Feature')
|
||||||
def _on_feature_permission_requested(self, frame, feature):
|
def _on_feature_permission_requested(self, frame, feature):
|
||||||
|
Loading…
Reference in New Issue
Block a user