fit-webview-bridge 0.2.1a5__tar.gz → 0.2.2a4__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.
- {fit_webview_bridge-0.2.1a5 → fit_webview_bridge-0.2.2a4}/PKG-INFO +1 -1
- {fit_webview_bridge-0.2.1a5 → fit_webview_bridge-0.2.2a4}/bindings/pyside6/macos/typesystem_wkwebview.xml +3 -0
- {fit_webview_bridge-0.2.1a5 → fit_webview_bridge-0.2.2a4}/pyproject.toml +1 -1
- {fit_webview_bridge-0.2.1a5 → fit_webview_bridge-0.2.2a4}/src/macos/CMakeLists.txt +1 -0
- fit_webview_bridge-0.2.2a4/src/macos/DownloadInfo.h +26 -0
- {fit_webview_bridge-0.2.1a5 → fit_webview_bridge-0.2.2a4}/src/macos/WKWebViewWidget.h +3 -1
- {fit_webview_bridge-0.2.1a5 → fit_webview_bridge-0.2.2a4}/src/macos/WKWebViewWidget.mm +99 -6
- {fit_webview_bridge-0.2.1a5 → fit_webview_bridge-0.2.2a4}/.github/workflows/wheels-macos.yml +0 -0
- {fit_webview_bridge-0.2.1a5 → fit_webview_bridge-0.2.2a4}/.gitignore +0 -0
- {fit_webview_bridge-0.2.1a5 → fit_webview_bridge-0.2.2a4}/.vscode/settings.json +0 -0
- {fit_webview_bridge-0.2.1a5 → fit_webview_bridge-0.2.2a4}/CMakeLists.txt +0 -0
- {fit_webview_bridge-0.2.1a5 → fit_webview_bridge-0.2.2a4}/README.md +0 -0
- {fit_webview_bridge-0.2.1a5 → fit_webview_bridge-0.2.2a4}/bindings/pyside6/macos/CMakeLists.txt +0 -0
- {fit_webview_bridge-0.2.1a5 → fit_webview_bridge-0.2.2a4}/examples/macos/wkwebview_demo.py +0 -0
- {fit_webview_bridge-0.2.1a5 → fit_webview_bridge-0.2.2a4}/fit_webview_bridge/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "fit-webview-bridge"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.2a4"
|
|
4
4
|
description = "Qt native WebView bridge with PySide6 bindings"
|
|
5
5
|
requires-python = ">=3.11,<3.14"
|
|
6
6
|
dependencies = ["PySide6==6.9.0", "shiboken6==6.9.0", "shiboken6-generator==6.9.0"]
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
#include <QObject>
|
|
3
|
+
#include <QString>
|
|
4
|
+
#include <QUrl>
|
|
5
|
+
|
|
6
|
+
class DownloadInfo : public QObject {
|
|
7
|
+
Q_OBJECT
|
|
8
|
+
Q_PROPERTY(QString downloadFileName READ downloadFileName CONSTANT)
|
|
9
|
+
Q_PROPERTY(QString downloadDirectory READ downloadDirectory CONSTANT)
|
|
10
|
+
Q_PROPERTY(QUrl downloadUrl READ downloadUrl CONSTANT)
|
|
11
|
+
public:
|
|
12
|
+
explicit DownloadInfo(const QString& fileName,
|
|
13
|
+
const QString& directory,
|
|
14
|
+
const QUrl& url,
|
|
15
|
+
QObject* parent=nullptr)
|
|
16
|
+
: QObject(parent), m_fileName(fileName), m_directory(directory), m_url(url) {}
|
|
17
|
+
|
|
18
|
+
QString downloadFileName() const { return m_fileName; }
|
|
19
|
+
QString downloadDirectory() const { return m_directory; }
|
|
20
|
+
QUrl downloadUrl() const { return m_url; }
|
|
21
|
+
|
|
22
|
+
private:
|
|
23
|
+
QString m_fileName;
|
|
24
|
+
QString m_directory;
|
|
25
|
+
QUrl m_url;
|
|
26
|
+
};
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
#include <QObject>
|
|
4
4
|
#include <QUrl>
|
|
5
5
|
|
|
6
|
+
#include "DownloadInfo.h"
|
|
7
|
+
|
|
6
8
|
class QString; class QShowEvent; class QResizeEvent;
|
|
7
9
|
|
|
8
10
|
class WKWebViewWidget : public QWidget {
|
|
@@ -36,7 +38,7 @@ signals:
|
|
|
36
38
|
|
|
37
39
|
void downloadStarted(const QString& suggestedFilename, const QString& destinationPath);
|
|
38
40
|
void downloadProgress(qint64 bytesReceived, qint64 totalBytes);
|
|
39
|
-
void downloadFinished(
|
|
41
|
+
void downloadFinished(DownloadInfo* info);
|
|
40
42
|
void downloadFailed(const QString& filePath, const QString& error);
|
|
41
43
|
|
|
42
44
|
protected:
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
#import <Cocoa/Cocoa.h>
|
|
2
2
|
#import <WebKit/WebKit.h>
|
|
3
|
+
#import <objc/message.h>
|
|
3
4
|
|
|
4
5
|
#include "WKWebViewWidget.h"
|
|
6
|
+
#include "DownloadInfo.h"
|
|
7
|
+
|
|
5
8
|
|
|
6
9
|
#include <QtWidgets>
|
|
7
10
|
#include <QString>
|
|
@@ -46,13 +49,15 @@ static NSURL* toNSURL(QUrl u);
|
|
|
46
49
|
@end
|
|
47
50
|
|
|
48
51
|
// ===== WKNavDelegate =====
|
|
49
|
-
@interface WKNavDelegate : NSObject <WKNavigationDelegate, WKDownloadDelegate>
|
|
52
|
+
@interface WKNavDelegate : NSObject <WKNavigationDelegate, WKDownloadDelegate, WKUIDelegate>
|
|
50
53
|
@property(nonatomic, assign) WKWebViewWidget* owner;
|
|
51
54
|
// mappe per download
|
|
52
55
|
@property(nonatomic, strong) NSMapTable<WKDownload*, NSString*>* downloadPaths; // weak key -> strong value
|
|
53
56
|
@property(nonatomic, strong) NSMapTable<NSProgress*, WKDownload*>* progressToDownload; // weak->weak
|
|
54
57
|
@property(nonatomic, strong) NSHashTable<NSProgress*>* completedProgresses; // weak set
|
|
55
58
|
@property(nonatomic, strong) NSMapTable<WKDownload*, NSNumber*>* expectedTotals; // weak->strong
|
|
59
|
+
@property(nonatomic, strong) NSMapTable<WKDownload*, NSURL*>* sourceURLs; // weak->strong
|
|
60
|
+
@property(nonatomic, strong) NSMapTable<WKDownload*, NSString*>* suggestedNames; // weak->strong
|
|
56
61
|
@end
|
|
57
62
|
|
|
58
63
|
@implementation WKNavDelegate
|
|
@@ -63,12 +68,42 @@ static NSURL* toNSURL(QUrl u);
|
|
|
63
68
|
_progressToDownload = [NSMapTable weakToWeakObjectsMapTable];
|
|
64
69
|
_completedProgresses = [NSHashTable weakObjectsHashTable];
|
|
65
70
|
_expectedTotals = [NSMapTable weakToStrongObjectsMapTable];
|
|
71
|
+
_sourceURLs = [NSMapTable weakToStrongObjectsMapTable];
|
|
72
|
+
_suggestedNames = [NSMapTable weakToStrongObjectsMapTable];
|
|
66
73
|
}
|
|
67
74
|
return self;
|
|
68
75
|
}
|
|
69
76
|
|
|
70
77
|
#pragma mark - Navigazione
|
|
71
78
|
|
|
79
|
+
|
|
80
|
+
// 1a) Navigation: intercetta click con targetFrame == nil (tipico di _blank)
|
|
81
|
+
- (void)webView:(WKWebView *)webView
|
|
82
|
+
decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction
|
|
83
|
+
decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
|
|
84
|
+
{
|
|
85
|
+
// targetFrame == nil => richiesta per nuova finestra (target="_blank" o window.open)
|
|
86
|
+
if (navigationAction.targetFrame == nil || !navigationAction.targetFrame.isMainFrame) {
|
|
87
|
+
[webView loadRequest:navigationAction.request]; // carica QUI
|
|
88
|
+
decisionHandler(WKNavigationActionPolicyCancel); // cancella la creazione nuova finestra
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
decisionHandler(WKNavigationActionPolicyAllow);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// 1b) UI: invocato quando la pagina chiede esplicitamente una nuova webview
|
|
95
|
+
- (WKWebView *)webView:(WKWebView *)webView
|
|
96
|
+
createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration
|
|
97
|
+
forNavigationAction:(WKNavigationAction *)navigationAction
|
|
98
|
+
windowFeatures:(WKWindowFeatures *)windowFeatures
|
|
99
|
+
{
|
|
100
|
+
if (navigationAction.targetFrame == nil || !navigationAction.targetFrame.isMainFrame) {
|
|
101
|
+
[webView loadRequest:navigationAction.request]; // apri nella stessa webview
|
|
102
|
+
}
|
|
103
|
+
return nil; // restituisci nil per NON creare una nuova finestra
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
|
|
72
107
|
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
|
|
73
108
|
if (!self.owner) return;
|
|
74
109
|
if (webView.URL)
|
|
@@ -133,6 +168,12 @@ navigationAction:(WKNavigationAction *)navigationAction
|
|
|
133
168
|
didBecomeDownload:(WKDownload *)download
|
|
134
169
|
{
|
|
135
170
|
download.delegate = self;
|
|
171
|
+
|
|
172
|
+
// URL sorgente (request dell’azione)
|
|
173
|
+
if (navigationAction.request.URL) {
|
|
174
|
+
[self.sourceURLs setObject:navigationAction.request.URL forKey:download];
|
|
175
|
+
}
|
|
176
|
+
|
|
136
177
|
if (self.owner) emit self.owner->downloadStarted(QString(), QString());
|
|
137
178
|
|
|
138
179
|
// KVO su NSProgress (3 keyPath, con INITIAL)
|
|
@@ -155,6 +196,10 @@ didBecomeDownload:(WKDownload *)download
|
|
|
155
196
|
{
|
|
156
197
|
download.delegate = self;
|
|
157
198
|
|
|
199
|
+
if (navigationResponse.response.URL) {
|
|
200
|
+
[self.sourceURLs setObject:navigationResponse.response.URL forKey:download];
|
|
201
|
+
}
|
|
202
|
+
|
|
158
203
|
NSString* suggested = navigationResponse.response.suggestedFilename ?: @"download";
|
|
159
204
|
if (self.owner) {
|
|
160
205
|
QString dir = self.owner->downloadDirectory();
|
|
@@ -228,6 +273,12 @@ completionHandler:(void (^)(NSURL * _Nullable destination))completionHandler
|
|
|
228
273
|
}
|
|
229
274
|
}
|
|
230
275
|
|
|
276
|
+
if (suggestedFilename) {
|
|
277
|
+
[self.suggestedNames setObject:suggestedFilename forKey:download];
|
|
278
|
+
} else if (![self.suggestedNames objectForKey:download]) {
|
|
279
|
+
[self.suggestedNames setObject:@"download" forKey:download];
|
|
280
|
+
}
|
|
281
|
+
|
|
231
282
|
completionHandler([NSURL fileURLWithPath:finalPath]);
|
|
232
283
|
}
|
|
233
284
|
|
|
@@ -269,43 +320,68 @@ completionHandler:(void (^)(NSURL * _Nullable destination))completionHandler
|
|
|
269
320
|
- (void)downloadDidFinish:(WKDownload *)download {
|
|
270
321
|
if (!self.owner) return;
|
|
271
322
|
|
|
323
|
+
// 1) stop KVO
|
|
272
324
|
@try {
|
|
273
325
|
[download.progress removeObserver:self forKeyPath:@"fractionCompleted"];
|
|
274
326
|
[download.progress removeObserver:self forKeyPath:@"completedUnitCount"];
|
|
275
327
|
[download.progress removeObserver:self forKeyPath:@"totalUnitCount"];
|
|
276
328
|
} @catch (...) {}
|
|
277
329
|
|
|
330
|
+
// 2) marca come completato per filtrare update tardivi
|
|
278
331
|
[self.completedProgresses addObject:download.progress];
|
|
279
332
|
|
|
280
|
-
|
|
333
|
+
// 3) raccogli dati
|
|
281
334
|
NSString* finalPath = [self.downloadPaths objectForKey:download];
|
|
282
|
-
|
|
335
|
+
NSString* fname = [self.suggestedNames objectForKey:download];
|
|
336
|
+
if (!fname && finalPath) fname = [finalPath lastPathComponent];
|
|
337
|
+
NSString* dir = finalPath ? [finalPath stringByDeletingLastPathComponent] : nil;
|
|
338
|
+
NSURL* src = [self.sourceURLs objectForKey:download];
|
|
339
|
+
|
|
340
|
+
// 4) crea DownloadInfo* e emetti
|
|
341
|
+
QString qFileName = fname ? QString::fromUtf8(fname.UTF8String) : QString();
|
|
342
|
+
QString qDir = dir ? QString::fromUtf8(dir.UTF8String) : QString();
|
|
343
|
+
QUrl qUrl = src ? QUrl::fromEncoded(QByteArray(src.absoluteString.UTF8String))
|
|
344
|
+
: QUrl();
|
|
345
|
+
|
|
346
|
+
DownloadInfo* info = new DownloadInfo(qFileName, qDir, qUrl, self.owner);
|
|
347
|
+
emit self.owner->downloadFinished(info);
|
|
348
|
+
|
|
349
|
+
// 5) cleanup mappe
|
|
283
350
|
if (finalPath) [self.downloadPaths removeObjectForKey:download];
|
|
284
351
|
[self.progressToDownload removeObjectForKey:download.progress];
|
|
285
|
-
[self.expectedTotals removeObjectForKey:download];
|
|
352
|
+
[self.expectedTotals removeObjectForKey:download];
|
|
353
|
+
[self.sourceURLs removeObjectForKey:download];
|
|
354
|
+
[self.suggestedNames removeObjectForKey:download];
|
|
286
355
|
}
|
|
287
356
|
|
|
357
|
+
|
|
288
358
|
- (void)download:(WKDownload *)download didFailWithError:(NSError *)error resumeData:(NSData *)resumeData {
|
|
289
359
|
if (!self.owner) return;
|
|
290
360
|
|
|
361
|
+
// stop KVO
|
|
291
362
|
@try {
|
|
292
363
|
[download.progress removeObserver:self forKeyPath:@"fractionCompleted"];
|
|
293
364
|
[download.progress removeObserver:self forKeyPath:@"completedUnitCount"];
|
|
294
365
|
[download.progress removeObserver:self forKeyPath:@"totalUnitCount"];
|
|
295
366
|
} @catch (...) {}
|
|
296
|
-
|
|
297
367
|
[self.completedProgresses addObject:download.progress];
|
|
298
368
|
|
|
369
|
+
// path (se già deciso)
|
|
299
370
|
NSString* finalPath = [self.downloadPaths objectForKey:download];
|
|
300
371
|
emit self.owner->downloadFailed(
|
|
301
372
|
finalPath ? QString::fromUtf8(finalPath.UTF8String) : QString(),
|
|
302
373
|
QString::fromUtf8(error.localizedDescription.UTF8String)
|
|
303
374
|
);
|
|
375
|
+
|
|
376
|
+
// cleanup mappe
|
|
304
377
|
if (finalPath) [self.downloadPaths removeObjectForKey:download];
|
|
305
378
|
[self.progressToDownload removeObjectForKey:download.progress];
|
|
306
|
-
[self.expectedTotals removeObjectForKey:download];
|
|
379
|
+
[self.expectedTotals removeObjectForKey:download];
|
|
380
|
+
[self.sourceURLs removeObjectForKey:download];
|
|
381
|
+
[self.suggestedNames removeObjectForKey:download];
|
|
307
382
|
}
|
|
308
383
|
|
|
384
|
+
|
|
309
385
|
@end
|
|
310
386
|
|
|
311
387
|
// =======================
|
|
@@ -344,6 +420,21 @@ WKWebViewWidget::WKWebViewWidget(QWidget* parent)
|
|
|
344
420
|
cfg.defaultWebpagePreferences.allowsContentJavaScript = YES;
|
|
345
421
|
}
|
|
346
422
|
|
|
423
|
+
// --- Fullscreen HTML5 (via KVC tollerante) ---
|
|
424
|
+
@try {
|
|
425
|
+
[cfg.preferences setValue:@YES forKey:@"fullScreenEnabled"];
|
|
426
|
+
} @catch (NSException *e) {
|
|
427
|
+
// ignore if not available
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// --- AirPlay & PiP via selector per compatibilità SDK ---
|
|
431
|
+
if ([cfg respondsToSelector:@selector(setAllowsAirPlayForMediaPlayback:)]) {
|
|
432
|
+
((void(*)(id, SEL, BOOL))objc_msgSend)(cfg, @selector(setAllowsAirPlayForMediaPlayback:), YES);
|
|
433
|
+
}
|
|
434
|
+
if ([cfg respondsToSelector:@selector(setAllowsPictureInPictureMediaPlayback:)]) {
|
|
435
|
+
((void(*)(id, SEL, BOOL))objc_msgSend)(cfg, @selector(setAllowsPictureInPictureMediaPlayback:), YES);
|
|
436
|
+
}
|
|
437
|
+
|
|
347
438
|
// SPA: intercetta pushState/replaceState/popstate/click
|
|
348
439
|
d->ucc = [WKUserContentController new];
|
|
349
440
|
d->msg = [FitUrlMsgHandler new];
|
|
@@ -377,6 +468,8 @@ WKWebViewWidget::WKWebViewWidget(QWidget* parent)
|
|
|
377
468
|
d->delegate = [WKNavDelegate new];
|
|
378
469
|
d->delegate.owner = this;
|
|
379
470
|
[d->wk setNavigationDelegate:d->delegate];
|
|
471
|
+
|
|
472
|
+
[d->wk setUIDelegate:d->delegate];
|
|
380
473
|
}
|
|
381
474
|
|
|
382
475
|
WKWebViewWidget::~WKWebViewWidget() {
|
{fit_webview_bridge-0.2.1a5 → fit_webview_bridge-0.2.2a4}/.github/workflows/wheels-macos.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fit_webview_bridge-0.2.1a5 → fit_webview_bridge-0.2.2a4}/bindings/pyside6/macos/CMakeLists.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|