55//! Unix platform extensions for [`WebContext`](super::WebContext).
66
77use crate :: { Error , RequestAsyncResponder } ;
8- use gtk:: {
9- glib:: { self , MainContext , ObjectExt } ,
10- traits:: WidgetExt ,
11- } ;
8+ use gtk:: glib:: { self , MainContext , ObjectExt } ;
129use http:: { header:: CONTENT_TYPE , HeaderName , HeaderValue , Request , Response as HttpResponse } ;
1310use soup:: { MessageHeaders , MessageHeadersType } ;
1411use std:: {
1512 borrow:: Cow ,
1613 cell:: RefCell ,
17- collections:: VecDeque ,
1814 env:: current_dir,
1915 path:: { Path , PathBuf } ,
2016 rc:: Rc ,
21- sync:: {
22- atomic:: { AtomicBool , Ordering :: SeqCst } ,
23- Mutex ,
24- } ,
2517} ;
2618use 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 ,
3122} ;
3223
3324#[ derive( Debug ) ]
3425pub struct WebContextImpl {
3526 context : WebContext ,
36- webview_uri_loader : Rc < WebViewUriLoader > ,
3727 automation : bool ,
3828 app_info : Option < ApplicationInfo > ,
3929}
@@ -87,7 +77,6 @@ impl WebContextImpl {
8777 Self {
8878 context,
8979 automation,
90- webview_uri_loader : Rc :: default ( ) ,
9180 app_info : Some ( app_info) ,
9281 }
9382 }
@@ -114,15 +103,8 @@ pub trait WebContextExt {
114103 where
115104 F : Fn ( crate :: WebViewId , Request < Vec < u8 > > , RequestAsyncResponder ) + ' static ;
116105
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 > ) ;
126108
127109 /// If the context allows automation.
128110 ///
@@ -279,12 +261,23 @@ impl WebContextExt for super::WebContext {
279261 Ok ( ( ) )
280262 }
281263
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 ( ) ;
285267
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+ }
288281 }
289282
290283 fn allows_automation ( & self ) -> bool {
@@ -399,134 +392,3 @@ impl MainThreadRequest {
399392
400393unsafe impl Send for MainThreadRequest { }
401394unsafe 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