fit-webview-bridge 0.1.1a5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of fit-webview-bridge might be problematic. Click here for more details.

@@ -0,0 +1,82 @@
1
+ name: wheels-macos
2
+
3
+ on:
4
+ push:
5
+ tags: ['v*']
6
+ pull_request:
7
+ workflow_dispatch:
8
+
9
+ jobs:
10
+ build-macos:
11
+ runs-on: macos-14
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+
15
+ - name: Extract version from tag
16
+ if: startsWith(github.ref, 'refs/tags/')
17
+ id: v
18
+ run: |
19
+ REF="${GITHUB_REF##*/}"
20
+ echo "version=${REF#v}" >> $GITHUB_OUTPUT
21
+
22
+ - name: Sync pyproject version to tag (workspace only)
23
+ if: startsWith(github.ref, 'refs/tags/')
24
+ env:
25
+ VERSION: ${{ steps.v.outputs.version }}
26
+ run: |
27
+ python - <<'PY'
28
+ import os, re, pathlib
29
+ ver = os.environ["VERSION"]
30
+ p = pathlib.Path("pyproject.toml")
31
+ s = p.read_text(encoding="utf-8")
32
+ s = re.sub(r'(?m)^version\s*=\s*".*"', f'version = "{ver}"', s)
33
+ p.write_text(s, encoding="utf-8")
34
+ print("pyproject.toml version ->", ver)
35
+ PY
36
+
37
+ - uses: actions/setup-python@v5
38
+ with:
39
+ python-version: '3.13'
40
+
41
+ - name: Install build tools
42
+ run: |
43
+ python -m pip install -U cibuildwheel aqtinstall build
44
+
45
+ - name: Install Qt SDK via aqt
46
+ run: |
47
+ aqt install-qt mac desktop 6.9.0 clang_64 -O "$RUNNER_TEMP/Qt"
48
+ echo "QT6_DIR=$RUNNER_TEMP/Qt/6.9.0/macos/lib/cmake/Qt6" >> $GITHUB_ENV
49
+
50
+ - name: Build wheels (macOS arm64, cp311/cp312/cp313)
51
+ env:
52
+ SKBUILD_CMAKE_ARGS: "-DQt6_DIR=${{ env.QT6_DIR }}"
53
+ CIBW_REPAIR_WHEEL_COMMAND: ""
54
+ FITWVB_VENDORIZE: "1"
55
+ CIBW_BUILD_VERBOSITY: "1"
56
+ SKBUILD_CONFIGURE_LOG: "1"
57
+ SKBUILD_BUILD_LOG: "1"
58
+ run: python -m cibuildwheel --output-dir dist
59
+
60
+ - name: Build sdist
61
+ run: python -m build --sdist
62
+
63
+ - name: Upload dist artifact
64
+ uses: actions/upload-artifact@v4
65
+ with:
66
+ name: dist
67
+ path: dist/*
68
+
69
+ publish:
70
+ needs: build-macos
71
+ runs-on: ubuntu-latest
72
+ if: startsWith(github.ref, 'refs/tags/')
73
+ steps:
74
+ - uses: actions/download-artifact@v4
75
+ with:
76
+ name: dist
77
+ path: dist
78
+
79
+ - name: Publish to PyPI
80
+ uses: pypa/gh-action-pypi-publish@release/v1
81
+ with:
82
+ password: ${{ secrets.PYPI_API_TOKEN }}
@@ -0,0 +1,184 @@
1
+ # Created by https://www.toptal.com/developers/gitignore/api/python
2
+ # Edit at https://www.toptal.com/developers/gitignore?templates=python
3
+
4
+ ### Python ###
5
+ # Byte-compiled / optimized / DLL files
6
+ __pycache__/
7
+ *.py[cod]
8
+ *$py.class
9
+
10
+ # C extensions
11
+ *.so
12
+
13
+ # Distribution / packaging
14
+ .Python
15
+ build/
16
+ develop-eggs/
17
+ dist/
18
+ downloads/
19
+ eggs/
20
+ .eggs/
21
+ lib/
22
+ lib64/
23
+ parts/
24
+ sdist/
25
+ var/
26
+ wheels/
27
+ share/python-wheels/
28
+ *.egg-info/
29
+ .installed.cfg
30
+ *.egg
31
+ MANIFEST
32
+
33
+ # PyInstaller
34
+ # Usually these files are written by a python script from a template
35
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
36
+ *.manifest
37
+ #*.spec
38
+
39
+ # Installer logs
40
+ pip-log.txt
41
+ pip-delete-this-directory.txt
42
+
43
+ # Unit test / coverage reports
44
+ htmlcov/
45
+ .tox/
46
+ .nox/
47
+ .coverage
48
+ .coverage.*
49
+ .cache
50
+ nosetests.xml
51
+ coverage.xml
52
+ *.cover
53
+ *.py,cover
54
+ .hypothesis/
55
+ .pytest_cache/
56
+ cover/
57
+
58
+ # Translations
59
+ *.mo
60
+ *.pot
61
+
62
+ # Django stuff:
63
+ *.log
64
+ local_settings.py
65
+ db.sqlite3
66
+ db.sqlite3-journal
67
+
68
+ # Flask stuff:
69
+ instance/
70
+ .webassets-cache
71
+
72
+ # Scrapy stuff:
73
+ .scrapy
74
+
75
+ # Sphinx documentation
76
+ docs/_build/
77
+
78
+ # PyBuilder
79
+ .pybuilder/
80
+ target/
81
+
82
+ # Jupyter Notebook
83
+ .ipynb_checkpoints
84
+
85
+ # IPython
86
+ profile_default/
87
+ ipython_config.py
88
+
89
+ # pyenv
90
+ # For a library or package, you might want to ignore these files since the code is
91
+ # intended to run in multiple environments; otherwise, check them in:
92
+ # .python-version
93
+
94
+ # pipenv
95
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
96
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
97
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
98
+ # install all needed dependencies.
99
+ #Pipfile.lock
100
+
101
+ # poetry
102
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
103
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
104
+ # commonly ignored for libraries.
105
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
106
+ #poetry.lock
107
+
108
+ # pdm
109
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
110
+ #pdm.lock
111
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
112
+ # in version control.
113
+ # https://pdm.fming.dev/#use-with-ide
114
+ .pdm.toml
115
+
116
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
117
+ __pypackages__/
118
+
119
+ # Celery stuff
120
+ celerybeat-schedule
121
+ celerybeat.pid
122
+
123
+ # SageMath parsed files
124
+ *.sage.py
125
+
126
+ # Environments
127
+ .env
128
+ .venv*
129
+ env/
130
+ venv/
131
+ ENV/
132
+ env.bak/
133
+ venv.bak/
134
+
135
+ # Spyder project settings
136
+ .spyderproject
137
+ .spyproject
138
+
139
+ # Rope project settings
140
+ .ropeproject
141
+
142
+ # mkdocs documentation
143
+ /site
144
+
145
+ # mypy
146
+ .mypy_cache/
147
+ .dmypy.json
148
+ dmypy.json
149
+
150
+ # Pyre type checker
151
+ .pyre/
152
+
153
+ # pytype static type analyzer
154
+ .pytype/
155
+
156
+ # Cython debug symbols
157
+ cython_debug/
158
+
159
+ # PyCharm
160
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
161
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
162
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
163
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
164
+ #.idea/
165
+
166
+ ### Python Patch ###
167
+ # Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
168
+ poetry.toml
169
+
170
+ # ruff
171
+ .ruff_cache/
172
+
173
+ # LSP config files
174
+ pyrightconfig.json
175
+
176
+ # End of https://www.toptal.com/developers/gitignore/api/python
177
+
178
+ # Fit archive
179
+ *.db
180
+ .DS_Store
181
+ FIT/
182
+ COMPILAZIONE.md
183
+ TEST.md
184
+ Qt/
@@ -0,0 +1,46 @@
1
+ {
2
+ "psi-header.config": {
3
+ "forceToTop": true,
4
+ "blankLinesAfter": 1,
5
+ "license": "Custom"
6
+ },
7
+ "psi-header.license-text": [
8
+ "Copyright (c) <<yeartoyear(fc, now)>> <<company>>",
9
+ "SPDX-License-Identifier: LGPL-3.0-or-later"
10
+ ],
11
+ "psi-header.variables": [
12
+ ["company", "FIT-Project"]
13
+ ],
14
+ "psi-header.lang-config": [
15
+ {
16
+ "language": "python",
17
+ "begin": "#####",
18
+ "end": "#####",
19
+ "prefix": "# ",
20
+ "blankLinesAfter": 1,
21
+ "beforeHeader": [
22
+ "#!/usr/bin/env python3",
23
+ "# -*- coding:utf-8 -*-"
24
+ ]
25
+ }
26
+ ],
27
+ "psi-header.templates": [
28
+ {
29
+ "language": "python",
30
+ "template": [
31
+ "-----",
32
+ "<<licensetext>>",
33
+ "-----"
34
+ ]
35
+ }
36
+ ],
37
+ "[python]": {
38
+ "editor.formatOnSave": true,
39
+ "editor.defaultFormatter": "ms-python.black-formatter",
40
+ "editor.codeActionsOnSave": {
41
+ "source.organizeImports": "explicit",
42
+ "source.fixAll": "explicit"
43
+ }
44
+ },
45
+ "ruff.enable": true
46
+ }
@@ -0,0 +1,29 @@
1
+ cmake_minimum_required(VERSION 3.24)
2
+ project(FITWebViewBridge LANGUAGES CXX)
3
+
4
+ # Standard C++
5
+ set(CMAKE_CXX_STANDARD 17)
6
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
7
+
8
+ # Opzione unica rimasta
9
+ option(BUILD_BINDINGS "Build Python bindings" ON)
10
+
11
+ # Dispatch per-OS: tutta la logica sta nelle sottodirectory
12
+ if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
13
+ add_subdirectory(src/macos)
14
+ if(BUILD_BINDINGS)
15
+ add_subdirectory(bindings/pyside6/macos)
16
+ endif()
17
+ elseif(WIN32)
18
+ add_subdirectory(src/win)
19
+ if(BUILD_BINDINGS)
20
+ add_subdirectory(bindings/pyside6/win)
21
+ endif()
22
+ elseif(UNIX) # Linux (non macOS)
23
+ add_subdirectory(src/linux)
24
+ if(BUILD_BINDINGS)
25
+ add_subdirectory(bindings/pyside6/linux)
26
+ endif()
27
+ else()
28
+ message(FATAL_ERROR "Unsupported platform: ${CMAKE_SYSTEM_NAME}")
29
+ endif()
@@ -0,0 +1,151 @@
1
+ Metadata-Version: 2.2
2
+ Name: fit-webview-bridge
3
+ Version: 0.1.1a5
4
+ Summary: Qt native WebView bridge with PySide6 bindings
5
+ Author: FIT Project
6
+ License: LGPL-3.0-or-later
7
+ Project-URL: Homepage, https://github.com/fit-project/fit-webview-bridge
8
+ Requires-Python: <3.14,>=3.11
9
+ Requires-Dist: PySide6==6.9.0
10
+ Requires-Dist: shiboken6==6.9.0
11
+ Requires-Dist: shiboken6-generator==6.9.0
12
+ Description-Content-Type: text/markdown
13
+
14
+ # FIT WebView Bridge
15
+
16
+ [🇬🇧 English](README.md) | [🇮🇹 Italiano](README.it.md)
17
+
18
+ ### Description
19
+
20
+ **FIT WebView Bridge** is a cross‑platform Qt widget (C++/Objective‑C++) with **PySide6** bindings that wraps the OS‑native web engines:
21
+ - **Windows →** Edge WebView2
22
+ - **macOS →** WKWebView
23
+ - **Linux →** WebKitGTK (with **GStreamer** for codecs)
24
+
25
+ It exposes a **unified Python API** for browser control and enables **forensic viewing/capture** of content, including media requiring proprietary codecs (e.g., **H.264/AAC**), **without** custom QtWebEngine builds or codec redistribution burdens (system codecs are used). All **controls** (UI and app logic) are **delegated to the PySide window**.
26
+
27
+
28
+ ### Why this project
29
+ QtWebEngine (Chromium) **does not enable proprietary codecs** by default, and redistributing them requires **licensing**. Alternatives (building QtWebEngine with codecs or using QtWebView/QML) have portability/control limitations. The chosen path is to **leverage native engines**, achieving codec compatibility and **full control** via a Python API.
30
+
31
+ ### Repository layout
32
+ ```
33
+ fit-webview-bridge/
34
+ ├─ CMakeLists.txt
35
+ ├─ cmake/ # Find*.cmake, toolchains, helpers
36
+ ├─ include/fitwvb/ # Public headers (API)
37
+ ├─ src/
38
+ │ ├─ core/ # Facade / common interfaces
39
+ │ ├─ win/ # Edge WebView2 backend (C++)
40
+ │ ├─ macos/ # WKWebView backend (Obj-C++)
41
+ │ └─ linux/ # WebKitGTK backend (C++)
42
+ ├─ bindings/pyside6/ # Shiboken6: typesystem & config
43
+ ├─ tests/ # Unit / integration
44
+ └─ examples/ # Minimal PySide6 demo app
45
+ ```
46
+
47
+ ### Interface (methods/slots + signals)
48
+ **Methods / slots**
49
+ - `load(url)`
50
+ - `back()`
51
+ - `forward()`
52
+ - `reload()`
53
+ - `stop()`
54
+ - `setHtml(html, baseUrl)`
55
+ - `evalJs(script, callback)`
56
+
57
+ **Signals**
58
+ - `urlChanged(QUrl)`
59
+ - `titleChanged(QString)`
60
+ - `loadProgress(int)`
61
+ - `loadFinished(bool)`
62
+ - `consoleMessage(QString)`
63
+
64
+ > Note: the API is **uniform** across OSes; implementations delegate to the native engine.
65
+
66
+ ### Prerequisites
67
+ **Common**
68
+ - **CMake** (>= 3.24 recommended)
69
+ - **Ninja** (generator)
70
+ - **Python** 3.9+
71
+ - **PySide6** and **Shiboken6** (for Python bindings)
72
+ - Platform build toolchain
73
+
74
+ **Windows**
75
+ - **MSVC** (Visual Studio 2022 or Build Tools) and Windows SDK
76
+ - **Microsoft Edge WebView2 Runtime**
77
+ - **WebView2 SDK** (NuGet/vcpkg)
78
+
79
+ **macOS**
80
+ - **Xcode** + Command Line Tools
81
+ - **Objective‑C++** enabled (.mm)
82
+ - Frameworks: `WebKit`, `Cocoa`
83
+
84
+ **Linux**
85
+ - **GCC/Clang**, `pkg-config`
86
+ - **WebKitGTK** dev packages (e.g., `webkit2gtk-4.1` or distro equivalent)
87
+ - **GStreamer** (base + required plugins for codecs)
88
+
89
+ ### Build (indicative)
90
+ ```bash
91
+ git clone https://github.com/fit-project/fit-webview-bridge.git
92
+ cd fit-webview-bridge
93
+ cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_PYSIDE6_BINDINGS=ON
94
+ cmake --build build
95
+ # (optional) ctest --test-dir build
96
+ ```
97
+
98
+ ### Examples
99
+ PySide6 samples in `examples/` demonstrate URL loading, JS injection, and signal handling.
100
+
101
+ ### Codec & licensing notes
102
+ The project **does not** redistribute proprietary codecs: it leverages codecs **already provided by the OS**. End‑user usage must comply with the relevant licenses/formats.
103
+
104
+ ### Project status
105
+ Initial/alpha (API subject to change).
106
+
107
+ # Fit Web — Project rationale and options for proprietary codecs
108
+
109
+ **Fit Web** is the FIT project's *scraper* module designed to **forensically acquire and preserve web content**: <https://github.com/fit-project/fit-web>.
110
+
111
+ Like the other modules, **Fit Web** is based on **PySide** (Qt for Python). It currently uses **QtWebEngine**, which is a **Chromium** wrapper.
112
+
113
+ ## The problem
114
+ By default, Chromium **does not enable proprietary audio/video codecs**, notably **H.264** and **AAC**.
115
+
116
+ ## Options considered
117
+
118
+ ### 1) Build QtWebEngine with proprietary codecs
119
+ Enable the `-webengine-proprietary-codecs` option.
120
+ Documentation: <https://doc.qt.io/qt-6/qtwebengine-overview.html>
121
+
122
+ **Drawbacks**
123
+ - Must be done for **all supported operating systems**.
124
+ - The build requires **very powerful machines** (e.g., difficulties on a MacBook Air M2 with 16 GB RAM).
125
+ - **Licensing**: distributing H.264 and AAC **requires a license**.
126
+
127
+ ### 2) Use QtWebView
128
+ QtWebView relies on **the OS’s native web APIs**; for proprietary‑codec content it uses **the system’s codecs**.
129
+ **Pros**: no custom builds, no direct license handling.
130
+ **Cons**: the UI layer is **QML**, geared toward lightweight (often mobile) UIs, so it **doesn’t provide full browser control** compared to QtWebEngine.
131
+
132
+ Documentation: <https://doc.qt.io/qt-6/qtwebview-index.html>
133
+
134
+ ### 3) Implement a native Qt widget (C/C++) per OS
135
+ Develop a Qt widget (usable from **PySide6**) that embeds the system’s web engine:
136
+
137
+ - **Windows →** Edge WebView2
138
+ - **macOS →** WKWebView
139
+ - **Linux →** WebKitGTK (with **GStreamer** for codecs)
140
+
141
+ **Advantages**
142
+ - **No redistribution licensing**: leverage the codecs already provided by the OS.
143
+ - A **common API** can be exposed to PySide6.
144
+ - **More control** than QtWebView, without QML’s limitations.
145
+
146
+ **Disadvantages**
147
+ - **Medium‑to‑high complexity** to implement.
148
+ - Requires **C++** and, on macOS, **Objective‑C++**.
149
+ - Requires **custom CMake** to include libraries and linking.
150
+
151
+ ---
@@ -0,0 +1,139 @@
1
+ # FIT WebView Bridge
2
+
3
+ [🇬🇧 English](README.md) | [🇮🇹 Italiano](README.it.md)
4
+
5
+ ### Descrizione
6
+
7
+ **FIT WebView Bridge** è un widget Qt multipiattaforma (C++/Objective-C++) con binding **PySide6** che incapsula i motori web nativi del sistema:
8
+ - **Windows →** Edge WebView2
9
+ - **macOS →** WKWebView
10
+ - **Linux →** WebKitGTK (con **GStreamer** per i codec)
11
+
12
+ Espone una **API Python unificata** per controllare il browser ed abilita la **visualizzazione/acquisizione forense** di contenuti, inclusi quelli che richiedono codec proprietari (es. **H.264/AAC**), **senza** build personalizzate di QtWebEngine né oneri di redistribuzione dei codec (si usano quelli del sistema operativo). I **controlli** (UI e logica applicativa) sono **demandati alla finestra PySide**.
13
+
14
+ ### Perché questo progetto
15
+ QtWebEngine (Chromium) di default **non abilita i codec proprietari** e la loro distribuzione richiede **licenza**. Le alternative considerate (compilare QtWebEngine con codec o usare QtWebView/QML) presentano limiti di portabilità/controllo. La via scelta è **incapsulare i motori di sistema**, ottenendo compatibilità con i codec e **massimo controllo** via API Python.
16
+
17
+ ### Struttura del repository
18
+ ```
19
+ fit-webview-bridge/
20
+ ├─ CMakeLists.txt
21
+ ├─ cmake/ # Find*.cmake, toolchain, helper
22
+ ├─ include/fitwvb/ # Header pubblici (API)
23
+ ├─ src/
24
+ │ ├─ core/ # Facade / interfacce comuni
25
+ │ ├─ win/ # Backend Edge WebView2 (C++)
26
+ │ ├─ macos/ # Backend WKWebView (Obj-C++)
27
+ │ └─ linux/ # Backend WebKitGTK (C++)
28
+ ├─ bindings/pyside6/ # Shiboken6: typesystem e config
29
+ ├─ tests/ # Unit / integration
30
+ └─ examples/ # Mini app PySide6 dimostrativa
31
+ ```
32
+
33
+ ### Interfaccia (metodi/slot + segnali)
34
+ **Metodi / slot**
35
+ - `load(url)`
36
+ - `back()`
37
+ - `forward()`
38
+ - `reload()`
39
+ - `stop()`
40
+ - `setHtml(html, baseUrl)`
41
+ - `evalJs(script, callback)`
42
+
43
+ **Segnali**
44
+ - `urlChanged(QUrl)`
45
+ - `titleChanged(QString)`
46
+ - `loadProgress(int)`
47
+ - `loadFinished(bool)`
48
+ - `consoleMessage(QString)`
49
+
50
+ > Nota: l’API è esposta in modo **uniforme** su tutti gli OS; l’implementazione delega al motore nativo.
51
+
52
+ ### Prerequisiti
53
+ **Comuni**
54
+ - **CMake** (>= 3.24 consigliato)
55
+ - **Ninja** (generator)
56
+ - **Python** 3.9+
57
+ - **PySide6** e **Shiboken6** (per i binding Python)
58
+ - Strumenti di build della piattaforma
59
+
60
+ **Windows**
61
+ - **MSVC** (Visual Studio 2022 o Build Tools) e Windows SDK
62
+ - **Microsoft Edge WebView2 Runtime**
63
+ - **WebView2 SDK** (NuGet/vcpkg)
64
+
65
+ **macOS**
66
+ - **Xcode** + Command Line Tools
67
+ - Linguaggio **Objective-C++** abilitato (.mm)
68
+ - Framework: `WebKit`, `Cocoa`
69
+
70
+ **Linux**
71
+ - **GCC/Clang**, `pkg-config`
72
+ - **WebKitGTK** dev (es. `webkit2gtk-4.1` o equivalente della distro)
73
+ - **GStreamer** (base + plugin necessari per i codec)
74
+
75
+ ### Compilazione (indicativa)
76
+ ```bash
77
+ git clone https://github.com/fit-project/fit-webview-bridge.git
78
+ cd fit-webview-bridge
79
+ cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_PYSIDE6_BINDINGS=ON
80
+ cmake --build build
81
+ # (opzionale) ctest --test-dir build
82
+ ```
83
+
84
+ ### Esempi
85
+ Gli esempi PySide6 si trovano in `examples/` e mostrano caricamento URL, iniezione JS e ascolto dei segnali.
86
+
87
+ ### Considerazioni su codec e licenze
88
+ Il progetto **non** ridistribuisce codec proprietari: sfrutta i codec **già presenti nel sistema operativo**. L’uso finale dei contenuti è responsabilità dell’utente in base alle rispettive licenze/formati.
89
+
90
+ ### Stato del progetto
91
+ Iniziale/alpha (API soggetta a modifiche).
92
+
93
+ ---
94
+
95
+ Fit Web — Motivazioni del progetto e opzioni per i codec proprietari
96
+
97
+ **Fit Web** è il modulo *scraper* del progetto FIT pensato per **acquisire e cristallizzare, con modalità forensi, contenuti web**: <https://github.com/fit-project/fit-web>.
98
+
99
+ Come gli altri moduli, **Fit Web** si basa su **PySide** (Qt per Python). Attualmente utilizza **QtWebEngine**, che è un *wrapper* di **Chromium**.
100
+
101
+ ## Il problema
102
+ Chromium, di default, **non abilita i codec audio/video proprietari**, in particolare **H.264** e **AAC**.
103
+
104
+ ## Soluzioni considerate
105
+
106
+ ### 1) Compilare QtWebEngine con codec proprietari
107
+ Abilitare l’opzione `-webengine-proprietary-codecs`.
108
+ Documentazione: <https://doc.qt.io/qt-6/qtwebengine-overview.html>
109
+
110
+ **Criticità**
111
+ - Va eseguito per **tutti i sistemi operativi** supportati.
112
+ - La compilazione richiede **macchine molto performanti** (es.: difficoltà su MacBook Air M2 con 16 GB di RAM).
113
+ - **Licenze**: la **distribuzione** di H.264 e AAC **richiede una licenza**.
114
+
115
+ ### 2) Usare QtWebView
116
+ QtWebView utilizza **le API web native del sistema operativo**; per i contenuti con codec proprietari sfrutta **i codec del sistema**.
117
+ **Vantaggi**: niente compilazioni personalizzate, niente gestione diretta delle licenze.
118
+ **Limiti**: l’interfaccia è in **QML**, pensata per UI leggere (spesso mobile), e **non offre il controllo completo** sul browser rispetto a QtWebEngine.
119
+
120
+ Documentazione: <https://doc.qt.io/qt-6/qtwebview-index.html>
121
+
122
+ ### 3) Scrivere un widget Qt nativo (C/C++) per ogni OS
123
+ Creare un widget Qt (usabile da **PySide6**) che *embedda* il motore web di sistema:
124
+
125
+ - **Windows →** Edge WebView2
126
+ - **macOS →** WKWebView
127
+ - **Linux →** WebKitGTK (con **GStreamer** per i codec)
128
+
129
+ **Vantaggi**
130
+ - **Nessuna licenza da redistribuire**: si usano i codec già forniti dal sistema.
131
+ - Possibilità di **un’API comune** da esporre a PySide6.
132
+ - **Maggiore controllo** rispetto a QtWebView, senza i limiti imposti da QML.
133
+
134
+ **Svantaggi**
135
+ - **Complessità medio‑alta** di implementazione.
136
+ - Richiede **C++** e, su macOS, anche **Objective‑C++**.
137
+ - Necessità di **CMake** ad hoc per includere librerie e collegamenti.
138
+
139
+ ---
@@ -0,0 +1,138 @@
1
+ # FIT WebView Bridge
2
+
3
+ [🇬🇧 English](README.md) | [🇮🇹 Italiano](README.it.md)
4
+
5
+ ### Description
6
+
7
+ **FIT WebView Bridge** is a cross‑platform Qt widget (C++/Objective‑C++) with **PySide6** bindings that wraps the OS‑native web engines:
8
+ - **Windows →** Edge WebView2
9
+ - **macOS →** WKWebView
10
+ - **Linux →** WebKitGTK (with **GStreamer** for codecs)
11
+
12
+ It exposes a **unified Python API** for browser control and enables **forensic viewing/capture** of content, including media requiring proprietary codecs (e.g., **H.264/AAC**), **without** custom QtWebEngine builds or codec redistribution burdens (system codecs are used). All **controls** (UI and app logic) are **delegated to the PySide window**.
13
+
14
+
15
+ ### Why this project
16
+ QtWebEngine (Chromium) **does not enable proprietary codecs** by default, and redistributing them requires **licensing**. Alternatives (building QtWebEngine with codecs or using QtWebView/QML) have portability/control limitations. The chosen path is to **leverage native engines**, achieving codec compatibility and **full control** via a Python API.
17
+
18
+ ### Repository layout
19
+ ```
20
+ fit-webview-bridge/
21
+ ├─ CMakeLists.txt
22
+ ├─ cmake/ # Find*.cmake, toolchains, helpers
23
+ ├─ include/fitwvb/ # Public headers (API)
24
+ ├─ src/
25
+ │ ├─ core/ # Facade / common interfaces
26
+ │ ├─ win/ # Edge WebView2 backend (C++)
27
+ │ ├─ macos/ # WKWebView backend (Obj-C++)
28
+ │ └─ linux/ # WebKitGTK backend (C++)
29
+ ├─ bindings/pyside6/ # Shiboken6: typesystem & config
30
+ ├─ tests/ # Unit / integration
31
+ └─ examples/ # Minimal PySide6 demo app
32
+ ```
33
+
34
+ ### Interface (methods/slots + signals)
35
+ **Methods / slots**
36
+ - `load(url)`
37
+ - `back()`
38
+ - `forward()`
39
+ - `reload()`
40
+ - `stop()`
41
+ - `setHtml(html, baseUrl)`
42
+ - `evalJs(script, callback)`
43
+
44
+ **Signals**
45
+ - `urlChanged(QUrl)`
46
+ - `titleChanged(QString)`
47
+ - `loadProgress(int)`
48
+ - `loadFinished(bool)`
49
+ - `consoleMessage(QString)`
50
+
51
+ > Note: the API is **uniform** across OSes; implementations delegate to the native engine.
52
+
53
+ ### Prerequisites
54
+ **Common**
55
+ - **CMake** (>= 3.24 recommended)
56
+ - **Ninja** (generator)
57
+ - **Python** 3.9+
58
+ - **PySide6** and **Shiboken6** (for Python bindings)
59
+ - Platform build toolchain
60
+
61
+ **Windows**
62
+ - **MSVC** (Visual Studio 2022 or Build Tools) and Windows SDK
63
+ - **Microsoft Edge WebView2 Runtime**
64
+ - **WebView2 SDK** (NuGet/vcpkg)
65
+
66
+ **macOS**
67
+ - **Xcode** + Command Line Tools
68
+ - **Objective‑C++** enabled (.mm)
69
+ - Frameworks: `WebKit`, `Cocoa`
70
+
71
+ **Linux**
72
+ - **GCC/Clang**, `pkg-config`
73
+ - **WebKitGTK** dev packages (e.g., `webkit2gtk-4.1` or distro equivalent)
74
+ - **GStreamer** (base + required plugins for codecs)
75
+
76
+ ### Build (indicative)
77
+ ```bash
78
+ git clone https://github.com/fit-project/fit-webview-bridge.git
79
+ cd fit-webview-bridge
80
+ cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_PYSIDE6_BINDINGS=ON
81
+ cmake --build build
82
+ # (optional) ctest --test-dir build
83
+ ```
84
+
85
+ ### Examples
86
+ PySide6 samples in `examples/` demonstrate URL loading, JS injection, and signal handling.
87
+
88
+ ### Codec & licensing notes
89
+ The project **does not** redistribute proprietary codecs: it leverages codecs **already provided by the OS**. End‑user usage must comply with the relevant licenses/formats.
90
+
91
+ ### Project status
92
+ Initial/alpha (API subject to change).
93
+
94
+ # Fit Web — Project rationale and options for proprietary codecs
95
+
96
+ **Fit Web** is the FIT project's *scraper* module designed to **forensically acquire and preserve web content**: <https://github.com/fit-project/fit-web>.
97
+
98
+ Like the other modules, **Fit Web** is based on **PySide** (Qt for Python). It currently uses **QtWebEngine**, which is a **Chromium** wrapper.
99
+
100
+ ## The problem
101
+ By default, Chromium **does not enable proprietary audio/video codecs**, notably **H.264** and **AAC**.
102
+
103
+ ## Options considered
104
+
105
+ ### 1) Build QtWebEngine with proprietary codecs
106
+ Enable the `-webengine-proprietary-codecs` option.
107
+ Documentation: <https://doc.qt.io/qt-6/qtwebengine-overview.html>
108
+
109
+ **Drawbacks**
110
+ - Must be done for **all supported operating systems**.
111
+ - The build requires **very powerful machines** (e.g., difficulties on a MacBook Air M2 with 16 GB RAM).
112
+ - **Licensing**: distributing H.264 and AAC **requires a license**.
113
+
114
+ ### 2) Use QtWebView
115
+ QtWebView relies on **the OS’s native web APIs**; for proprietary‑codec content it uses **the system’s codecs**.
116
+ **Pros**: no custom builds, no direct license handling.
117
+ **Cons**: the UI layer is **QML**, geared toward lightweight (often mobile) UIs, so it **doesn’t provide full browser control** compared to QtWebEngine.
118
+
119
+ Documentation: <https://doc.qt.io/qt-6/qtwebview-index.html>
120
+
121
+ ### 3) Implement a native Qt widget (C/C++) per OS
122
+ Develop a Qt widget (usable from **PySide6**) that embeds the system’s web engine:
123
+
124
+ - **Windows →** Edge WebView2
125
+ - **macOS →** WKWebView
126
+ - **Linux →** WebKitGTK (with **GStreamer** for codecs)
127
+
128
+ **Advantages**
129
+ - **No redistribution licensing**: leverage the codecs already provided by the OS.
130
+ - A **common API** can be exposed to PySide6.
131
+ - **More control** than QtWebView, without QML’s limitations.
132
+
133
+ **Disadvantages**
134
+ - **Medium‑to‑high complexity** to implement.
135
+ - Requires **C++** and, on macOS, **Objective‑C++**.
136
+ - Requires **custom CMake** to include libraries and linking.
137
+
138
+ ---
@@ -0,0 +1,249 @@
1
+ cmake_minimum_required(VERSION 3.21)
2
+
3
+ # Python + Shiboken6 + PySide6
4
+ find_package(Python3 REQUIRED COMPONENTS Interpreter Development.Module)
5
+ find_program(SHIBOKEN6_GEN shiboken6 REQUIRED)
6
+
7
+ # =============== Qt headers (solo SDK, via aqt) ==================
8
+ # Passa -DQt6_DIR=/path/to/Qt/6.9.0/macos/lib/cmake/Qt6
9
+ find_package(Qt6 6.7 REQUIRED COMPONENTS Core Gui Widgets)
10
+
11
+ # Deriva la cartella frameworks (…/macos/lib) a partire da Qt6_DIR
12
+ get_filename_component(_qt_cmake_dir "${Qt6_DIR}" DIRECTORY) # …/macos/lib/cmake
13
+ get_filename_component(QT_FRAMEWORKS_DIR "${_qt_cmake_dir}" DIRECTORY) # …/macos/lib
14
+
15
+ # Verifica headers Qt (SDK)
16
+ foreach(_mod QtCore QtGui QtWidgets)
17
+ if(NOT EXISTS "${QT_FRAMEWORKS_DIR}/${_mod}.framework/Headers")
18
+ message(FATAL_ERROR "Missing ${_mod}.framework/Headers under: ${QT_FRAMEWORKS_DIR}")
19
+ endif()
20
+ endforeach()
21
+
22
+ # Shim uniforme per includere QtCore/QtGui/QtWidgets
23
+ set(QT_HDRSHIM "${CMAKE_BINARY_DIR}/qt_hdrshim")
24
+ file(MAKE_DIRECTORY "${QT_HDRSHIM}")
25
+ file(CREATE_LINK "${QT_FRAMEWORKS_DIR}/QtCore.framework/Headers" "${QT_HDRSHIM}/QtCore" SYMBOLIC)
26
+ file(CREATE_LINK "${QT_FRAMEWORKS_DIR}/QtGui.framework/Headers" "${QT_HDRSHIM}/QtGui" SYMBOLIC)
27
+ file(CREATE_LINK "${QT_FRAMEWORKS_DIR}/QtWidgets.framework/Headers" "${QT_HDRSHIM}/QtWidgets" SYMBOLIC)
28
+
29
+ # =============== PySide6 runtime (da site-packages) ===============
30
+ # Dir dei frameworks Qt nel venv (PySide6/Qt/lib)
31
+ if(NOT DEFINED PYSIDE_QT_LIBDIR)
32
+ execute_process(
33
+ COMMAND ${Python3_EXECUTABLE} -c "import os, PySide6; print(os.path.join(os.path.dirname(PySide6.__file__), 'Qt', 'lib'))"
34
+ OUTPUT_VARIABLE PYSIDE_QT_LIBDIR
35
+ OUTPUT_STRIP_TRAILING_WHITESPACE
36
+ )
37
+ endif()
38
+ if(NOT EXISTS "${PYSIDE_QT_LIBDIR}")
39
+ message(FATAL_ERROR "PySide6 Qt frameworks not found: ${PYSIDE_QT_LIBDIR}")
40
+ endif()
41
+
42
+ # Binari framework da PySide6 (linker)
43
+ set(QTCORE_BIN "${PYSIDE_QT_LIBDIR}/QtCore.framework/Versions/A/QtCore")
44
+ set(QTGUI_BIN "${PYSIDE_QT_LIBDIR}/QtGui.framework/Versions/A/QtGui")
45
+ set(QTWIDGETS_BIN "${PYSIDE_QT_LIBDIR}/QtWidgets.framework/Versions/A/QtWidgets")
46
+ foreach(_f ${QTCORE_BIN} ${QTGUI_BIN} ${QTWIDGETS_BIN})
47
+ if(NOT EXISTS "${_f}")
48
+ message(FATAL_ERROR "Framework binary not found: ${_f}")
49
+ endif()
50
+ endforeach()
51
+
52
+ # Trova anche i frameworks per completezza (stessa cartella)
53
+ find_library(QTCORE_FW NAMES QtCore PATHS "${PYSIDE_QT_LIBDIR}" NO_DEFAULT_PATH REQUIRED)
54
+ find_library(QTGUI_FW NAMES QtGui PATHS "${PYSIDE_QT_LIBDIR}" NO_DEFAULT_PATH REQUIRED)
55
+ find_library(QTWIDGETS_FW NAMES QtWidgets PATHS "${PYSIDE_QT_LIBDIR}" NO_DEFAULT_PATH REQUIRED)
56
+
57
+ # =============== Typesystem & includes Shiboken/PySide6 ==========
58
+ execute_process(
59
+ COMMAND ${Python3_EXECUTABLE} -c "import os,shiboken6;print(os.path.join(os.path.dirname(shiboken6.__file__),'typesystems'))"
60
+ OUTPUT_VARIABLE SHIBOKEN_TYPESYS
61
+ OUTPUT_STRIP_TRAILING_WHITESPACE
62
+ )
63
+ execute_process(
64
+ COMMAND ${Python3_EXECUTABLE} -c "import os,PySide6;print(os.path.join(os.path.dirname(PySide6.__file__),'typesystems'))"
65
+ OUTPUT_VARIABLE PYSIDE_TYPESYS
66
+ OUTPUT_STRIP_TRAILING_WHITESPACE
67
+ )
68
+
69
+ # Shiboken include (runtime, poi fallback al generator)
70
+ execute_process(
71
+ COMMAND ${Python3_EXECUTABLE} -c "import os,importlib; m=importlib.import_module('shiboken6'); print(os.path.join(os.path.dirname(m.__file__),'include'))"
72
+ OUTPUT_VARIABLE SHIBOKEN_INCLUDE_DIR
73
+ OUTPUT_STRIP_TRAILING_WHITESPACE
74
+ )
75
+ if(NOT EXISTS "${SHIBOKEN_INCLUDE_DIR}/shiboken.h")
76
+ execute_process(
77
+ COMMAND ${Python3_EXECUTABLE} -c "import os,importlib; m=importlib.import_module('shiboken6_generator'); print(os.path.join(os.path.dirname(m.__file__),'include'))"
78
+ OUTPUT_VARIABLE SHIBOKEN_INCLUDE_DIR
79
+ OUTPUT_STRIP_TRAILING_WHITESPACE
80
+ )
81
+ endif()
82
+ if(NOT EXISTS "${SHIBOKEN_INCLUDE_DIR}/shiboken.h")
83
+ message(FATAL_ERROR "Cannot find shiboken.h in shiboken6/include nor shiboken6_generator/include.")
84
+ endif()
85
+ message(STATUS "Shiboken include dir: ${SHIBOKEN_INCLUDE_DIR}")
86
+
87
+ # Shiboken runtime library
88
+ execute_process(
89
+ COMMAND ${Python3_EXECUTABLE} -c "import os,glob,importlib; d=os.path.dirname(importlib.import_module('shiboken6').__file__); m=glob.glob(os.path.join(d,'libshiboken*.dylib')) or glob.glob(os.path.join(d,'libshiboken*.so*')); print(m[0] if m else '')"
90
+ OUTPUT_VARIABLE SHIBOKEN_LIBRARY
91
+ OUTPUT_STRIP_TRAILING_WHITESPACE
92
+ )
93
+ if(NOT SHIBOKEN_LIBRARY)
94
+ execute_process(
95
+ COMMAND ${Python3_EXECUTABLE} -c "import os,glob,importlib; d=os.path.dirname(importlib.import_module('PySide6').__file__); m=glob.glob(os.path.join(d,'libshiboken*.dylib')) or glob.glob(os.path.join(d,'libshiboken*.so*')); print(m[0] if m else '')"
96
+ OUTPUT_VARIABLE SHIBOKEN_LIBRARY
97
+ OUTPUT_STRIP_TRAILING_WHITESPACE
98
+ )
99
+ endif()
100
+ if(NOT SHIBOKEN_LIBRARY)
101
+ message(FATAL_ERROR "libshiboken*.dylib/.so not found in the venv.")
102
+ endif()
103
+ message(STATUS "Shiboken library: ${SHIBOKEN_LIBRARY}")
104
+
105
+ # PySide6 include & lib
106
+ execute_process(
107
+ COMMAND ${Python3_EXECUTABLE} -c "import os, PySide6; print(os.path.join(os.path.dirname(PySide6.__file__), 'include'))"
108
+ OUTPUT_VARIABLE PYSIDE_INCLUDE_DIR
109
+ OUTPUT_STRIP_TRAILING_WHITESPACE
110
+ )
111
+ execute_process(
112
+ COMMAND ${Python3_EXECUTABLE} -c "import os, glob, PySide6 as P; d=os.path.dirname(P.__file__); m=sorted(glob.glob(os.path.join(d,'libpyside6.abi3*.dylib'))+glob.glob(os.path.join(d,'libpyside6.abi3*.so*'))); print(m[0] if m else '')"
113
+ OUTPUT_VARIABLE PYSIDE_LIBRARY
114
+ OUTPUT_STRIP_TRAILING_WHITESPACE
115
+ )
116
+ if(NOT PYSIDE_LIBRARY)
117
+ message(FATAL_ERROR "libpyside6.abi3*.dylib not found in the PySide6 venv.")
118
+ endif()
119
+ message(STATUS "PySide library : ${PYSIDE_LIBRARY}")
120
+ get_filename_component(PYSIDE_LIB_DIR "${PYSIDE_LIBRARY}" DIRECTORY)
121
+
122
+ # Include per shiboken (SDK Qt + tuoi header + shiboken + PySide6)
123
+ set(SHIBOKEN_INCLUDE_LIST
124
+ "${QT_HDRSHIM}" # QtCore/QtGui/QtWidgets headers
125
+ "${PROJECT_SOURCE_DIR}/src/macos" # tuoi header nativi
126
+ "${SHIBOKEN_INCLUDE_DIR}" # shiboken.h
127
+ "${QT_FRAMEWORKS_DIR}/QtCore.framework/Headers"
128
+ "${QT_FRAMEWORKS_DIR}/QtGui.framework/Headers"
129
+ "${QT_FRAMEWORKS_DIR}/QtWidgets.framework/Headers"
130
+ )
131
+ if(EXISTS "${PYSIDE_INCLUDE_DIR}")
132
+ list(APPEND SHIBOKEN_INCLUDE_LIST
133
+ "${PYSIDE_INCLUDE_DIR}"
134
+ "${PYSIDE_INCLUDE_DIR}/QtCore"
135
+ "${PYSIDE_INCLUDE_DIR}/QtGui"
136
+ "${PYSIDE_INCLUDE_DIR}/QtWidgets"
137
+ )
138
+ endif()
139
+ list(JOIN SHIBOKEN_INCLUDE_LIST ":" SHIBOKEN_INCLUDE_ARG)
140
+ message(STATUS "Shiboken include paths:\n ${SHIBOKEN_INCLUDE_ARG}")
141
+
142
+ # =============== Shiboken: codegen ================================
143
+ set(GEN_DIR ${CMAKE_CURRENT_BINARY_DIR}/shiboken_out)
144
+ file(MAKE_DIRECTORY ${GEN_DIR})
145
+
146
+ set(HEADERS "${PROJECT_SOURCE_DIR}/src/macos/WKWebViewWidget.h")
147
+ set(TYPESYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/typesystem_wkwebview.xml")
148
+
149
+ message(STATUS "Running shiboken6: output -> ${GEN_DIR}")
150
+ execute_process(
151
+ COMMAND "${SHIBOKEN6_GEN}"
152
+ --generator-set=shiboken
153
+ --enable-pyside-extensions
154
+ --include-paths=${SHIBOKEN_INCLUDE_ARG}
155
+ --typesystem-paths=${SHIBOKEN_TYPESYS}
156
+ --typesystem-paths=${PYSIDE_TYPESYS}
157
+ --output-directory=${GEN_DIR}
158
+ ${HEADERS}
159
+ ${TYPESYSTEM}
160
+ RESULT_VARIABLE SHIBOKEN_RV
161
+ OUTPUT_VARIABLE SHIBOKEN_STDOUT
162
+ ERROR_VARIABLE SHIBOKEN_STDERR
163
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
164
+ )
165
+ if (NOT SHIBOKEN_RV EQUAL 0)
166
+ message(STATUS "Shiboken stdout:\n${SHIBOKEN_STDOUT}")
167
+ message(STATUS "Shiboken stderr:\n${SHIBOKEN_STDERR}")
168
+ message(FATAL_ERROR "Shiboken generation failed with code ${SHIBOKEN_RV}")
169
+ endif()
170
+
171
+ file(GLOB_RECURSE SHIBOKEN_SRCS ${GEN_DIR}/*.cpp)
172
+ if (SHIBOKEN_SRCS STREQUAL "")
173
+ message(FATAL_ERROR "No sources generated in ${GEN_DIR}")
174
+ endif()
175
+
176
+ # =============== Estensione Python ================================
177
+ add_library(_wkwebview MODULE ${SHIBOKEN_SRCS})
178
+ target_compile_features(_wkwebview PRIVATE cxx_std_17)
179
+
180
+ # Installa l’estensione nel pacchetto Python
181
+ install(TARGETS _wkwebview
182
+ LIBRARY DESTINATION fit_webview_bridge
183
+ RUNTIME DESTINATION fit_webview_bridge
184
+ )
185
+
186
+ # Suffisso Python (es. .cpython-311-darwin.so)
187
+ execute_process(
188
+ COMMAND ${Python3_EXECUTABLE} -c "import sysconfig; print(sysconfig.get_config_var('EXT_SUFFIX') or '.so')"
189
+ OUTPUT_VARIABLE PY_EXT_SUFFIX
190
+ OUTPUT_STRIP_TRAILING_WHITESPACE
191
+ )
192
+
193
+ # Include & link
194
+ target_include_directories(_wkwebview PRIVATE
195
+ ${GEN_DIR}
196
+ "${PROJECT_SOURCE_DIR}/src/macos"
197
+ ${Python3_INCLUDE_DIRS}
198
+ ${SHIBOKEN_INCLUDE_DIR}
199
+ ${QT_HDRSHIM}
200
+ )
201
+ if(EXISTS "${PYSIDE_INCLUDE_DIR}")
202
+ target_include_directories(_wkwebview PRIVATE
203
+ "${PYSIDE_INCLUDE_DIR}"
204
+ "${PYSIDE_INCLUDE_DIR}/QtCore"
205
+ "${PYSIDE_INCLUDE_DIR}/QtGui"
206
+ "${PYSIDE_INCLUDE_DIR}/QtWidgets"
207
+ )
208
+ endif()
209
+
210
+ # CMake deve cercare nelle dir dei framework di PySide6
211
+ target_link_directories(_wkwebview PRIVATE "${PYSIDE_QT_LIBDIR}")
212
+
213
+ # Linka CONTRO i binari di PySide6 + shiboken + Python + lib nativa
214
+ target_link_libraries(_wkwebview PRIVATE
215
+ "${QTCORE_BIN}"
216
+ "${QTGUI_BIN}"
217
+ "${QTWIDGETS_BIN}"
218
+ "${SHIBOKEN_LIBRARY}"
219
+ "${PYSIDE_LIBRARY}"
220
+ Python3::Module
221
+ wkwebview
222
+ )
223
+
224
+ # =============== RPATH per runtime su macOS =======================
225
+ get_filename_component(SHIBOKEN_LIB_DIR "${SHIBOKEN_LIBRARY}" DIRECTORY)
226
+ option(FITWVB_VENDORIZE "Vendorize PySide/Qt lookup via @loader_path" ON)
227
+
228
+ set_target_properties(_wkwebview PROPERTIES
229
+ PREFIX "" OUTPUT_NAME "wkwebview" SUFFIX "${PY_EXT_SUFFIX}"
230
+ MACOSX_RPATH ON BUILD_WITH_INSTALL_RPATH ON
231
+ )
232
+
233
+ if(FITWVB_VENDORIZE)
234
+ set_target_properties(_wkwebview PROPERTIES
235
+ LIBRARY_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/fit_webview_bridge"
236
+ )
237
+ set_property(TARGET _wkwebview APPEND PROPERTY BUILD_RPATH
238
+ "@loader_path" "@loader_path/../PySide6" "@loader_path/../PySide6/Qt/lib" "@loader_path/../shiboken6")
239
+ set_property(TARGET _wkwebview APPEND PROPERTY INSTALL_RPATH
240
+ "@loader_path" "@loader_path/../PySide6" "@loader_path/../PySide6/Qt/lib" "@loader_path/../shiboken6")
241
+ else()
242
+ set_target_properties(_wkwebview PROPERTIES
243
+ LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}
244
+ )
245
+ set_property(TARGET _wkwebview APPEND PROPERTY BUILD_RPATH
246
+ "@loader_path" "${PYSIDE_QT_LIBDIR}" "${SHIBOKEN_LIB_DIR}" "${PYSIDE_LIB_DIR}")
247
+ set_property(TARGET _wkwebview APPEND PROPERTY INSTALL_RPATH
248
+ "@loader_path" "${PYSIDE_QT_LIBDIR}" "${SHIBOKEN_LIB_DIR}" "${PYSIDE_LIB_DIR}")
249
+ endif()
@@ -0,0 +1,12 @@
1
+ <?xml version="1.0"?>
2
+ <typesystem package="wkwebview">
3
+ <load-typesystem name="typesystem_core.xml" generate="no"/>
4
+ <load-typesystem name="typesystem_gui.xml" generate="no"/>
5
+ <load-typesystem name="typesystem_widgets.xml" generate="no"/>
6
+
7
+ <object-type name="WKWebViewWidget">
8
+ <include file-name="WKWebViewWidget.h" location="global"/>
9
+ </object-type>
10
+ </typesystem>
11
+
12
+
@@ -0,0 +1,37 @@
1
+ import os, sys
2
+ ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
3
+ sys.path[:0] = [os.path.join(ROOT, "build"),
4
+ os.path.join(ROOT, "build", "bindings", "shiboken_out")]
5
+
6
+
7
+ from PySide6.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout
8
+ from fit_webview_bridge import SystemWebView
9
+ # tentativo 1: pacchetto generato da shiboken (wkwebview)
10
+ try:
11
+ import wkwebview
12
+ WKWebViewWidget = wkwebview.WKWebViewWidget # accesso via attributo
13
+ except Exception:
14
+ # tentativo 2: modulo nativo diretto
15
+ from _wkwebview import WKWebViewWidget
16
+
17
+
18
+ class Main(QMainWindow):
19
+ def __init__(self):
20
+ super().__init__()
21
+ central = QWidget(self)
22
+ lay = QVBoxLayout(central)
23
+ self.view = WKWebViewWidget()
24
+ lay.addWidget(self.view)
25
+ self.setCentralWidget(central)
26
+
27
+ # segnali utili
28
+ self.view.titleChanged.connect(self.setWindowTitle)
29
+ self.view.loadProgress.connect(lambda p: print("progress:", p))
30
+
31
+ self.view.load("https://www.facebook.com/reel/574839388710756/")
32
+
33
+ app = QApplication([])
34
+ m = Main()
35
+ m.resize(1200, 800)
36
+ m.show()
37
+ app.exec()
@@ -0,0 +1 @@
1
+ from .wkwebview import WKWebViewWidget as SystemWebView
@@ -0,0 +1,33 @@
1
+ [project]
2
+ name = "fit-webview-bridge"
3
+ version = "0.1.1a5"
4
+ description = "Qt native WebView bridge with PySide6 bindings"
5
+ requires-python = ">=3.11,<3.14"
6
+ dependencies = ["PySide6==6.9.0", "shiboken6==6.9.0", "shiboken6-generator==6.9.0"]
7
+ readme = "README.md"
8
+ license = {text = "LGPL-3.0-or-later"}
9
+ authors = [{ name = "FIT Project" }]
10
+ urls = { Homepage = "https://github.com/fit-project/fit-webview-bridge" }
11
+
12
+ [build-system]
13
+ requires = [
14
+ "scikit-build-core>=0.9",
15
+ "cmake>=3.26",
16
+ "ninja",
17
+ "PySide6==6.9.0",
18
+ "shiboken6==6.9.0",
19
+ "shiboken6-generator==6.9.0"
20
+ ]
21
+ build-backend = "scikit_build_core.build"
22
+
23
+ [tool.scikit-build]
24
+ wheel.packages = ["fit_webview_bridge"]
25
+ cmake.build-type = "Release"
26
+ cmake.define = { FITWVB_VENDORIZE = "ON" }
27
+
28
+ [tool.cibuildwheel]
29
+ build = ["cp311-*", "cp312-*", "cp313-*"]
30
+
31
+ [tool.cibuildwheel.macos]
32
+ archs = ["arm64"]
33
+ repair-wheel-command = ""
@@ -0,0 +1,49 @@
1
+ cmake_minimum_required(VERSION 3.21)
2
+ enable_language(OBJCXX)
3
+ set(CMAKE_CXX_STANDARD 17)
4
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
5
+ set(CMAKE_OBJCXX_STANDARD 17)
6
+ set(CMAKE_OBJCXX_STANDARD_REQUIRED ON)
7
+ set(CMAKE_AUTOMOC ON)
8
+
9
+ # Qt da aqt: serve solo per moc + headers
10
+ # Passa -DQt6_DIR=/path/to/Qt/6.9.0/macos/lib/cmake/Qt6
11
+ find_package(Qt6 6.7 REQUIRED COMPONENTS Core Gui Widgets)
12
+
13
+ # Deriva la cartella dei frameworks (…/macos/lib) da Qt6_DIR
14
+ get_filename_component(_qt_cmake_dir "${Qt6_DIR}" DIRECTORY) # …/macos/lib/cmake
15
+ get_filename_component(QT_FRAMEWORKS_DIR "${_qt_cmake_dir}" DIRECTORY) # …/macos/lib
16
+
17
+ # Verifica headers
18
+ foreach(_m QtCore QtGui QtWidgets)
19
+ if(NOT EXISTS "${QT_FRAMEWORKS_DIR}/${_m}.framework/Headers")
20
+ message(FATAL_ERROR "Missing ${_m}.framework/Headers in: ${QT_FRAMEWORKS_DIR}")
21
+ endif()
22
+ endforeach()
23
+
24
+ # Shim include uniforme
25
+ set(QT_HDRSHIM "${CMAKE_BINARY_DIR}/qt_hdrshim")
26
+ file(MAKE_DIRECTORY "${QT_HDRSHIM}")
27
+ file(CREATE_LINK "${QT_FRAMEWORKS_DIR}/QtCore.framework/Headers" "${QT_HDRSHIM}/QtCore" SYMBOLIC)
28
+ file(CREATE_LINK "${QT_FRAMEWORKS_DIR}/QtGui.framework/Headers" "${QT_HDRSHIM}/QtGui" SYMBOLIC)
29
+ file(CREATE_LINK "${QT_FRAMEWORKS_DIR}/QtWidgets.framework/Headers" "${QT_HDRSHIM}/QtWidgets" SYMBOLIC)
30
+
31
+ add_library(wkwebview STATIC
32
+ WKWebViewWidget.mm
33
+ WKWebViewWidget.h
34
+ )
35
+ target_compile_features(wkwebview PRIVATE cxx_std_17)
36
+
37
+ target_include_directories(wkwebview PUBLIC
38
+ ${CMAKE_CURRENT_SOURCE_DIR}
39
+ "${QT_FRAMEWORKS_DIR}/QtCore.framework/Headers"
40
+ "${QT_FRAMEWORKS_DIR}/QtGui.framework/Headers"
41
+ "${QT_FRAMEWORKS_DIR}/QtWidgets.framework/Headers"
42
+ ${QT_HDRSHIM}
43
+ )
44
+
45
+ # Link SOLO framework di sistema (Cocoa/WebKit). Niente Qt qui.
46
+ target_link_libraries(wkwebview PUBLIC
47
+ "-framework Cocoa"
48
+ "-framework WebKit"
49
+ )
@@ -0,0 +1,34 @@
1
+ #pragma once
2
+ #include <QWidget>
3
+ #include <QObject>
4
+
5
+ class QString; class QUrl; class QShowEvent; class QResizeEvent;
6
+
7
+ class WKWebViewWidget : public QWidget {
8
+ Q_OBJECT
9
+ public:
10
+ explicit WKWebViewWidget(QWidget* parent = nullptr);
11
+ ~WKWebViewWidget() override;
12
+
13
+ Q_INVOKABLE void load(const QString& url);
14
+ Q_INVOKABLE void back();
15
+ Q_INVOKABLE void forward();
16
+ Q_INVOKABLE void stop();
17
+ Q_INVOKABLE void reload();
18
+ Q_INVOKABLE void evaluateJavaScript(const QString& script);
19
+
20
+ signals:
21
+ void loadFinished(bool ok);
22
+ void urlChanged(const QUrl& url);
23
+ void titleChanged(const QString& title);
24
+ void loadProgress(int percent);
25
+ void canGoBackChanged(bool);
26
+ void canGoForwardChanged(bool);
27
+
28
+ protected:
29
+ void showEvent(QShowEvent*) override;
30
+ void resizeEvent(QResizeEvent*) override;
31
+
32
+ private:
33
+ struct Impl; Impl* d = nullptr;
34
+ };
@@ -0,0 +1,96 @@
1
+ #import <Cocoa/Cocoa.h>
2
+ #import <WebKit/WebKit.h>
3
+
4
+ #include "WKWebViewWidget.h"
5
+
6
+ #include <QtWidgets>
7
+ #include <QString>
8
+ #include <QUrl>
9
+
10
+ @class WKNavDelegate;
11
+
12
+ struct WKWebViewWidget::Impl {
13
+ WKWebView* wk = nil;
14
+ WKNavDelegate* delegate = nil;
15
+ };
16
+
17
+ @interface WKNavDelegate : NSObject <WKNavigationDelegate>
18
+ @property(nonatomic, assign) WKWebViewWidget* owner;
19
+ @end
20
+
21
+ @implementation WKNavDelegate
22
+ - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
23
+ if (!self.owner) return;
24
+ emit self.owner->loadFinished(true);
25
+ if (webView.URL) emit self.owner->urlChanged(QUrl(QString::fromUtf8(webView.URL.absoluteString.UTF8String)));
26
+ if (webView.title) emit self.owner->titleChanged(QString::fromUtf8(webView.title.UTF8String));
27
+ emit self.owner->loadProgress(100);
28
+ emit self.owner->canGoBackChanged(webView.canGoBack);
29
+ emit self.owner->canGoForwardChanged(webView.canGoForward);
30
+ }
31
+ - (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error {
32
+ if (!self.owner) return;
33
+ emit self.owner->loadFinished(false);
34
+ emit self.owner->loadProgress(0);
35
+ emit self.owner->canGoBackChanged(webView.canGoBack);
36
+ emit self.owner->canGoForwardChanged(webView.canGoForward);
37
+ }
38
+ - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
39
+ if (self.owner) emit self.owner->loadProgress(5);
40
+ }
41
+ - (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {
42
+ if (self.owner) emit self.owner->loadProgress(50);
43
+ }
44
+ @end
45
+
46
+ static NSURL* toNSURL(QString u) {
47
+ u = u.trimmed();
48
+ if (!u.startsWith("http://") && !u.startsWith("https://")) u = "https://" + u;
49
+ return [NSURL URLWithString:[NSString stringWithUTF8String:u.toUtf8().constData()]];
50
+ }
51
+
52
+ WKWebViewWidget::WKWebViewWidget(QWidget* parent)
53
+ : QWidget(parent), d(new Impl) {
54
+ setAttribute(Qt::WA_NativeWindow, true);
55
+ (void)winId();
56
+
57
+ NSView* nsParent = (__bridge NSView*)reinterpret_cast<void*>(winId());
58
+ WKWebViewConfiguration* cfg = [[WKWebViewConfiguration alloc] init];
59
+ if ([cfg respondsToSelector:@selector(defaultWebpagePreferences)]) {
60
+ cfg.defaultWebpagePreferences.allowsContentJavaScript = YES;
61
+ }
62
+ d->wk = [[WKWebView alloc] initWithFrame:nsParent.bounds configuration:cfg];
63
+ d->wk.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
64
+ [nsParent addSubview:d->wk];
65
+
66
+ d->delegate = [WKNavDelegate new];
67
+ d->delegate.owner = this;
68
+ [d->wk setNavigationDelegate:d->delegate];
69
+
70
+ load(QStringLiteral("https://example.org"));
71
+ }
72
+
73
+ WKWebViewWidget::~WKWebViewWidget() {
74
+ if (!d) return;
75
+ if (d->wk) { [d->wk removeFromSuperview]; d->wk = nil; }
76
+ d->delegate = nil;
77
+ delete d; d = nullptr;
78
+ }
79
+
80
+ void WKWebViewWidget::showEvent(QShowEvent* e) { QWidget::showEvent(e); /* opzionale */ }
81
+ void WKWebViewWidget::resizeEvent(QResizeEvent* e) { QWidget::resizeEvent(e); /* opzionale */ }
82
+
83
+ void WKWebViewWidget::load(const QString& url) {
84
+ if (d && d->wk) [d->wk loadRequest:[NSURLRequest requestWithURL:toNSURL(url)]];
85
+ }
86
+ void WKWebViewWidget::back() { if (d && d->wk && d->wk.canGoBack) [d->wk goBack]; }
87
+ void WKWebViewWidget::forward() { if (d && d->wk && d->wk.canGoForward) [d->wk goForward]; }
88
+ void WKWebViewWidget::stop() { if (d && d->wk) [d->wk stopLoading:nil]; }
89
+ void WKWebViewWidget::reload() { if (d && d->wk) [d->wk reload]; }
90
+ void WKWebViewWidget::evaluateJavaScript(const QString& script) {
91
+ if (!d || !d->wk) return;
92
+ NSString* s = [NSString stringWithUTF8String:script.toUtf8().constData()];
93
+ [d->wk evaluateJavaScript:s completionHandler:^(id result, NSError* error){
94
+ Q_UNUSED(result); Q_UNUSED(error);
95
+ }];
96
+ }