Qt WebView - intercept loading of JS/CSS Libraries to load local ones
Asked Answered
D

2

7

I've been looking for a while through documentation to find a way to accomplish this and haven't been successful yet. The basic idea is, that I have a piece of html that I load through Qt's webview. The same content can be exported to a single html file.

This file uses Libraries such as Bootstrap and jQuery. Currently I load them through CDN which works when online just fine. However, my application also needs to run offline. So I'm looking for a way to intercept loading of the Libraries in Qt and serve a locally saved file instead. I've tried installing a https QWebEngineUrlSchemeHandler, but that never seems to trigger the requestStarted method on it.

(PyQT example follows)
QWebEngineProfile.defaultProfile().installUrlSchemeHandler(b'https', self)

If I use a different text for the scheme and embed that into the page it works, so my assumption is that it doesn't work as Qt has a default handler for it already registered. But that different scheme would fail in the file export.

Anyway, back to the core question; Is there a way to intercept loading of libraries, or to change the url scheme specifically within Qt only?

Got Further with QWebEngineUrlRequestInterceptor, now redirecting https requests to my own uri, which has a uri handler. However, the request never gets through to it, because: Redirect location 'conapp://webresource/bootstrap.min.css' has a disallowed scheme for cross-origin requests. How do I whitelist my own conapp uri scheme?

Edit: For completeness sake, it turns out back when I originally stated the question, it was impossible to accomplish with PySide 5.11 due to bugs in it. The bug I reported back then is nowadays flagged as fixed (5.12.1 I believe) so it should now be possible to accomplish this again using Qt methods, however for my own project I'll stick to jinja for now which has become a solution for many other problems.

Dijon answered 19/3, 2018 at 11:46 Comment(4)
I have never used this before, but have you looked at doc.qt.io/qt-5.10/qwebengineurlrequestinterceptor.html ?Polycotyledon
I have not, but that should be the solution once I dig into it.Dijon
I hit a snag, updated the question to reflect this.Dijon
Does this help? blog.qt.io/blog/2011/04/29/http-caching-with-qtPassivism
D
0

So, the solution I went with in the end was, first, introduce jinja templates. Then, using those the template would have variables and blocks set based on export or internal use and from there I did not need the interceptor anymore.

Dijon answered 26/4, 2018 at 8:6 Comment(0)
C
3

The following example shows how I've done it. It uses the QWebEngineUrlRequestInterceptor to redirect content to a local server.

As an example, I intercept the stacks.css for stackoverflow and make an obvious change.

import requests
import sys
import threading
from PyQt5 import QtWidgets, QtCore
from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEnginePage, QWebEngineProfile
from PyQt5.QtWebEngineCore import QWebEngineUrlRequestInterceptor, QWebEngineUrlRequestInfo
from http.server import HTTPServer, SimpleHTTPRequestHandler
from socketserver import ThreadingMixIn

# Set these to the address you want your local patch server to run
HOST = '127.0.0.1'
PORT = 1235

class WebEngineUrlRequestInterceptor(QWebEngineUrlRequestInterceptor):
    def patch_css(self, url):
        print('patching', url)
        r = requests.get(url)
        new_css = r.text + '#mainbar {background-color: cyan;}'  # Example of some css change
        with open('local_stacks.css', 'w') as outfile:
            outfile.write(new_css)
    def interceptRequest(self, info: QWebEngineUrlRequestInfo):
        url = info.requestUrl().url()
        if url == "https://cdn.sstatic.net/Shared/stacks.css?v=596945d5421b":
            self.patch_css(url)
            print('Using local file for', url)
            info.redirect(QtCore.QUrl('http:{}:{}/local_stacks.css'.format(HOST, PORT)))

class ThreadingHTTPServer(ThreadingMixIn, HTTPServer):
    """Threaded HTTPServer"""

app = QtWidgets.QApplication(sys.argv)

# Start up thread to server patched content
server = ThreadingHTTPServer((HOST, PORT), SimpleHTTPRequestHandler)
server_thread = threading.Thread(target=server.serve_forever)
server_thread.daemon = True
server_thread.start()

# Install an interceptor to redirect to patched content
interceptor = WebEngineUrlRequestInterceptor()
profile = QWebEngineProfile.defaultProfile()
profile.setRequestInterceptor(interceptor)

w = QWebEngineView()
w.load(QtCore.QUrl('https://stackoverflow.com'))
w.show()
app.exec_()
Cento answered 29/3, 2018 at 17:40 Comment(0)
D
0

So, the solution I went with in the end was, first, introduce jinja templates. Then, using those the template would have variables and blocks set based on export or internal use and from there I did not need the interceptor anymore.

Dijon answered 26/4, 2018 at 8:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.