5
5
//! Unix platform extensions for [`WebContext`](super::WebContext).
6
6
7
7
use crate :: { Error , RequestAsyncResponder } ;
8
- use gtk:: {
9
- glib:: { self , MainContext , ObjectExt } ,
10
- traits:: WidgetExt ,
11
- } ;
8
+ use gtk:: glib:: { self , MainContext , ObjectExt } ;
12
9
use http:: { header:: CONTENT_TYPE , HeaderName , HeaderValue , Request , Response as HttpResponse } ;
13
10
use soup:: { MessageHeaders , MessageHeadersType } ;
14
11
use std:: {
15
12
borrow:: Cow ,
16
13
cell:: RefCell ,
17
- collections:: VecDeque ,
18
14
env:: current_dir,
19
15
path:: { Path , PathBuf } ,
20
16
rc:: Rc ,
21
- sync:: {
22
- atomic:: { AtomicBool , Ordering :: SeqCst } ,
23
- Mutex ,
24
- } ,
25
17
} ;
26
18
use webkit2gtk:: {
27
- ApplicationInfo , AutomationSessionExt , CookiePersistentStorage , DownloadExt , LoadEvent ,
28
- SecurityManagerExt , URIRequest , URIRequestExt , URISchemeRequest , URISchemeRequestExt ,
29
- URISchemeResponse , URISchemeResponseExt , WebContext , WebContextExt as Webkit2gtkContextExt ,
30
- WebView , WebViewExt ,
19
+ ApplicationInfo , AutomationSessionExt , CookiePersistentStorage , DownloadExt , SecurityManagerExt ,
20
+ URIRequest , URIRequestExt , URISchemeRequest , URISchemeRequestExt , URISchemeResponse ,
21
+ URISchemeResponseExt , WebContext , WebContextExt as Webkit2gtkContextExt , WebView , WebViewExt ,
31
22
} ;
32
23
33
24
#[ derive( Debug ) ]
34
25
pub struct WebContextImpl {
35
26
context : WebContext ,
36
- webview_uri_loader : Rc < WebViewUriLoader > ,
37
27
automation : bool ,
38
28
app_info : Option < ApplicationInfo > ,
39
29
}
@@ -87,7 +77,6 @@ impl WebContextImpl {
87
77
Self {
88
78
context,
89
79
automation,
90
- webview_uri_loader : Rc :: default ( ) ,
91
80
app_info : Some ( app_info) ,
92
81
}
93
82
}
@@ -114,15 +103,8 @@ pub trait WebContextExt {
114
103
where
115
104
F : Fn ( crate :: WebViewId , Request < Vec < u8 > > , RequestAsyncResponder ) + ' static ;
116
105
117
- /// Add a [`WebView`] to the queue waiting to be opened.
118
- ///
119
- /// See the [`WebViewUriLoader`] for more information.
120
- fn queue_load_uri ( & self , webview : WebView , url : String , headers : Option < http:: HeaderMap > ) ;
121
-
122
- /// Flush all queued [`WebView`]s waiting to load a uri.
123
- ///
124
- /// See the [`WebViewUriLoader`] for more information.
125
- fn flush_queue_loader ( & self ) ;
106
+ /// Loads a URI for a [`WebView`].
107
+ fn load_uri ( & self , webview : WebView , url : String , headers : Option < http:: HeaderMap > ) ;
126
108
127
109
/// If the context allows automation.
128
110
///
@@ -279,12 +261,23 @@ impl WebContextExt for super::WebContext {
279
261
Ok ( ( ) )
280
262
}
281
263
282
- fn queue_load_uri ( & self , webview : WebView , url : String , headers : Option < http:: HeaderMap > ) {
283
- self . os . webview_uri_loader . push ( webview , url , headers)
284
- }
264
+ fn load_uri ( & self , webview : WebView , uri : String , headers : Option < http:: HeaderMap > ) {
265
+ if let Some ( headers) = headers {
266
+ let req = URIRequest :: builder ( ) . uri ( & uri ) . build ( ) ;
285
267
286
- fn flush_queue_loader ( & self ) {
287
- self . os . webview_uri_loader . clone ( ) . flush ( )
268
+ if let Some ( ref mut req_headers) = req. http_headers ( ) {
269
+ for ( header, value) in headers. iter ( ) {
270
+ req_headers. append (
271
+ header. to_string ( ) . as_str ( ) ,
272
+ value. to_str ( ) . unwrap_or_default ( ) ,
273
+ ) ;
274
+ }
275
+ }
276
+
277
+ webview. load_request ( & req) ;
278
+ } else {
279
+ webview. load_uri ( & uri) ;
280
+ }
288
281
}
289
282
290
283
fn allows_automation ( & self ) -> bool {
@@ -399,134 +392,3 @@ impl MainThreadRequest {
399
392
400
393
unsafe impl Send for MainThreadRequest { }
401
394
unsafe impl Sync for MainThreadRequest { }
402
-
403
- #[ derive( Debug ) ]
404
- struct WebviewUriRequest {
405
- webview : WebView ,
406
- uri : String ,
407
- headers : Option < http:: HeaderMap > ,
408
- }
409
-
410
- /// Prevents an unknown concurrency bug with loading multiple URIs at the same time on webkit2gtk.
411
- ///
412
- /// Using the queue prevents data race issues with loading uris for multiple [`WebView`]s in the
413
- /// same context at the same time. Occasionally, the one of the [`WebView`]s will be clobbered
414
- /// and it's content will be injected into a different [`WebView`].
415
- ///
416
- /// Example of `webview-c` clobbering `webview-b` while `webview-a` is okay:
417
- /// ```text
418
- /// webview-a triggers load-change::started
419
- /// URISchemeRequestCallback triggered with webview-a
420
- /// webview-a triggers load-change::committed
421
- /// webview-a triggers load-change::finished
422
- /// webview-b triggers load-change::started
423
- /// webview-c triggers load-change::started
424
- /// URISchemeRequestCallback triggered with webview-c
425
- /// URISchemeRequestCallback triggered with webview-c
426
- /// webview-c triggers load-change::committed
427
- /// webview-c triggers load-change::finished
428
- /// ```
429
- ///
430
- /// In that example, `webview-a` will load fine. `webview-b` will remain empty as the uri was
431
- /// never loaded. `webview-c` will contain the content of both `webview-b` and `webview-c`
432
- /// because it was triggered twice even through only started once. The content injected will not
433
- /// be sequential, and often is interjected in the middle of one of the other contents.
434
- ///
435
- /// FIXME: We think this may be an underlying concurrency bug in webkit2gtk as the usual ways of
436
- /// fixing threading issues are not working. Ideally, the locks are not needed if we can understand
437
- /// the true cause of the bug.
438
- #[ derive( Debug , Default ) ]
439
- struct WebViewUriLoader {
440
- lock : AtomicBool ,
441
- queue : Mutex < VecDeque < WebviewUriRequest > > ,
442
- }
443
-
444
- impl WebViewUriLoader {
445
- /// Check if the lock is in use.
446
- fn is_locked ( & self ) -> bool {
447
- self . lock . swap ( true , SeqCst )
448
- }
449
-
450
- /// Unlock the lock.
451
- fn unlock ( & self ) {
452
- self . lock . store ( false , SeqCst )
453
- }
454
-
455
- /// Add a [`WebView`] to the queue.
456
- fn push ( & self , webview : WebView , uri : String , headers : Option < http:: HeaderMap > ) {
457
- let mut queue = self . queue . lock ( ) . expect ( "poisoned load queue" ) ;
458
- queue. push_back ( WebviewUriRequest {
459
- webview,
460
- uri,
461
- headers,
462
- } )
463
- }
464
-
465
- /// Remove a [`WebView`] from the queue and return it.
466
- fn pop ( & self ) -> Option < WebviewUriRequest > {
467
- let mut queue = self . queue . lock ( ) . expect ( "poisoned load queue" ) ;
468
- queue. pop_front ( )
469
- }
470
-
471
- /// Load the next uri to load if the lock is not engaged.
472
- fn flush ( self : Rc < Self > ) {
473
- if !self . is_locked ( ) {
474
- if let Some ( WebviewUriRequest {
475
- webview,
476
- uri,
477
- headers,
478
- } ) = self . pop ( )
479
- {
480
- // ensure that the lock is released when the webview is destroyed before LoadEvent::Finished is handled
481
- let self_ = self . clone ( ) ;
482
- let destroy_id = webview. connect_destroy ( move |_| {
483
- self_. unlock ( ) ;
484
- self_. clone ( ) . flush ( ) ;
485
- } ) ;
486
- let destroy_id_guard = Mutex :: new ( Some ( destroy_id) ) ;
487
-
488
- let load_changed_id_guard = Rc :: new ( Mutex :: new ( None ) ) ;
489
- let load_changed_id_guard_ = load_changed_id_guard. clone ( ) ;
490
- let self_ = self . clone ( ) ;
491
- // noet: we do not need to listen to failed events because those will finish the change event anyways
492
- let load_changed_id = webview. connect_load_changed ( move |w, event| {
493
- if let LoadEvent :: Finished = event {
494
- self_. unlock ( ) ;
495
- self_. clone ( ) . flush ( ) ;
496
-
497
- // unregister listeners
498
- if let Some ( id) = destroy_id_guard. lock ( ) . unwrap ( ) . take ( ) {
499
- w. disconnect ( id) ;
500
- }
501
- if let Some ( id) = load_changed_id_guard_. lock ( ) . unwrap ( ) . take ( ) {
502
- w. disconnect ( id) ;
503
- }
504
- } ;
505
- } ) ;
506
- load_changed_id_guard
507
- . lock ( )
508
- . unwrap ( )
509
- . replace ( load_changed_id) ;
510
-
511
- if let Some ( headers) = headers {
512
- let req = URIRequest :: builder ( ) . uri ( & uri) . build ( ) ;
513
-
514
- if let Some ( ref mut req_headers) = req. http_headers ( ) {
515
- for ( header, value) in headers. iter ( ) {
516
- req_headers. append (
517
- header. to_string ( ) . as_str ( ) ,
518
- value. to_str ( ) . unwrap_or_default ( ) ,
519
- ) ;
520
- }
521
- }
522
-
523
- webview. load_request ( & req) ;
524
- } else {
525
- webview. load_uri ( & uri) ;
526
- }
527
- } else {
528
- self . unlock ( ) ;
529
- }
530
- }
531
- }
532
- }
0 commit comments