mirror of
https://github.com/servo/servo.git
synced 2025-09-03 11:38:22 +01:00
Allow OHOS servoshell to have a simple multiple tab implementation. (#36891)
Currently we just pause the compositor and replace the window in it while having separate bookkeeping to remember which window belongs to which tab. Currently there are no tests for OHOS, so we cannot test the changes. --------- Signed-off-by: Narfinger <Narfinger@users.noreply.github.com> Co-authored-by: Jonathan Schwender <55576758+jschwe@users.noreply.github.com>
This commit is contained in:
parent
71bf9fb92d
commit
3b73b83a9f
6 changed files with 245 additions and 87 deletions
|
@ -6,8 +6,9 @@
|
|||
use std::cell::RefCell;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::os::raw::c_void;
|
||||
use std::rc::Rc;
|
||||
use std::sync::mpsc::{Receiver, Sender};
|
||||
use std::sync::{LazyLock, Once, OnceLock, mpsc};
|
||||
use std::sync::{LazyLock, Mutex, Once, OnceLock, mpsc};
|
||||
use std::thread;
|
||||
use std::thread::sleep;
|
||||
use std::time::Duration;
|
||||
|
@ -23,7 +24,7 @@ use ohos_ime_sys::types::InputMethod_EnterKeyType;
|
|||
use servo::style::Zero;
|
||||
use servo::{
|
||||
AlertResponse, EventLoopWaker, InputMethodType, LoadStatus, MediaSessionPlaybackState,
|
||||
PermissionRequest, SimpleDialog, WebView,
|
||||
PermissionRequest, SimpleDialog, WebView, WebViewId,
|
||||
};
|
||||
use xcomponent_sys::{
|
||||
OH_NativeXComponent, OH_NativeXComponent_Callback, OH_NativeXComponent_GetKeyEvent,
|
||||
|
@ -65,9 +66,11 @@ fn call(action: ServoAction) -> Result<(), CallError> {
|
|||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
struct XComponentWrapper(*mut OH_NativeXComponent);
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct XComponentWrapper(*mut OH_NativeXComponent);
|
||||
#[repr(transparent)]
|
||||
struct WindowWrapper(*mut c_void);
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct WindowWrapper(*mut c_void);
|
||||
unsafe impl Send for XComponentWrapper {}
|
||||
unsafe impl Send for WindowWrapper {}
|
||||
|
||||
|
@ -80,7 +83,6 @@ pub(super) enum TouchEventType {
|
|||
Unknown,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) enum ServoAction {
|
||||
WakeUp,
|
||||
LoadUrl(String),
|
||||
|
@ -104,6 +106,8 @@ pub(super) enum ServoAction {
|
|||
width: i32,
|
||||
height: i32,
|
||||
},
|
||||
FocusWebview(u32),
|
||||
NewWebview(XComponentWrapper, WindowWrapper),
|
||||
}
|
||||
|
||||
/// Queue length for the thread-safe function to submit URL updates to ArkTS
|
||||
|
@ -125,6 +129,20 @@ static PROMPT_TOAST: OnceLock<
|
|||
ThreadsafeFunction<String, (), String, false, false, PROMPT_QUEUE_SIZE>,
|
||||
> = OnceLock::new();
|
||||
|
||||
/// Storing webview related items
|
||||
struct NativeWebViewComponents {
|
||||
/// The id of the related webview
|
||||
id: WebViewId,
|
||||
/// The XComponentWrapper for the above webview
|
||||
xcomponent: XComponentWrapper,
|
||||
/// The WindowWrapper for the above webview
|
||||
window: WindowWrapper,
|
||||
}
|
||||
|
||||
/// Currently we do not support different contexts for different windows but we might want to change tabs.
|
||||
/// For this we store the window context for every tab and change the compositor by hand.
|
||||
static NATIVE_WEBVIEWS: Mutex<Vec<NativeWebViewComponents>> = Mutex::new(Vec::new());
|
||||
|
||||
impl ServoAction {
|
||||
fn dispatch_touch_event(
|
||||
servo: &RunningAppState,
|
||||
|
@ -143,7 +161,7 @@ impl ServoAction {
|
|||
}
|
||||
|
||||
// todo: consider making this take `self`, so we don't need to needlessly clone.
|
||||
fn do_action(&self, servo: &RunningAppState) {
|
||||
fn do_action(&self, servo: &Rc<RunningAppState>) {
|
||||
use ServoAction::*;
|
||||
match self {
|
||||
WakeUp => servo.perform_updates(),
|
||||
|
@ -155,7 +173,7 @@ impl ServoAction {
|
|||
x,
|
||||
y,
|
||||
pointer_id,
|
||||
} => Self::dispatch_touch_event(servo, *kind, *x, *y, *pointer_id),
|
||||
} => Self::dispatch_touch_event(&servo, *kind, *x, *y, *pointer_id),
|
||||
KeyUp(k) => servo.key_up(k.clone()),
|
||||
KeyDown(k) => servo.key_down(k.clone()),
|
||||
InsertText(text) => servo.ime_insert_text(text.clone()),
|
||||
|
@ -183,6 +201,56 @@ impl ServoAction {
|
|||
servo.present_if_needed();
|
||||
},
|
||||
Resize { width, height } => servo.resize(Coordinates::new(0, 0, *width, *height)),
|
||||
FocusWebview(arkts_id) => {
|
||||
if let Some(native_webview_components) =
|
||||
NATIVE_WEBVIEWS.lock().unwrap().get(*arkts_id as usize)
|
||||
{
|
||||
if (servo.active_webview().id() != native_webview_components.id) {
|
||||
servo.focus_webview(native_webview_components.id);
|
||||
servo.pause_compositor();
|
||||
let (window_handle, _, coordinates) = simpleservo::get_raw_window_handle(
|
||||
native_webview_components.xcomponent.0,
|
||||
native_webview_components.window.0,
|
||||
);
|
||||
servo.resume_compositor(window_handle, coordinates);
|
||||
let url = servo
|
||||
.active_webview()
|
||||
.url()
|
||||
.map(|u| u.to_string())
|
||||
.unwrap_or(String::from("about:blank"));
|
||||
SET_URL_BAR_CB
|
||||
.get()
|
||||
.map(|f| f.call(url, ThreadsafeFunctionCallMode::Blocking));
|
||||
}
|
||||
} else {
|
||||
error!("Could not find webview to focus");
|
||||
}
|
||||
},
|
||||
NewWebview(xcomponent, window) => {
|
||||
servo.pause_compositor();
|
||||
servo.new_toplevel_webview("about:blank".parse().unwrap());
|
||||
let (window_handle, _, coordinates) =
|
||||
simpleservo::get_raw_window_handle(xcomponent.0, window.0);
|
||||
|
||||
servo.resume_compositor(window_handle, coordinates);
|
||||
let webview = servo.newest_webview().expect("There should always be one");
|
||||
let id = webview.id();
|
||||
NATIVE_WEBVIEWS
|
||||
.lock()
|
||||
.unwrap()
|
||||
.push(NativeWebViewComponents {
|
||||
id: id,
|
||||
xcomponent: xcomponent.clone(),
|
||||
window: window.clone(),
|
||||
});
|
||||
let url = webview
|
||||
.url()
|
||||
.map(|u| u.to_string())
|
||||
.unwrap_or(String::from("about:blank"));
|
||||
SET_URL_BAR_CB
|
||||
.get()
|
||||
.map(|f| f.call(url, ThreadsafeFunctionCallMode::Blocking));
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -221,50 +289,64 @@ extern "C" fn on_surface_created_cb(xcomponent: *mut OH_NativeXComponent, window
|
|||
let xc_wrapper = XComponentWrapper(xcomponent);
|
||||
let window_wrapper = WindowWrapper(window);
|
||||
|
||||
// Todo: Perhaps it would be better to move this thread into the vsync signal thread.
|
||||
// This would allow us to save one thread and the IPC for the vsync signal.
|
||||
//
|
||||
// Each thread will send its id via the channel
|
||||
let _main_surface_thread = thread::spawn(move || {
|
||||
let (tx, rx): (Sender<ServoAction>, Receiver<ServoAction>) = mpsc::channel();
|
||||
if !SERVO_CHANNEL.get().is_some() {
|
||||
// Todo: Perhaps it would be better to move this thread into the vsync signal thread.
|
||||
// This would allow us to save one thread and the IPC for the vsync signal.
|
||||
//
|
||||
// Each thread will send its id via the channel
|
||||
let _main_surface_thread = thread::spawn(move || {
|
||||
let (tx, rx): (Sender<ServoAction>, Receiver<ServoAction>) = mpsc::channel();
|
||||
|
||||
SERVO_CHANNEL
|
||||
.set(tx.clone())
|
||||
.expect("Servo channel already initialized");
|
||||
SERVO_CHANNEL
|
||||
.set(tx.clone())
|
||||
.expect("Servo channel already initialized");
|
||||
|
||||
let wakeup = Box::new(WakeupCallback::new(tx));
|
||||
let callbacks = Box::new(HostCallbacks::new());
|
||||
let wakeup = Box::new(WakeupCallback::new(tx));
|
||||
let callbacks = Box::new(HostCallbacks::new());
|
||||
|
||||
let xc = xc_wrapper;
|
||||
let window = window_wrapper;
|
||||
let init_opts = if let Ok(ServoAction::Initialize(init_opts)) = rx.recv() {
|
||||
init_opts
|
||||
} else {
|
||||
panic!("Servos GL thread received another event before it was initialized")
|
||||
};
|
||||
let servo = simpleservo::init(*init_opts, window.0, xc.0, wakeup, callbacks)
|
||||
.expect("Servo initialization failed");
|
||||
let xc = xc_wrapper;
|
||||
let window = window_wrapper;
|
||||
|
||||
info!("Surface created!");
|
||||
let native_vsync =
|
||||
ohos_vsync::NativeVsync::new("ServoVsync").expect("Failed to create NativeVsync");
|
||||
// get_period() returns an error - perhaps we need to wait until the first callback?
|
||||
// info!("Native vsync period is {} nanoseconds", native_vsync.get_period().unwrap());
|
||||
unsafe {
|
||||
native_vsync
|
||||
.request_raw_callback_with_self(Some(on_vsync_cb))
|
||||
.expect("Failed to request vsync callback")
|
||||
}
|
||||
info!("Enabled Vsync!");
|
||||
let init_opts = if let Ok(ServoAction::Initialize(init_opts)) = rx.recv() {
|
||||
init_opts
|
||||
} else {
|
||||
panic!("Servos GL thread received another event before it was initialized")
|
||||
};
|
||||
let servo = simpleservo::init(*init_opts, window.0, xc.0, wakeup, callbacks)
|
||||
.expect("Servo initialization failed");
|
||||
|
||||
while let Ok(action) = rx.recv() {
|
||||
trace!("Wakeup message received!");
|
||||
action.do_action(&servo);
|
||||
}
|
||||
NATIVE_WEBVIEWS
|
||||
.lock()
|
||||
.unwrap()
|
||||
.push(NativeWebViewComponents {
|
||||
id: servo.active_webview().id(),
|
||||
xcomponent: xc,
|
||||
window,
|
||||
});
|
||||
|
||||
info!("Sender disconnected - Terminating main surface thread");
|
||||
});
|
||||
info!("Surface created!");
|
||||
let native_vsync =
|
||||
ohos_vsync::NativeVsync::new("ServoVsync").expect("Failed to create NativeVsync");
|
||||
// get_period() returns an error - perhaps we need to wait until the first callback?
|
||||
// info!("Native vsync period is {} nanoseconds", native_vsync.get_period().unwrap());
|
||||
unsafe {
|
||||
native_vsync
|
||||
.request_raw_callback_with_self(Some(on_vsync_cb))
|
||||
.expect("Failed to request vsync callback")
|
||||
}
|
||||
info!("Enabled Vsync!");
|
||||
|
||||
while let Ok(action) = rx.recv() {
|
||||
trace!("Wakeup message received!");
|
||||
action.do_action(&servo);
|
||||
}
|
||||
|
||||
info!("Sender disconnected - Terminating main surface thread");
|
||||
});
|
||||
} else {
|
||||
call(ServoAction::NewWebview(xc_wrapper, window_wrapper))
|
||||
.expect("Could not create new webview");
|
||||
}
|
||||
info!("Returning from on_surface_created_cb");
|
||||
}
|
||||
|
||||
|
@ -647,6 +729,12 @@ pub fn init_servo(init_opts: InitOpts) -> napi_ohos::Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[napi]
|
||||
fn focus_webview(id: u32) {
|
||||
debug!("Focusing webview {id} from napi");
|
||||
call(ServoAction::FocusWebview(id)).expect("Could not focus webview");
|
||||
}
|
||||
|
||||
struct OhosImeOptions {
|
||||
input_type: ohos_ime_sys::types::InputMethod_TextInputType,
|
||||
enterkey_type: InputMethod_EnterKeyType,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue