diff options
-rw-r--r-- | Cargo.lock | 1 | ||||
-rw-r--r-- | components/embedder_traits/lib.rs | 10 | ||||
-rw-r--r-- | ports/glutin/browser.rs | 5 | ||||
-rw-r--r-- | ports/libsimpleservo/api/Cargo.toml | 1 | ||||
-rw-r--r-- | ports/libsimpleservo/api/src/lib.rs | 30 | ||||
-rw-r--r-- | ports/libsimpleservo/capi/src/lib.rs | 42 | ||||
-rw-r--r-- | support/hololens/ServoApp/ServoControl/Servo.cpp | 11 | ||||
-rw-r--r-- | support/hololens/ServoApp/ServoControl/Servo.h | 7 | ||||
-rw-r--r-- | support/hololens/ServoApp/ServoControl/ServoControl.cpp | 32 | ||||
-rw-r--r-- | support/hololens/ServoApp/ServoControl/ServoControl.h | 3 |
10 files changed, 132 insertions, 10 deletions
diff --git a/Cargo.lock b/Cargo.lock index cf9f0fb33db..772adcc29ef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5400,6 +5400,7 @@ dependencies = [ "core-foundation 0.6.4", "getopts", "gl_generator 0.11.0", + "ipc-channel", "libc", "libloading", "libservo", diff --git a/components/embedder_traits/lib.rs b/components/embedder_traits/lib.rs index 45fa6e1d7b4..961cd5e53a4 100644 --- a/components/embedder_traits/lib.rs +++ b/components/embedder_traits/lib.rs @@ -107,6 +107,13 @@ impl EmbedderReceiver { } #[derive(Deserialize, Serialize)] +pub enum ContextMenuResult { + Dismissed, + Ignored, + Selected(usize), +} + +#[derive(Deserialize, Serialize)] pub enum PromptDefinition { /// Show a message. Alert(String, IpcSender<()>), @@ -149,6 +156,8 @@ pub enum EmbedderMsg { ResizeTo(DeviceIntSize), /// Show dialog to user Prompt(PromptDefinition, PromptOrigin), + /// Show a context menu to the user + ShowContextMenu(IpcSender<ContextMenuResult>, Vec<String>), /// 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 @@ -235,6 +244,7 @@ impl Debug for EmbedderMsg { EmbedderMsg::ReportProfile(..) => write!(f, "ReportProfile"), EmbedderMsg::MediaSessionEvent(..) => write!(f, "MediaSessionEvent"), EmbedderMsg::OnDevtoolsStarted(..) => write!(f, "OnDevtoolsStarted"), + EmbedderMsg::ShowContextMenu(..) => write!(f, "ShowContextMenu"), } } } diff --git a/ports/glutin/browser.rs b/ports/glutin/browser.rs index 3aec703c8fc..d92eb1bde9b 100644 --- a/ports/glutin/browser.rs +++ b/ports/glutin/browser.rs @@ -8,7 +8,7 @@ use euclid::{Point2D, Vector2D}; use keyboard_types::{Key, KeyboardEvent, Modifiers, ShortcutMatcher}; use servo::compositing::windowing::{WebRenderDebugOption, WindowEvent}; use servo::embedder_traits::{ - EmbedderMsg, FilterPattern, PermissionRequest, PromptDefinition, PromptOrigin, PromptResult, + ContextMenuResult, EmbedderMsg, FilterPattern, PermissionRequest, PromptDefinition, PromptOrigin, PromptResult, PermissionPrompt, }; use servo::msg::constellation_msg::TopLevelBrowsingContextId as BrowserId; @@ -521,6 +521,9 @@ where Err(()) => error!("Error running devtools server"), } }, + EmbedderMsg::ShowContextMenu(sender, _) => { + let _ = sender.send(ContextMenuResult::Ignored); + } } } } diff --git a/ports/libsimpleservo/api/Cargo.toml b/ports/libsimpleservo/api/Cargo.toml index 66a0cd83a3d..61dc1106de3 100644 --- a/ports/libsimpleservo/api/Cargo.toml +++ b/ports/libsimpleservo/api/Cargo.toml @@ -8,6 +8,7 @@ publish = false [dependencies] getopts = "0.2.11" +ipc-channel = "0.14" libservo = { path = "../../../components/servo" } log = "0.4" servo-media = { git = "https://github.com/servo/media" } diff --git a/ports/libsimpleservo/api/src/lib.rs b/ports/libsimpleservo/api/src/lib.rs index 98a2029d402..c876ffb9fc0 100644 --- a/ports/libsimpleservo/api/src/lib.rs +++ b/ports/libsimpleservo/api/src/lib.rs @@ -8,11 +8,12 @@ extern crate log; pub mod gl_glue; pub use servo::embedder_traits::{ - MediaSessionPlaybackState, PermissionPrompt, PermissionRequest, PromptResult, + ContextMenuResult, MediaSessionPlaybackState, PermissionPrompt, PermissionRequest, PromptResult, }; pub use servo::script_traits::{MediaSessionActionType, MouseButton}; use getopts::Options; +use ipc_channel::ipc::IpcSender; use servo::canvas::{SurfaceProviders, WebGlExecutor}; use servo::compositing::windowing::{ AnimationState, EmbedderCoordinates, EmbedderMethods, MouseWindowEvent, WindowEvent, @@ -103,6 +104,8 @@ pub trait HostTrait { fn prompt_ok_cancel(&self, msg: String, trusted: bool) -> PromptResult; /// Ask for string fn prompt_input(&self, msg: String, default: String, trusted: bool) -> Option<String>; + /// Show context menu + fn show_context_menu(&self, items: Vec<String>); /// Page starts loading. /// "Reload button" should be disabled. /// "Stop button" should be enabled. @@ -164,6 +167,7 @@ pub struct ServoGlue { browsers: Vec<BrowserId>, events: Vec<WindowEvent>, current_url: Option<ServoUrl>, + context_menu_sender: Option<IpcSender<ContextMenuResult>>, } pub fn servo_version() -> String { @@ -240,6 +244,7 @@ pub fn init( browsers: vec![], events: vec![], current_url: Some(url.clone()), + context_menu_sender: None, }; let browser_id = BrowserId::new(); let _ = servo_glue.process_event(WindowEvent::NewBrowser(url, browser_id)); @@ -503,6 +508,18 @@ impl ServoGlue { } } + pub fn on_context_menu_closed( + &mut self, + result: ContextMenuResult, + ) -> Result<(), &'static str> { + if let Some(sender) = self.context_menu_sender.take() { + let _ = sender.send(result); + } else { + warn!("Trying to close a context menu when no context menu is active"); + } + Ok(()) + } + fn process_event(&mut self, event: WindowEvent) -> Result<(), &'static str> { self.events.push(event); if !self.batch_mode { @@ -562,6 +579,17 @@ impl ServoGlue { EmbedderMsg::AllowUnload(sender) => { let _ = sender.send(true); }, + EmbedderMsg::ShowContextMenu(sender, items) => { + if self.context_menu_sender.is_some() { + warn!( + "Trying to show a context menu when a context menu is already active" + ); + let _ = sender.send(ContextMenuResult::Ignored); + } else { + self.context_menu_sender = Some(sender); + self.callbacks.host_callbacks.show_context_menu(items); + } + }, EmbedderMsg::Prompt(definition, origin) => { let cb = &self.callbacks.host_callbacks; let trusted = origin == PromptOrigin::Trusted; diff --git a/ports/libsimpleservo/capi/src/lib.rs b/ports/libsimpleservo/capi/src/lib.rs index 2fd0e48fabe..d55d9f41175 100644 --- a/ports/libsimpleservo/capi/src/lib.rs +++ b/ports/libsimpleservo/capi/src/lib.rs @@ -17,7 +17,7 @@ use env_logger; use log::LevelFilter; use simpleservo::{self, gl_glue, ServoGlue, SERVO}; use simpleservo::{ - Coordinates, EventLoopWaker, HostTrait, InitOptions, MediaSessionActionType, + ContextMenuResult, Coordinates, EventLoopWaker, HostTrait, InitOptions, MediaSessionActionType, MediaSessionPlaybackState, MouseButton, PromptResult, VRInitOptions, }; use std::ffi::{CStr, CString}; @@ -230,6 +230,7 @@ pub struct CHostCallbacks { trusted: bool, ) -> *const c_char, pub on_devtools_started: extern "C" fn(result: CDevtoolsServerState, port: c_uint), + pub show_context_menu: extern "C" fn(items_list: *const *const c_char, items_size: u32), } /// Servo options @@ -332,6 +333,25 @@ impl CMediaSessionActionType { } } +#[repr(C)] +pub enum CContextMenuResult { + Ignored, + Selected, + // Can't use Dismissed. Already used by PromptResult. See: + // https://github.com/servo/servo/issues/25986 + Dismissed_, +} + +impl CContextMenuResult { + pub fn convert(&self, idx: u32) -> ContextMenuResult { + match self { + CContextMenuResult::Ignored => ContextMenuResult::Ignored, + CContextMenuResult::Dismissed_ => ContextMenuResult::Dismissed, + CContextMenuResult::Selected => ContextMenuResult::Selected(idx as usize), + } + } +} + /// The returned string is not freed. This will leak. #[no_mangle] pub extern "C" fn servo_version() -> *const c_char { @@ -495,6 +515,14 @@ pub extern "C" fn set_batch_mode(batch: bool) { } #[no_mangle] +pub extern "C" fn on_context_menu_closed(result: CContextMenuResult, item: u32) { + catch_any_panic(|| { + debug!("on_context_menu_closed"); + call(|s| s.on_context_menu_closed(result.convert(item))); + }); +} + +#[no_mangle] pub extern "C" fn resize(width: i32, height: i32) { catch_any_panic(|| { debug!("resize {}/{}", width, height); @@ -874,4 +902,16 @@ impl HostTrait for HostCallbacks { }, } } + + fn show_context_menu(&self, items: Vec<String>) { + debug!("show_context_menu"); + let size = items.len() as u32; + let cstrs: Vec<CString> = items + .into_iter() + .map(|i| CString::new(i).expect("Can't create string")) + .collect(); + let ptrs: Vec<*const c_char> = cstrs.iter().map(|cstr| cstr.as_ptr()).collect(); + (self.0.show_context_menu)(ptrs.as_ptr(), size); + // let _ = cstrs; // Don't drop these too early + } } diff --git a/support/hololens/ServoApp/ServoControl/Servo.cpp b/support/hololens/ServoApp/ServoControl/Servo.cpp index 88c2376283b..96438300ac4 100644 --- a/support/hololens/ServoApp/ServoControl/Servo.cpp +++ b/support/hololens/ServoApp/ServoControl/Servo.cpp @@ -43,7 +43,7 @@ void on_ime_state_changed(bool aShow) { sServo->Delegate().OnServoIMEStateChanged(aShow); } -void set_clipboard_contents(const char *content) { +void set_clipboard_contents(const char *) { // FIXME } @@ -67,6 +67,14 @@ void prompt_alert(const char *message, bool trusted) { sServo->Delegate().OnServoPromptAlert(char2hstring(message), trusted); } +void show_context_menu(const char *const *items_list, uint32_t items_size) { + std::vector<winrt::hstring> items; + for (uint32_t i = 0; i < items_size; i++) { + items.push_back(char2hstring(items_list[i])); + } + sServo->Delegate().OnServoShowContextMenu(items); +} + void on_devtools_started(Servo::DevtoolsServerState result, const unsigned int port) { sServo->Delegate().OnServoDevtoolsStarted( @@ -150,6 +158,7 @@ Servo::Servo(hstring url, hstring args, GLsizei width, GLsizei height, c.prompt_yes_no = &prompt_yes_no; c.prompt_input = &prompt_input; c.on_devtools_started = &on_devtools_started; + c.show_context_menu = &show_context_menu; capi::register_panic_handler(&on_panic); diff --git a/support/hololens/ServoApp/ServoControl/Servo.h b/support/hololens/ServoApp/ServoControl/Servo.h index 1667118b499..2b396f6d09d 100644 --- a/support/hololens/ServoApp/ServoControl/Servo.h +++ b/support/hololens/ServoApp/ServoControl/Servo.h @@ -29,6 +29,7 @@ public: typedef capi::CMouseButton MouseButton; typedef capi::CPromptResult PromptResult; + typedef capi::CContextMenuResult ContextMenuResult; typedef capi::CMediaSessionActionType MediaSessionActionType; typedef capi::CMediaSessionPlaybackState MediaSessionPlaybackState; typedef capi::CDevtoolsServerState DevtoolsServerState; @@ -74,6 +75,9 @@ public: void SendMediaSessionAction(capi::CMediaSessionActionType action) { capi::media_session_action(action); } + void ContextMenuClosed(capi::CContextMenuResult res, unsigned int idx) { + capi::on_context_menu_closed(res, idx); + } private: ServoDelegate &mDelegate; @@ -95,12 +99,13 @@ public: virtual bool OnServoAllowNavigation(hstring) = 0; virtual void OnServoAnimatingChanged(bool) = 0; virtual void OnServoIMEStateChanged(bool) = 0; - virtual void OnServoDevtoolsStarted(bool, unsigned int) = 0; + virtual void OnServoDevtoolsStarted(bool, const unsigned int) = 0; virtual void Flush() = 0; virtual void MakeCurrent() = 0; virtual void OnServoMediaSessionMetadata(hstring, hstring, hstring) = 0; virtual void OnServoMediaSessionPlaybackStateChange(int) = 0; virtual void OnServoPromptAlert(hstring, bool) = 0; + virtual void OnServoShowContextMenu(std::vector<hstring>) = 0; virtual Servo::PromptResult OnServoPromptOkCancel(hstring, bool) = 0; virtual Servo::PromptResult OnServoPromptYesNo(hstring, bool) = 0; virtual std::optional<hstring> OnServoPromptInput(hstring, hstring, bool) = 0; diff --git a/support/hololens/ServoApp/ServoControl/ServoControl.cpp b/support/hololens/ServoApp/ServoControl/ServoControl.cpp index cf3393f976e..810efed02e1 100644 --- a/support/hololens/ServoApp/ServoControl/ServoControl.cpp +++ b/support/hololens/ServoApp/ServoControl/ServoControl.cpp @@ -233,9 +233,9 @@ void ServoControl::OnSurfaceWheelChanged( void ServoControl::OnSurfaceResized(IInspectable const &, SizeChangedEventArgs const &e) { auto size = e.NewSize(); - auto w = size.Width * mDPI; - auto h = size.Height * mDPI; - RunOnGLThread([=] { mServo->SetSize(w, h); }); + auto w = (size.Width * mDPI); + auto h = (size.Height * mDPI); + RunOnGLThread([=] { mServo->SetSize((GLsizei)w, (GLsizei)h); }); } void ServoControl::GoBack() { @@ -435,7 +435,7 @@ void ServoControl::OnServoAnimatingChanged(bool animating) { WakeConditionVariable(&mGLCondVar); } -void ServoControl::OnServoIMEStateChanged(bool aShow) { +void ServoControl::OnServoIMEStateChanged(bool) { // FIXME: // https://docs.microsoft.com/en-us/windows/win32/winauto/uiauto-implementingtextandtextrange } @@ -564,6 +564,30 @@ void ServoControl::OnServoDevtoolsStarted(bool success, }); } +void ServoControl::OnServoShowContextMenu(std::vector<winrt::hstring> items) { + RunOnUIThread([=] { + MessageDialog msg{L"Menu"}; + for (auto i = 0; i < items.size(); i++) { + UICommand cmd{items[i], [=](auto) { + RunOnGLThread([=] { + mServo->ContextMenuClosed( + Servo::ContextMenuResult::Selected, i); + }); + }}; + msg.Commands().Append(cmd); + } + UICommand cancel{L"Cancel", [=](auto) { + RunOnGLThread([=] { + mServo->ContextMenuClosed( + Servo::ContextMenuResult::Dismissed_, 0); + }); + }}; + msg.Commands().Append(cancel); + msg.CancelCommandIndex((uint32_t)items.size()); + msg.ShowAsync(); + }); +} + template <typename Callable> void ServoControl::RunOnUIThread(Callable cb) { Dispatcher().RunAsync(CoreDispatcherPriority::High, cb); } diff --git a/support/hololens/ServoApp/ServoControl/ServoControl.h b/support/hololens/ServoApp/ServoControl/ServoControl.h index 685f038311b..889c3510bfb 100644 --- a/support/hololens/ServoApp/ServoControl/ServoControl.h +++ b/support/hololens/ServoApp/ServoControl/ServoControl.h @@ -117,12 +117,13 @@ struct ServoControl : ServoControlT<ServoControl>, public servo::ServoDelegate { winrt::hstring); virtual void OnServoMediaSessionPlaybackStateChange(int); virtual void OnServoPromptAlert(winrt::hstring, bool); + virtual void OnServoShowContextMenu(std::vector<winrt::hstring>); virtual servo::Servo::PromptResult OnServoPromptOkCancel(winrt::hstring, bool); virtual servo::Servo::PromptResult OnServoPromptYesNo(winrt::hstring, bool); virtual std::optional<hstring> OnServoPromptInput(winrt::hstring, winrt::hstring, bool); - virtual void OnServoDevtoolsStarted(bool success, const unsigned int port); + virtual void OnServoDevtoolsStarted(bool, const unsigned int); DevtoolsStatus GetDevtoolsStatus(); |