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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: fit-webview-bridge
3
- Version: 0.2.1a5
3
+ Version: 0.2.2a4
4
4
  Summary: Qt native WebView bridge with PySide6 bindings
5
5
  Author: FIT Project
6
6
  License: LGPL-3.0-or-later
@@ -7,6 +7,9 @@
7
7
  <object-type name="WKWebViewWidget">
8
8
  <include file-name="WKWebViewWidget.h" location="global"/>
9
9
  </object-type>
10
+ <object-type name="DownloadInfo">
11
+ <include file-name="DownloadInfo.h" location="global"/>
12
+ </object-type>
10
13
  </typesystem>
11
14
 
12
15
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "fit-webview-bridge"
3
- version = "0.2.1a5"
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"]
@@ -28,6 +28,7 @@ file(CREATE_LINK "${QT_FRAMEWORKS_DIR}/QtWidgets.framework/Headers" "${QT_HDRSHI
28
28
  add_library(wkwebview STATIC
29
29
  WKWebViewWidget.mm
30
30
  WKWebViewWidget.h
31
+ DownloadInfo.h
31
32
  )
32
33
  target_compile_features(wkwebview PRIVATE cxx_std_17)
33
34
 
@@ -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(const QString& filePath);
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
- emit self.owner->downloadFinished(finalPath ? QString::fromUtf8(finalPath.UTF8String) : QString());
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() {