77use crate :: { Error , RequestAsyncResponder } ;
88use gtk:: {
99 glib:: { self , MainContext , ObjectExt } ,
10- traits:: WidgetExt ,
1110} ;
1211use http:: { header:: CONTENT_TYPE , HeaderName , HeaderValue , Request , Response as HttpResponse } ;
1312use soup:: { MessageHeaders , MessageHeadersType } ;
1413use std:: {
1514 borrow:: Cow ,
1615 cell:: RefCell ,
17- collections:: VecDeque ,
1816 env:: current_dir,
1917 path:: { Path , PathBuf } ,
2018 rc:: Rc ,
21- sync:: {
22- atomic:: { AtomicBool , Ordering :: SeqCst } ,
23- Mutex ,
24- } ,
2519} ;
2620use webkit2gtk:: {
2721 ApplicationInfo , AutomationSessionExt , CookiePersistentStorage , DownloadExt , LoadEvent ,
@@ -33,7 +27,6 @@ use webkit2gtk::{
3327#[ derive( Debug ) ]
3428pub struct WebContextImpl {
3529 context : WebContext ,
36- webview_uri_loader : Rc < WebViewUriLoader > ,
3730 automation : bool ,
3831 app_info : Option < ApplicationInfo > ,
3932}
@@ -87,7 +80,6 @@ impl WebContextImpl {
8780 Self {
8881 context,
8982 automation,
90- webview_uri_loader : Rc :: default ( ) ,
9183 app_info : Some ( app_info) ,
9284 }
9385 }
@@ -114,15 +106,8 @@ pub trait WebContextExt {
114106 where
115107 F : Fn ( crate :: WebViewId , Request < Vec < u8 > > , RequestAsyncResponder ) + ' static ;
116108
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 ) ;
109+ /// Loads a URI for a [`WebView`].
110+ fn load_uri ( & self , webview : WebView , url : String , headers : Option < http:: HeaderMap > ) ;
126111
127112 /// If the context allows automation.
128113 ///
@@ -279,12 +264,23 @@ impl WebContextExt for super::WebContext {
279264 Ok ( ( ) )
280265 }
281266
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- }
267+ fn load_uri ( & self , webview : WebView , uri : String , headers : Option < http:: HeaderMap > ) {
268+ if let Some ( headers) = headers {
269+ let req = URIRequest :: builder ( ) . uri ( & uri ) . build ( ) ;
285270
286- fn flush_queue_loader ( & self ) {
287- self . os . webview_uri_loader . clone ( ) . flush ( )
271+ if let Some ( ref mut req_headers) = req. http_headers ( ) {
272+ for ( header, value) in headers. iter ( ) {
273+ req_headers. append (
274+ header. to_string ( ) . as_str ( ) ,
275+ value. to_str ( ) . unwrap_or_default ( ) ,
276+ ) ;
277+ }
278+ }
279+
280+ webview. load_request ( & req) ;
281+ } else {
282+ webview. load_uri ( & uri) ;
283+ }
288284 }
289285
290286 fn allows_automation ( & self ) -> bool {
@@ -399,134 +395,3 @@ impl MainThreadRequest {
399395
400396unsafe impl Send for MainThreadRequest { }
401397unsafe 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