diff --git a/Cargo.lock b/Cargo.lock index 3b5253a1bd8..a7bea7667f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4233,6 +4233,7 @@ dependencies = [ "style_traits", "surfman", "tracing", + "url", "webdriver_server", "webgpu", "webrender", diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index 5f3c3cf9469..6aef9294078 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -156,7 +156,7 @@ pub struct IOCompositor { ready_to_save_state: ReadyState, /// The webrender renderer. - webrender: webrender::Renderer, + webrender: Option, /// The active webrender document. webrender_document: DocumentId, @@ -386,7 +386,7 @@ impl IOCompositor { constellation_chan: state.constellation_chan, time_profiler_chan: state.time_profiler_chan, ready_to_save_state: ReadyState::Unknown, - webrender: state.webrender, + webrender: Some(state.webrender), webrender_document: state.webrender_document, webrender_api: state.webrender_api, rendering_context: state.rendering_context, @@ -411,11 +411,13 @@ impl IOCompositor { compositor } - pub fn deinit(self) { + pub fn deinit(&mut self) { if let Err(err) = self.rendering_context.make_current() { warn!("Failed to make the rendering context current: {:?}", err); } - self.webrender.deinit(); + if let Some(webrender) = self.webrender.take() { + webrender.deinit(); + } } fn update_cursor(&mut self, result: CompositorHitTestResult) { @@ -515,10 +517,12 @@ impl IOCompositor { self.remove_webview(top_level_browsing_context_id); }, + // TODO: remove this CompositorMsg::MoveResizeWebView(webview_id, rect) => { self.move_resize_webview(webview_id, rect); }, + // TODO: remove this CompositorMsg::ShowWebView(webview_id, hide_others) => { if let Err(UnknownWebView(webview_id)) = self.show_webview(webview_id, hide_others) { @@ -526,12 +530,14 @@ impl IOCompositor { } }, + // TODO: remove this CompositorMsg::HideWebView(webview_id) => { if let Err(UnknownWebView(webview_id)) = self.hide_webview(webview_id) { warn!("{webview_id}: HideWebView on unknown webview id"); } }, + // TODO: remove this CompositorMsg::RaiseWebViewToTop(webview_id, hide_others) => { if let Err(UnknownWebView(webview_id)) = self.raise_webview_to_top(webview_id, hide_others) @@ -1941,7 +1947,8 @@ impl IOCompositor { for id in self.pipeline_details.keys() { if let Some(WebRenderEpoch(epoch)) = self .webrender - .current_epoch(self.webrender_document, id.into()) + .as_ref() + .and_then(|wr| wr.current_epoch(self.webrender_document, id.into())) { let epoch = Epoch(epoch); pipeline_epochs.insert(*id, epoch); @@ -2016,7 +2023,9 @@ impl IOCompositor { } self.assert_no_gl_error(); - self.webrender.update(); + if let Some(webrender) = self.webrender.as_mut() { + webrender.update(); + } let wait_for_stable_image = matches!( target, @@ -2073,7 +2082,9 @@ impl IOCompositor { // Paint the scene. // TODO(gw): Take notice of any errors the renderer returns! self.clear_background(); - self.webrender.render(size, 0 /* buffer_age */).ok(); + if let Some(webrender) = self.webrender.as_mut() { + webrender.render(size, 0 /* buffer_age */).ok(); + } }, ); @@ -2204,7 +2215,8 @@ impl IOCompositor { for (pipeline_id, pending_epochs) in pending_paint_metrics.iter_mut() { let Some(WebRenderEpoch(current_epoch)) = self .webrender - .current_epoch(self.webrender_document, pipeline_id.into()) + .as_ref() + .and_then(|wr| wr.current_epoch(self.webrender_document, pipeline_id.into())) else { continue; }; @@ -2427,7 +2439,10 @@ impl IOCompositor { } pub fn toggle_webrender_debug(&mut self, option: WebRenderDebugOption) { - let mut flags = self.webrender.get_debug_flags(); + let Some(webrender) = self.webrender.as_mut() else { + return; + }; + let mut flags = webrender.get_debug_flags(); let flag = match option { WebRenderDebugOption::Profiler => { webrender::DebugFlags::PROFILER_DBG | @@ -2438,7 +2453,7 @@ impl IOCompositor { WebRenderDebugOption::RenderTargetDebug => webrender::DebugFlags::RENDER_TARGET_DBG, }; flags.toggle(flag); - self.webrender.set_debug_flags(flags); + webrender.set_debug_flags(flags); let mut txn = Transaction::new(); self.generate_frame(&mut txn, RenderReasons::TESTING); diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index 9a46e194075..bf58ece059c 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -4567,13 +4567,23 @@ where self.handle_close_top_level_browsing_context(top_level_browsing_context_id); }, WebDriverCommandMsg::NewWebView(sender, load_sender) => { - let top_level_browsing_context_id = TopLevelBrowsingContextId::new(); + let (chan, port) = match ipc::channel() { + Ok(result) => result, + Err(error) => return warn!("Failed to create channel: {error:?}"), + }; + self.embedder_proxy + .send((None, EmbedderMsg::AllowOpeningWebView(chan))); + let webview_id = match port.recv() { + Ok(Some(webview_id)) => webview_id, + Ok(None) => return warn!("Embedder refused to allow opening webview"), + Err(error) => return warn!("Failed to receive webview id: {error:?}"), + }; self.handle_new_top_level_browsing_context( ServoUrl::parse_with_base(None, "about:blank").expect("Infallible parse"), - top_level_browsing_context_id, + webview_id, Some(load_sender), ); - let _ = sender.send(top_level_browsing_context_id); + let _ = sender.send(webview_id); }, WebDriverCommandMsg::FocusWebView(top_level_browsing_context_id) => { self.handle_focus_web_view(top_level_browsing_context_id); diff --git a/components/script/dom/windowproxy.rs b/components/script/dom/windowproxy.rs index a4659eedf9d..5f113b5f068 100644 --- a/components/script/dom/windowproxy.rs +++ b/components/script/dom/windowproxy.rs @@ -294,8 +294,7 @@ impl WindowProxy { .unwrap(); let msg = EmbedderMsg::AllowOpeningWebView(chan); window.send_to_embedder(msg); - if port.recv().unwrap() { - let new_top_level_browsing_context_id = TopLevelBrowsingContextId::new(); + if let Some(new_top_level_browsing_context_id) = port.recv().unwrap() { let new_browsing_context_id = BrowsingContextId::from(new_top_level_browsing_context_id); let new_pipeline_id = PipelineId::new(); diff --git a/components/servo/Cargo.toml b/components/servo/Cargo.toml index 2afbe3a0b63..9db597a8324 100644 --- a/components/servo/Cargo.toml +++ b/components/servo/Cargo.toml @@ -89,6 +89,7 @@ servo_url = { path = "../url" } style = { workspace = true } style_traits = { workspace = true } tracing = { workspace = true, optional = true } +url = { workspace = true } webdriver_server = { path = "../webdriver_server", optional = true } webgpu = { path = "../webgpu" } webrender = { workspace = true } diff --git a/components/servo/examples/winit_minimal.rs b/components/servo/examples/winit_minimal.rs index da4742ca880..62114194ba9 100644 --- a/components/servo/examples/winit_minimal.rs +++ b/components/servo/examples/winit_minimal.rs @@ -4,19 +4,17 @@ use std::cell::Cell; use std::error::Error; -use std::mem::replace; use std::rc::Rc; use std::sync::{Arc, Mutex}; -use base::id::WebViewId; use compositing::windowing::{AnimationState, EmbedderEvent, EmbedderMethods, WindowMethods}; use embedder_traits::EmbedderMsg; use euclid::{Point2D, Scale, Size2D}; -use servo::Servo; +use servo::{Servo, WebView}; use servo_geometry::DeviceIndependentPixel; -use servo_url::ServoUrl; use surfman::{Connection, SurfaceType}; use tracing::warn; +use url::Url; use webrender_api::units::{DeviceIntPoint, DeviceIntRect, DevicePixel}; use webrender_traits::SurfmanRenderingContext; use winit::application::ApplicationHandler; @@ -37,6 +35,10 @@ fn main() -> Result<(), Box> { let mut app = App::new(&event_loop); event_loop.run_app(&mut app)?; + if let App::Running { servo, .. } = app { + servo.deinit(); + } + Ok(()) } @@ -45,8 +47,8 @@ enum App { Running { window_delegate: Rc, servo: Servo, + webviews: Vec, }, - Exiting, } impl App { @@ -90,7 +92,7 @@ impl ApplicationHandler for App { .make_gl_context_current() .expect("Failed to make context current"); let window_delegate = Rc::new(WindowDelegate::new(window)); - let mut servo = Servo::new( + let servo = Servo::new( Default::default(), Default::default(), Rc::new(rendering_context), @@ -102,14 +104,14 @@ impl ApplicationHandler for App { compositing::CompositeTarget::Window, ); servo.setup_logging(); - servo.handle_events([EmbedderEvent::NewWebView( - ServoUrl::parse("https://demo.servo.org/experiments/twgl-tunnel/") + let webviews = vec![servo.new_webview( + Url::parse("https://demo.servo.org/experiments/twgl-tunnel/") .expect("Guaranteed by argument"), - WebViewId::new(), - )]); + )]; *self = Self::Running { window_delegate, servo, + webviews, }; } } @@ -123,46 +125,69 @@ impl ApplicationHandler for App { if let Self::Running { window_delegate, servo, + webviews, } = self { - let mut events_for_servo = vec![]; - for (_webview_id, message) in servo.get_events() { + for (_webview_id, message) in servo.get_events().collect::>() { match message { // FIXME: rust-analyzer autocompletes this as top_level_browsing_context_id EmbedderMsg::WebViewOpened(webview_id) => { + // TODO: We currently assume `webview` refers to the same webview as `_webview_id` let rect = window_delegate.get_coordinates().get_viewport().to_f32(); - events_for_servo.extend([ - EmbedderEvent::FocusWebView(webview_id), - EmbedderEvent::MoveResizeWebView(webview_id, rect), - EmbedderEvent::RaiseWebViewToTop(webview_id, true), - ]); + if let Some(webview) = + webviews.iter().find(|webview| webview.id() == webview_id) + { + webview.focus(); + webview.move_resize(rect); + webview.raise_to_top(true); + } + }, + EmbedderMsg::AllowOpeningWebView(webview_id_sender) => { + let webview = servo.new_auxiliary_webview(); + let _ = webview_id_sender.send(Some(webview.id())); + webviews.push(webview); + }, + EmbedderMsg::AllowNavigationRequest(pipeline_id, _) => { + servo.handle_events([EmbedderEvent::AllowNavigationResponse( + pipeline_id, + true, + )]); }, _ => {}, } } - servo.handle_events(events_for_servo); + // FIXME: still needed for the compositor to actually run + servo.handle_events([]); } match event { WindowEvent::CloseRequested => { - if matches!(self, Self::Running { .. }) { - let Self::Running { servo, .. } = replace(self, Self::Exiting) else { - unreachable!() - }; - // TODO: ask Servo to shut down and wait for EmbedderMsg::Shutdown? - servo.deinit(); - } event_loop.exit(); }, WindowEvent::RedrawRequested => { if let Self::Running { window_delegate, servo, + .. } = self { servo.present(); window_delegate.window.request_redraw(); } }, + WindowEvent::MouseInput { .. } => { + // When the window is clicked, close the last webview by dropping its handle, + // then show the next most recently opened webview. + // + // TODO: Test closing webviews a better way, so that we can use mouse input to test + // input handling. + if let Self::Running { webviews, .. } = self { + let _ = webviews.pop(); + match webviews.last() { + Some(last) => last.show(true), + None => event_loop.exit(), + } + } + }, _ => (), } } diff --git a/components/servo/lib.rs b/components/servo/lib.rs index a516f228019..c96926d9af0 100644 --- a/components/servo/lib.rs +++ b/components/servo/lib.rs @@ -17,7 +17,11 @@ //! `Servo` is fed events from a generic type that implements the //! `WindowMethods` trait. -use std::borrow::{BorrowMut, Cow}; +mod proxies; +mod webview; + +use std::borrow::Cow; +use std::cell::RefCell; use std::cmp::max; use std::path::PathBuf; use std::rc::Rc; @@ -103,12 +107,15 @@ use webrender_traits::{ }; pub use { background_hang_monitor, base, bluetooth, bluetooth_traits, canvas, canvas_traits, compositing, - constellation, devtools, devtools_traits, embedder_traits, euclid, fonts, ipc_channel, - keyboard_types, layout_thread_2020, media, net, net_traits, profile, profile_traits, script, + devtools, devtools_traits, embedder_traits, euclid, fonts, ipc_channel, keyboard_types, + layout_thread_2020, media, net, net_traits, profile, profile_traits, script, script_layout_interface, script_traits, servo_config as config, servo_config, servo_geometry, - servo_url as url, servo_url, style, style_traits, webrender_api, webrender_traits, + servo_url, style, style_traits, webrender_api, webrender_traits, }; +use crate::proxies::ConstellationProxy; +pub use crate::webview::WebView; + #[cfg(feature = "webdriver")] fn webdriver(port: u16, constellation: Sender) { webdriver_server::start_server(port, constellation); @@ -177,8 +184,8 @@ mod media_platform { /// loop to pump messages between the embedding application and /// various browser components. pub struct Servo { - compositor: IOCompositor, - constellation_chan: Sender, + compositor: Rc>, + constellation_proxy: ConstellationProxy, embedder_receiver: EmbedderReceiver, messages_for_embedder: Vec<(Option, EmbedderMsg)>, profiler_enabled: bool, @@ -440,7 +447,7 @@ impl Servo { ); let (player_context, glplayer_threads) = Self::create_media_window_gl_context( - external_image_handlers.borrow_mut(), + &mut external_image_handlers, external_images.clone(), &rendering_context, ); @@ -518,8 +525,8 @@ impl Servo { ); Servo { - compositor, - constellation_chan, + compositor: Rc::new(RefCell::new(compositor)), + constellation_proxy: ConstellationProxy::new(constellation_chan), embedder_receiver, messages_for_embedder: Vec::new(), profiler_enabled: false, @@ -640,15 +647,15 @@ impl Servo { EmbedderEvent::Idle => {}, EmbedderEvent::Refresh => { - self.compositor.composite(); + self.compositor.borrow_mut().composite(); }, EmbedderEvent::WindowResize => { - return self.compositor.on_resize_window_event(); + return self.compositor.borrow_mut().on_resize_window_event(); }, EmbedderEvent::ThemeChange(theme) => { let msg = ConstellationMsg::ThemeChange(theme); - if let Err(e) = self.constellation_chan.send(msg) { + if let Err(e) = self.constellation_proxy.try_send(msg) { warn!( "Sending platform theme change to constellation failed ({:?}).", e @@ -656,16 +663,17 @@ impl Servo { } }, EmbedderEvent::InvalidateNativeSurface => { - self.compositor.invalidate_native_surface(); + self.compositor.borrow_mut().invalidate_native_surface(); }, EmbedderEvent::ReplaceNativeSurface(native_widget, coords) => { self.compositor + .borrow_mut() .replace_native_surface(native_widget, coords); - self.compositor.composite(); + self.compositor.borrow_mut().composite(); }, EmbedderEvent::AllowNavigationResponse(pipeline_id, allowed) => { let msg = ConstellationMsg::AllowNavigationResponse(pipeline_id, allowed); - if let Err(e) = self.constellation_chan.send(msg) { + if let Err(e) = self.constellation_proxy.try_send(msg) { warn!( "Sending allow navigation to constellation failed ({:?}).", e @@ -675,57 +683,66 @@ impl Servo { EmbedderEvent::LoadUrl(top_level_browsing_context_id, url) => { let msg = ConstellationMsg::LoadUrl(top_level_browsing_context_id, url); - if let Err(e) = self.constellation_chan.send(msg) { + if let Err(e) = self.constellation_proxy.try_send(msg) { warn!("Sending load url to constellation failed ({:?}).", e); } }, EmbedderEvent::ClearCache => { let msg = ConstellationMsg::ClearCache; - if let Err(e) = self.constellation_chan.send(msg) { + if let Err(e) = self.constellation_proxy.try_send(msg) { warn!("Sending clear cache to constellation failed ({:?}).", e); } }, EmbedderEvent::MouseWindowEventClass(mouse_window_event) => { self.compositor + .borrow_mut() .on_mouse_window_event_class(mouse_window_event); }, EmbedderEvent::MouseWindowMoveEventClass(cursor) => { - self.compositor.on_mouse_window_move_event_class(cursor); + self.compositor + .borrow_mut() + .on_mouse_window_move_event_class(cursor); }, EmbedderEvent::Touch(event_type, identifier, location) => { self.compositor + .borrow_mut() .on_touch_event(event_type, identifier, location); }, EmbedderEvent::Wheel(delta, location) => { - self.compositor.on_wheel_event(delta, location); + self.compositor.borrow_mut().on_wheel_event(delta, location); }, EmbedderEvent::Scroll(scroll_location, cursor, phase) => { self.compositor + .borrow_mut() .on_scroll_event(scroll_location, cursor, phase); }, EmbedderEvent::Zoom(magnification) => { - self.compositor.on_zoom_window_event(magnification); + self.compositor + .borrow_mut() + .on_zoom_window_event(magnification); }, EmbedderEvent::ResetZoom => { - self.compositor.on_zoom_reset_window_event(); + self.compositor.borrow_mut().on_zoom_reset_window_event(); }, EmbedderEvent::PinchZoom(zoom) => { - self.compositor.on_pinch_zoom_window_event(zoom); + self.compositor + .borrow_mut() + .on_pinch_zoom_window_event(zoom); }, EmbedderEvent::Navigation(top_level_browsing_context_id, direction) => { let msg = ConstellationMsg::TraverseHistory(top_level_browsing_context_id, direction); - if let Err(e) = self.constellation_chan.send(msg) { + if let Err(e) = self.constellation_proxy.try_send(msg) { warn!("Sending navigation to constellation failed ({:?}).", e); } self.messages_for_embedder.push(( @@ -736,14 +753,14 @@ impl Servo { EmbedderEvent::Keyboard(key_event) => { let msg = ConstellationMsg::Keyboard(key_event); - if let Err(e) = self.constellation_chan.send(msg) { + if let Err(e) = self.constellation_proxy.try_send(msg) { warn!("Sending keyboard event to constellation failed ({:?}).", e); } }, EmbedderEvent::IMEComposition(ime_event) => { let msg = ConstellationMsg::IMECompositionEvent(ime_event); - if let Err(e) = self.constellation_chan.send(msg) { + if let Err(e) = self.constellation_proxy.try_send(msg) { warn!( "Sending composition event to constellation failed ({:?}).", e @@ -753,7 +770,7 @@ impl Servo { EmbedderEvent::IMEDismissed => { let msg = ConstellationMsg::IMEDismissed; - if let Err(e) = self.constellation_chan.send(msg) { + if let Err(e) = self.constellation_proxy.try_send(msg) { warn!( "Sending IMEDismissed event to constellation failed ({:?}).", e @@ -762,19 +779,19 @@ impl Servo { }, EmbedderEvent::Quit => { - self.compositor.maybe_start_shutting_down(); + self.compositor.borrow_mut().maybe_start_shutting_down(); }, EmbedderEvent::ExitFullScreen(top_level_browsing_context_id) => { let msg = ConstellationMsg::ExitFullScreen(top_level_browsing_context_id); - if let Err(e) = self.constellation_chan.send(msg) { + if let Err(e) = self.constellation_proxy.try_send(msg) { warn!("Sending exit fullscreen to constellation failed ({:?}).", e); } }, EmbedderEvent::Reload(top_level_browsing_context_id) => { let msg = ConstellationMsg::Reload(top_level_browsing_context_id); - if let Err(e) = self.constellation_chan.send(msg) { + if let Err(e) = self.constellation_proxy.try_send(msg) { warn!("Sending reload to constellation failed ({:?}).", e); } }, @@ -786,22 +803,22 @@ impl Servo { } else { ConstellationMsg::DisableProfiler }; - if let Err(e) = self.constellation_chan.send(msg) { + if let Err(e) = self.constellation_proxy.try_send(msg) { warn!("Sending profiler toggle to constellation failed ({:?}).", e); } }, EmbedderEvent::ToggleWebRenderDebug(option) => { - self.compositor.toggle_webrender_debug(option); + self.compositor.borrow_mut().toggle_webrender_debug(option); }, EmbedderEvent::CaptureWebRender => { - self.compositor.capture_webrender(); + self.compositor.borrow_mut().capture_webrender(); }, EmbedderEvent::NewWebView(url, top_level_browsing_context_id) => { let msg = ConstellationMsg::NewWebView(url, top_level_browsing_context_id); - if let Err(e) = self.constellation_chan.send(msg) { + if let Err(e) = self.constellation_proxy.try_send(msg) { warn!( "Sending NewBrowser message to constellation failed ({:?}).", e @@ -811,7 +828,7 @@ impl Servo { EmbedderEvent::FocusWebView(top_level_browsing_context_id) => { let msg = ConstellationMsg::FocusWebView(top_level_browsing_context_id); - if let Err(e) = self.constellation_chan.send(msg) { + if let Err(e) = self.constellation_proxy.try_send(msg) { warn!( "Sending FocusBrowser message to constellation failed ({:?}).", e @@ -821,7 +838,7 @@ impl Servo { EmbedderEvent::CloseWebView(top_level_browsing_context_id) => { let msg = ConstellationMsg::CloseWebView(top_level_browsing_context_id); - if let Err(e) = self.constellation_chan.send(msg) { + if let Err(e) = self.constellation_proxy.try_send(msg) { warn!( "Sending CloseBrowser message to constellation failed ({:?}).", e @@ -830,23 +847,30 @@ impl Servo { }, EmbedderEvent::MoveResizeWebView(webview_id, rect) => { - self.compositor.move_resize_webview(webview_id, rect); + self.compositor + .borrow_mut() + .move_resize_webview(webview_id, rect); }, EmbedderEvent::ShowWebView(webview_id, hide_others) => { - if let Err(UnknownWebView(webview_id)) = - self.compositor.show_webview(webview_id, hide_others) + if let Err(UnknownWebView(webview_id)) = self + .compositor + .borrow_mut() + .show_webview(webview_id, hide_others) { warn!("{webview_id}: ShowWebView on unknown webview id"); } }, EmbedderEvent::HideWebView(webview_id) => { - if let Err(UnknownWebView(webview_id)) = self.compositor.hide_webview(webview_id) { + if let Err(UnknownWebView(webview_id)) = + self.compositor.borrow_mut().hide_webview(webview_id) + { warn!("{webview_id}: HideWebView on unknown webview id"); } }, EmbedderEvent::RaiseWebViewToTop(webview_id, hide_others) => { if let Err(UnknownWebView(webview_id)) = self .compositor + .borrow_mut() .raise_webview_to_top(webview_id, hide_others) { warn!("{webview_id}: RaiseWebViewToTop on unknown webview id"); @@ -858,7 +882,7 @@ impl Servo { EmbedderEvent::SendError(top_level_browsing_context_id, e) => { let msg = ConstellationMsg::SendError(top_level_browsing_context_id, e); - if let Err(e) = self.constellation_chan.send(msg) { + if let Err(e) = self.constellation_proxy.try_send(msg) { warn!( "Sending SendError message to constellation failed ({:?}).", e @@ -868,7 +892,7 @@ impl Servo { EmbedderEvent::MediaSessionAction(a) => { let msg = ConstellationMsg::MediaSessionAction(a); - if let Err(e) = self.constellation_chan.send(msg) { + if let Err(e) = self.constellation_proxy.try_send(msg) { warn!( "Sending MediaSessionAction message to constellation failed ({:?}).", e @@ -878,7 +902,7 @@ impl Servo { EmbedderEvent::SetWebViewThrottled(webview_id, throttled) => { let msg = ConstellationMsg::SetWebViewThrottled(webview_id, throttled); - if let Err(e) = self.constellation_chan.send(msg) { + if let Err(e) = self.constellation_proxy.try_send(msg) { warn!( "Sending SetWebViewThrottled to constellation failed ({:?}).", e @@ -888,12 +912,12 @@ impl Servo { EmbedderEvent::Gamepad(gamepad_event) => { let msg = ConstellationMsg::Gamepad(gamepad_event); - if let Err(e) = self.constellation_chan.send(msg) { + if let Err(e) = self.constellation_proxy.try_send(msg) { warn!("Sending Gamepad event to constellation failed ({:?}).", e); } }, EmbedderEvent::Vsync => { - self.compositor.on_vsync(); + self.compositor.borrow_mut().on_vsync(); }, EmbedderEvent::ClipboardAction(clipboard_event) => { self.send_to_constellation(ConstellationMsg::Clipboard(clipboard_event)); @@ -904,7 +928,7 @@ impl Servo { fn send_to_constellation(&self, msg: ConstellationMsg) { let variant_name = msg.variant_name(); - if let Err(e) = self.constellation_chan.send(msg) { + if let Err(e) = self.constellation_proxy.try_send(msg) { warn!("Sending {variant_name} to constellation failed: {e:?}"); } } @@ -913,7 +937,7 @@ impl Servo { while let Some((top_level_browsing_context, msg)) = self.embedder_receiver.try_recv_embedder_msg() { - match (msg, self.compositor.shutdown_state) { + match (msg, self.compositor.borrow().shutdown_state) { (_, ShutdownState::FinishedShuttingDown) => { error!( "embedder shouldn't be handling messages after compositor has shut down" @@ -940,7 +964,7 @@ impl Servo { } pub fn handle_events(&mut self, events: impl IntoIterator) -> bool { - if self.compositor.receive_messages() { + if self.compositor.borrow_mut().receive_messages() { self.receive_messages(); } let mut need_resize = false; @@ -948,8 +972,8 @@ impl Servo { trace!("servo <- embedder EmbedderEvent {:?}", event); need_resize |= self.handle_window_event(event); } - if self.compositor.shutdown_state != ShutdownState::FinishedShuttingDown { - self.compositor.perform_updates(); + if self.compositor.borrow().shutdown_state != ShutdownState::FinishedShuttingDown { + self.compositor.borrow_mut().perform_updates(); } else { self.messages_for_embedder .push((None, EmbedderMsg::Shutdown)); @@ -958,15 +982,15 @@ impl Servo { } pub fn repaint_synchronously(&mut self) { - self.compositor.repaint_synchronously() + self.compositor.borrow_mut().repaint_synchronously() } pub fn pinch_zoom_level(&self) -> f32 { - self.compositor.pinch_zoom_level().get() + self.compositor.borrow_mut().pinch_zoom_level().get() } pub fn setup_logging(&self) { - let constellation_chan = self.constellation_chan.clone(); + let constellation_chan = self.constellation_proxy.sender(); let env = env_logger::Env::default(); let env_logger = EnvLoggerBuilder::from_env(env).build(); let con_logger = FromCompositorLogger::new(constellation_chan); @@ -978,22 +1002,27 @@ impl Servo { log::set_max_level(filter); } - pub fn window(&self) -> &Rc { - &self.compositor.window - } - pub fn deinit(self) { - self.compositor.deinit(); + self.compositor.borrow_mut().deinit(); } pub fn present(&mut self) { - self.compositor.present(); + self.compositor.borrow_mut().present(); } /// Return the OpenGL framebuffer name of the most-recently-completed frame when compositing to /// [`CompositeTarget::Fbo`], or None otherwise. pub fn offscreen_framebuffer_id(&self) -> Option { - self.compositor.offscreen_framebuffer_id() + self.compositor.borrow().offscreen_framebuffer_id() + } + + pub fn new_webview(&self, url: url::Url) -> WebView { + WebView::new(&self.constellation_proxy, self.compositor.clone(), url) + } + + /// FIXME: Remove this once we have a webview delegate. + pub fn new_auxiliary_webview(&self) -> WebView { + WebView::new_auxiliary(&self.constellation_proxy, self.compositor.clone()) } } diff --git a/components/servo/proxies.rs b/components/servo/proxies.rs new file mode 100644 index 00000000000..526eeea21f3 --- /dev/null +++ b/components/servo/proxies.rs @@ -0,0 +1,47 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; + +use compositing_traits::ConstellationMsg; +use crossbeam_channel::{SendError, Sender}; +use log::warn; + +#[derive(Clone)] +pub(crate) struct ConstellationProxy { + sender: Sender, + disconnected: Arc, +} + +impl ConstellationProxy { + pub fn new(sender: Sender) -> Self { + Self { + sender, + disconnected: Arc::default(), + } + } + + pub fn send(&self, msg: ConstellationMsg) { + if self.try_send(msg).is_err() { + warn!("Lost connection to Constellation. Will report to embedder.") + } + } + + pub fn try_send(&self, msg: ConstellationMsg) -> Result<(), SendError> { + if self.disconnected.load(Ordering::SeqCst) { + return Err(SendError(msg)); + } + if let Err(error) = self.sender.send(msg) { + self.disconnected.store(true, Ordering::SeqCst); + return Err(error); + } + + Ok(()) + } + + pub fn sender(&self) -> Sender { + self.sender.clone() + } +} diff --git a/components/servo/webview.rs b/components/servo/webview.rs new file mode 100644 index 00000000000..03dd954f688 --- /dev/null +++ b/components/servo/webview.rs @@ -0,0 +1,117 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use std::cell::RefCell; +use std::rc::Rc; + +use base::id::WebViewId; +use compositing::IOCompositor; +use compositing_traits::ConstellationMsg; +use webrender_api::units::DeviceRect; + +use crate::ConstellationProxy; + +pub struct WebView(Rc); + +struct WebViewInner { + // TODO: ensure that WebView instances interact with the correct Servo instance + pub(crate) id: WebViewId, + pub(crate) constellation_proxy: ConstellationProxy, + pub(crate) compositor: Rc>, +} + +impl Drop for WebViewInner { + fn drop(&mut self) { + self.constellation_proxy + .send(ConstellationMsg::CloseWebView(self.id)); + } +} + +/// Handle for a webview. +/// +/// - The webview exists for exactly as long as there are WebView handles +/// (FIXME: this is not true yet; webviews can still close of their own volition) +/// - All methods are infallible; if the constellation dies, the embedder finds out when calling +/// [Servo::handle_events](crate::Servo::handle_events) +impl WebView { + pub(crate) fn new( + constellation_proxy: &ConstellationProxy, + compositor: Rc>, + url: url::Url, + ) -> Self { + let webview_id = WebViewId::new(); + constellation_proxy.send(ConstellationMsg::NewWebView(url.into(), webview_id)); + + Self(Rc::new(WebViewInner { + id: webview_id, + constellation_proxy: constellation_proxy.clone(), + compositor, + })) + } + + /// FIXME: Remove this once we have a webview delegate. + pub(crate) fn new_auxiliary( + constellation_proxy: &ConstellationProxy, + compositor: Rc>, + ) -> Self { + let webview_id = WebViewId::new(); + + Self( + WebViewInner { + id: webview_id, + constellation_proxy: constellation_proxy.clone(), + compositor, + } + .into(), + ) + } + + /// FIXME: Remove this once we have a webview delegate. + pub fn id(&self) -> WebViewId { + self.0.id + } + + pub fn focus(&self) { + self.0 + .constellation_proxy + .send(ConstellationMsg::FocusWebView(self.id())); + } + + pub fn blur(&self) { + self.0 + .constellation_proxy + .send(ConstellationMsg::BlurWebView); + } + + pub fn move_resize(&self, rect: DeviceRect) { + self.0 + .compositor + .borrow_mut() + .move_resize_webview(self.id(), rect); + } + + pub fn show(&self, hide_others: bool) { + self.0 + .compositor + .borrow_mut() + .show_webview(self.id(), hide_others) + .expect("BUG: invalid WebView instance"); + } + + pub fn hide(&self) { + self.0 + .compositor + .borrow_mut() + .hide_webview(self.id()) + .expect("BUG: invalid WebView instance"); + } + + pub fn raise_to_top(&self, hide_others: bool) { + self.0 + .compositor + .borrow_mut() + .raise_webview_to_top(self.id(), hide_others) + .expect("BUG: invalid WebView instance"); + } +} diff --git a/components/shared/embedder/lib.rs b/components/shared/embedder/lib.rs index 286dc51bff7..1a3632713ab 100644 --- a/components/shared/embedder/lib.rs +++ b/components/shared/embedder/lib.rs @@ -181,7 +181,7 @@ pub enum EmbedderMsg { /// Whether or not to allow a pipeline to load a url. AllowNavigationRequest(PipelineId, ServoUrl), /// Whether or not to allow script to open a new tab/browser - AllowOpeningWebView(IpcSender), + AllowOpeningWebView(IpcSender>), /// A webview was created. WebViewOpened(TopLevelBrowsingContextId), /// A webview was destroyed. diff --git a/ports/servoshell/desktop/app.rs b/ports/servoshell/desktop/app.rs index 75449adac2b..93d2d22cc5d 100644 --- a/ports/servoshell/desktop/app.rs +++ b/ports/servoshell/desktop/app.rs @@ -19,7 +19,7 @@ use servo::config::opts::Opts; use servo::config::prefs::Preferences; use servo::embedder_traits::EventLoopWaker; use servo::servo_config::pref; -use servo::url::ServoUrl; +use servo::servo_url::ServoUrl; use servo::webrender_traits::SurfmanRenderingContext; use servo::Servo; use surfman::Connection; diff --git a/ports/servoshell/desktop/webview.rs b/ports/servoshell/desktop/webview.rs index 2e1a85321c2..7204bf91a6a 100644 --- a/ports/servoshell/desktop/webview.rs +++ b/ports/servoshell/desktop/webview.rs @@ -809,7 +809,7 @@ where EmbedderMsg::AllowOpeningWebView(response_chan) => { // Note: would be a place to handle pop-ups config. // see Step 7 of #the-rules-for-choosing-a-browsing-context-given-a-browsing-context-name - if let Err(e) = response_chan.send(true) { + if let Err(e) = response_chan.send(Some(WebViewId::new())) { warn!("Failed to send AllowOpeningWebView response: {}", e); }; }, diff --git a/ports/servoshell/egl/servo_glue.rs b/ports/servoshell/egl/servo_glue.rs index 4500efd5e05..e5829e02146 100644 --- a/ports/servoshell/egl/servo_glue.rs +++ b/ports/servoshell/egl/servo_glue.rs @@ -526,7 +526,7 @@ impl ServoGlue { EmbedderMsg::AllowOpeningWebView(response_chan) => { // Note: would be a place to handle pop-ups config. // see Step 7 of #the-rules-for-choosing-a-browsing-context-given-a-browsing-context-name - if let Err(e) = response_chan.send(true) { + if let Err(e) = response_chan.send(Some(WebViewId::new())) { warn!("Failed to send AllowOpeningBrowser response: {}", e); }; }, diff --git a/ports/servoshell/prefs.rs b/ports/servoshell/prefs.rs index e732403fa54..2a84b395717 100644 --- a/ports/servoshell/prefs.rs +++ b/ports/servoshell/prefs.rs @@ -14,7 +14,7 @@ use log::{error, warn}; use serde_json::Value; use servo::config::opts::{DebugOptions, Opts, OutputOptions}; use servo::config::prefs::{PrefValue, Preferences}; -use servo::url::ServoUrl; +use servo::servo_url::ServoUrl; use url::Url; #[cfg_attr(any(target_os = "android", target_env = "ohos"), allow(dead_code))]