From 5b17b5d8716ed2199091ccc3616d490a1af17fd4 Mon Sep 17 00:00:00 2001 From: Wu Yu Wei Date: Tue, 15 Oct 2024 13:38:30 +0900 Subject: [PATCH 1/2] Register verso protocol --- Cargo.lock | 2 ++ Cargo.toml | 2 ++ src/compositor.rs | 16 ++++++------- src/config.rs | 45 +++++++++++++++++++++++++++++++++++ src/verso.rs | 24 ++++++------------- versoview/src/lib.rs | 1 + versoview_messages/src/lib.rs | 1 + 7 files changed, 66 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fd375c3b..bcc8deb6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6254,12 +6254,14 @@ dependencies = [ "gleam", "glutin", "glutin-winit", + "headers", "ipc-channel", "keyboard-types", "layout_thread_2020", "log", "media", "net", + "net_traits", "objc2", "objc2-app-kit", "profile", diff --git a/Cargo.toml b/Cargo.toml index ed1fb48f..22447042 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -80,6 +80,7 @@ fonts = { git = "https://github.com/servo/servo.git", rev = "fc0835b" } layout_thread_2020 = { git = "https://github.com/servo/servo.git", rev = "fc0835b" } media = { git = "https://github.com/servo/servo.git", rev = "fc0835b" } net = { git = "https://github.com/servo/servo.git", rev = "fc0835b" } +net_traits = { git = "https://github.com/servo/servo.git", rev = "fc0835b" } profile = { git = "https://github.com/servo/servo.git", rev = "fc0835b" } profile_traits = { git = "https://github.com/servo/servo.git", rev = "fc0835b" } script = { git = "https://github.com/servo/servo.git", rev = "fc0835b" } @@ -104,6 +105,7 @@ cargo-packager-resource-resolver = { version = "0.1.1", features = [ "auto-detect-format", ], optional = true } url = "2.5.2" +headers = "0.3" versoview_messages = { path = "./versoview_messages" } [target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies] diff --git a/src/compositor.rs b/src/compositor.rs index 7ef71210..02701624 100644 --- a/src/compositor.rs +++ b/src/compositor.rs @@ -41,7 +41,7 @@ use webrender_api::{ }; use webrender_traits::display_list::{HitTestInfo, ScrollTree}; use webrender_traits::{ - CompositorHitTestResult, CrossProcessCompositorMessage, ImageUpdate, UntrustedNodeAddress + CompositorHitTestResult, CrossProcessCompositorMessage, ImageUpdate, UntrustedNodeAddress, }; use winit::window::WindowId; @@ -572,7 +572,7 @@ impl IOCompositor { CompositorMsg::CrossProcess(cross_proces_message) => { self.handle_cross_process_message(cross_proces_message); - }, + } } true @@ -707,11 +707,11 @@ impl IOCompositor { match update { ImageUpdate::AddImage(key, desc, data) => { txn.add_image(key, desc, data.into(), None) - }, + } ImageUpdate::DeleteImage(key) => txn.delete_image(key), ImageUpdate::UpdateImage(key, desc, data) => { txn.update_image(key, desc, data.into(), &DirtyRect::All) - }, + } } } self.webrender_api @@ -727,7 +727,7 @@ impl IOCompositor { transaction.add_native_font(font_key, native_handle); self.webrender_api .send_transaction(self.webrender_document, transaction); - }, + } CrossProcessCompositorMessage::AddFontInstance( font_instance_key, @@ -777,13 +777,13 @@ impl IOCompositor { if let Err(e) = req.send(self.viewport.into()) { warn!("Sending response to get client window failed ({:?}).", e); } - }, + } CrossProcessCompositorMessage::GetScreenSize(req) => { if let Err(e) = req.send(self.viewport) { warn!("Sending response to get screen size failed ({:?}).", e); } - }, + } CrossProcessCompositorMessage::GetAvailableScreenSize(req) => { if let Err(e) = req.send(self.viewport) { @@ -833,7 +833,7 @@ impl IOCompositor { .map(|_| self.webrender_api.generate_font_instance_key()) .collect(); let _ = result_sender.send((font_keys, font_instance_keys)); - }, + } CompositorMsg::CrossProcess(CrossProcessCompositorMessage::GetClientWindowRect( req, )) => { diff --git a/src/config.rs b/src/config.rs index 538484e7..3a75ab09 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,6 +1,13 @@ use std::{fs, path::PathBuf}; use embedder_traits::resources::{self, Resource, ResourceReaderMethods}; +use headers::{ContentType, HeaderMapExt}; +use net::protocols::{ProtocolHandler, ProtocolRegistry}; +use net_traits::{ + request::Request, + response::{Response, ResponseBody}, + ResourceFetchTiming, +}; use servo_config::opts::{default_opts, set_options, Opts}; /// Configuration of Verso instance. @@ -20,6 +27,14 @@ impl Config { Self { opts, resource_dir } } + /// Register URL scheme protocols + pub fn create_protocols(&self) -> ProtocolRegistry { + let handler = ResourceReader(self.resource_dir.clone()); + let mut protocols = ProtocolRegistry::with_internal_protocols(); + protocols.register("verso", handler); + protocols + } + /// Init options and preferences. pub fn init(self) { // Set the resource files and preferences of Servo. @@ -46,3 +61,33 @@ impl ResourceReaderMethods for ResourceReader { vec![] } } + +impl ProtocolHandler for ResourceReader { + fn load( + &self, + request: &mut Request, + _done_chan: &mut net::fetch::methods::DoneChannel, + _context: &net::fetch::methods::FetchContext, + ) -> std::pin::Pin + Send>> { + let path = self.0.join(request.current_url().domain().unwrap()); + + let response = if let Ok(file) = fs::read(path) { + let mut response = Response::new( + request.current_url(), + ResourceFetchTiming::new(request.timing_type()), + ); + + // Set Content-Type header. + // TODO: We assume it's HTML for now. This should be updated once we have IPC interface. + response.headers.typed_insert(ContentType::html()); + + *response.body.lock().unwrap() = ResponseBody::Done(file); + + response + } else { + Response::network_internal_error("Opening file failed") + }; + + Box::pin(std::future::ready(response)) + } +} diff --git a/src/verso.rs b/src/verso.rs index ff349078..bab97d6a 100644 --- a/src/verso.rs +++ b/src/verso.rs @@ -1,7 +1,6 @@ use std::{ borrow::Cow, collections::HashMap, - path::PathBuf, sync::{atomic::Ordering, Arc}, }; @@ -10,9 +9,7 @@ use base::id::WebViewId; use bluetooth::BluetoothThreadFactory; use bluetooth_traits::BluetoothRequest; use canvas::canvas_paint_thread::CanvasPaintThread; -use compositing_traits::{ - CompositorMsg, CompositorProxy, CompositorReceiver, ConstellationMsg, -}; +use compositing_traits::{CompositorMsg, CompositorProxy, CompositorReceiver, ConstellationMsg}; use constellation::{Constellation, FromCompositorLogger, InitialConstellationState}; use crossbeam_channel::{unbounded, Sender}; use devtools; @@ -24,7 +21,7 @@ use ipc_channel::router::ROUTER; use layout_thread_2020; use log::{Log, Metadata, Record}; use media::{GlApi, GlContext, NativeDisplay, WindowGLContext}; -use net::{protocols::ProtocolRegistry, resource_thread}; +use net::resource_thread; use profile; use script::{self, JSEngineSetup}; use script_traits::WindowSizeData; @@ -62,7 +59,6 @@ pub struct Verso { _js_engine_setup: Option, /// FIXME: It's None on wayland in Flatpak. Find a way to support this. clipboard: Option, - resource_dir: PathBuf, } impl Verso { @@ -84,7 +80,7 @@ impl Verso { /// - Image Cache: Enabled pub fn new(evl: &ActiveEventLoop, proxy: EventLoopProxy<()>, config: Config) -> Self { // Initialize configurations and Verso window - let resource_dir = config.resource_dir.clone(); + let protocols = config.create_protocols(); config.init(); let (window, rendering_context) = Window::new(evl); let event_loop_waker = Box::new(Waker(proxy)); @@ -262,9 +258,6 @@ impl Verso { let bluetooth_thread: IpcSender = BluetoothThreadFactory::new(embedder_sender.clone()); - // Register URL scheme protocols - let protocols = ProtocolRegistry::with_internal_protocols(); - // Create resource thread pool let user_agent: Cow<'static, str> = default_user_agent_string().into(); let (public_resource_threads, private_resource_threads) = @@ -282,7 +275,8 @@ impl Verso { // Create font cache thread let system_font_service = Arc::new( - SystemFontService::spawn(compositor_sender.cross_process_compositor_api.clone()).to_proxy(), + SystemFontService::spawn(compositor_sender.cross_process_compositor_api.clone()) + .to_proxy(), ); // Create canvas thread @@ -369,8 +363,7 @@ impl Verso { // Send the constellation message to start Panel UI // TODO: Should become a window method let panel_id = window.panel.as_ref().unwrap().webview_id; - let path = resource_dir.join("panel.html"); - let url = ServoUrl::from_file_path(path.to_str().unwrap()).unwrap(); + let url = ServoUrl::parse("verso://panel.html").unwrap(); send_to_constellation( &constellation_sender, ConstellationMsg::NewWebView(url, panel_id), @@ -387,7 +380,6 @@ impl Verso { embedder_receiver, _js_engine_setup: js_engine_setup, clipboard: Clipboard::new().ok(), - resource_dir, }; verso.setup_logging(); @@ -445,10 +437,8 @@ impl Verso { let mut window = Window::new_with_compositor(evl, compositor); let panel_id = WebViewId::new(); - let path = self.resource_dir.join("panel.html"); let url = - ServoUrl::from_file_path(path.to_str().unwrap()) - .unwrap(); + ServoUrl::parse("verso://panel.html").unwrap(); send_to_constellation( &self.constellation_sender, ConstellationMsg::NewWebView(url, panel_id), diff --git a/versoview/src/lib.rs b/versoview/src/lib.rs index e69de29b..8b137891 100644 --- a/versoview/src/lib.rs +++ b/versoview/src/lib.rs @@ -0,0 +1 @@ + diff --git a/versoview_messages/src/lib.rs b/versoview_messages/src/lib.rs index e69de29b..8b137891 100644 --- a/versoview_messages/src/lib.rs +++ b/versoview_messages/src/lib.rs @@ -0,0 +1 @@ + From 62beed935ac6884e9b6b1289151836d8d951ae56 Mon Sep 17 00:00:00 2001 From: Wu Yu Wei Date: Tue, 15 Oct 2024 13:54:34 +0900 Subject: [PATCH 2/2] Make webivew generic --- src/verso.rs | 4 +++- src/webview.rs | 23 +---------------------- src/window.rs | 17 +++++++++++++++-- 3 files changed, 19 insertions(+), 25 deletions(-) diff --git a/src/verso.rs b/src/verso.rs index bab97d6a..f97413ca 100644 --- a/src/verso.rs +++ b/src/verso.rs @@ -5,7 +5,7 @@ use std::{ }; use arboard::Clipboard; -use base::id::WebViewId; +use base::id::{PipelineNamespace, PipelineNamespaceId, WebViewId}; use bluetooth::BluetoothThreadFactory; use bluetooth_traits::BluetoothRequest; use canvas::canvas_paint_thread::CanvasPaintThread; @@ -82,6 +82,8 @@ impl Verso { // Initialize configurations and Verso window let protocols = config.create_protocols(); config.init(); + // Reserving a namespace to create TopLevelBrowsingContextId. + PipelineNamespace::install(PipelineNamespaceId(0)); let (window, rendering_context) = Window::new(evl); let event_loop_waker = Box::new(Waker(proxy)); let opts = opts::get(); diff --git a/src/webview.rs b/src/webview.rs index 6c559fa4..b44561d2 100644 --- a/src/webview.rs +++ b/src/webview.rs @@ -1,5 +1,5 @@ use arboard::Clipboard; -use base::id::{BrowsingContextId, PipelineNamespace, PipelineNamespaceId, WebViewId}; +use base::id::{BrowsingContextId, WebViewId}; use compositing_traits::ConstellationMsg; use crossbeam_channel::Sender; use embedder_traits::{CompositorEventVariant, EmbedderMsg, PromptDefinition}; @@ -29,27 +29,6 @@ impl WebView { Self { webview_id, rect } } - /// Create a panel view from Winit window. A panel is a special web view that focus on controlling states around window. - /// It could be treated as the control panel or navigation bar of the window depending on usages. - /// - /// At the moment, following Web API is supported: - /// - Close window: `window.close()` - /// - Navigate to previous page: `window.prompt('PREV')` - /// - Navigate to next page: `window.prompt('FORWARD')` - /// - Refresh the page: `window.prompt('REFRESH')` - /// - Minimize the window: `window.prompt('MINIMIZE')` - /// - Maximize the window: `window.prompt('MAXIMIZE')` - /// - Navigate to a specific URL: `window.prompt('NAVIGATE_TO:${url}')` - pub fn new_panel(rect: DeviceIntRect) -> Self { - // Reserving a namespace to create TopLevelBrowsingContextId. - PipelineNamespace::install(PipelineNamespaceId(0)); - let id = WebViewId::new(); - Self { - webview_id: id, - rect, - } - } - /// Set the webview size corresponding to the window size. pub fn set_size(&mut self, mut rect: DeviceIntRect) { rect.min.y = rect.max.y.min(100); diff --git a/src/window.rs b/src/window.rs index 3fa20ad4..f3bdd6cb 100644 --- a/src/window.rs +++ b/src/window.rs @@ -39,7 +39,17 @@ pub struct Window { pub(crate) window: WinitWindow, /// GL surface of the window pub(crate) surface: Surface, - /// The main control panel of this window. + /// The main panel of this window. A panel is a special web view that focus on controlling states around window. + /// It could be treated as the control panel or navigation bar of the window depending on usages. + /// + /// At the moment, following Web API is supported: + /// - Close window: `window.close()` + /// - Navigate to previous page: `window.prompt('PREV')` + /// - Navigate to next page: `window.prompt('FORWARD')` + /// - Refresh the page: `window.prompt('REFRESH')` + /// - Minimize the window: `window.prompt('MINIMIZE')` + /// - Maximize the window: `window.prompt('MAXIMIZE')` + /// - Navigate to a specific URL: `window.prompt('NAVIGATE_TO:${url}')` pub(crate) panel: Option, /// The WebView of this window. pub(crate) webview: Option, @@ -89,7 +99,10 @@ impl Window { Self { window, surface, - panel: Some(WebView::new_panel(DeviceIntRect::from_size(size))), + panel: Some(WebView::new( + WebViewId::new(), + DeviceIntRect::from_size(size), + )), webview: None, mouse_position: Cell::new(PhysicalPosition::default()), modifiers_state: Cell::new(ModifiersState::default()),