From 9fcfe09e5185f05af02f617c4a2bd934c29344dc Mon Sep 17 00:00:00 2001 From: Jonathan Schwender <55576758+jschwe@users.noreply.github.com> Date: Thu, 3 Oct 2024 18:33:21 +0200 Subject: [PATCH] ohos: Add toast prompt (#33621) * ohos: Add toast prompt Signed-off-by: Jonathan Schwender * ohos: Add toast on `load_ended` Signed-off-by: Jonathan Schwender * Apply review feedback Signed-off-by: Jonathan Schwender --------- Signed-off-by: Jonathan Schwender Signed-off-by: Jonathan Schwender --- ports/servoshell/egl/ohos.rs | 82 ++++++++++++++----- .../entry/src/main/ets/pages/Index.ets | 10 +++ 2 files changed, 70 insertions(+), 22 deletions(-) diff --git a/ports/servoshell/egl/ohos.rs b/ports/servoshell/egl/ohos.rs index 188b359080e..1c424471f0f 100644 --- a/ports/servoshell/egl/ohos.rs +++ b/ports/servoshell/egl/ohos.rs @@ -103,6 +103,7 @@ enum ServoAction { // Todo: Need to check if OnceLock is suitable, or if the TS function can be destroyed, e.g. // if the activity gets suspended. static SET_URL_BAR_CB: OnceLock> = OnceLock::new(); +static PROMPT_TOAST: OnceLock> = OnceLock::new(); impl ServoAction { fn dispatch_touch_event( @@ -426,27 +427,48 @@ pub fn go_forward() { } #[napi(js_name = "registerURLcallback")] -pub fn register_url_callback(cb: JsFunction) -> napi_ohos::Result<()> { - info!("register_url_callback called!"); - let tsfn: ThreadsafeFunction = - cb.create_threadsafe_function(1, |ctx| { - debug!( - "url callback argument transformer called with arg {}", - ctx.value - ); - let s = ctx - .env - .create_string_from_std(ctx.value) - .inspect_err(|e| error!("Failed to create JsString: {e:?}"))?; - Ok(vec![s]) - })?; +pub fn register_url_callback(callback: JsFunction) -> napi_ohos::Result<()> { + // Currently we call the callback in a blocking fashion, always from the embedder thread, + // so a queue size of 1 is sufficient. + const UPDATE_URL_QUEUE_SIZE: usize = 1; + debug!("register_url_callback called!"); + let function = callback.create_threadsafe_function(UPDATE_URL_QUEUE_SIZE, |ctx| { + Ok(vec![ctx + .env + .create_string_from_std(ctx.value) + .inspect_err(|e| { + error!("Failed to create JsString: {e:?}") + })?]) + })?; // We ignore any error for now - but probably we should propagate it back to the TS layer. let _ = SET_URL_BAR_CB - .set(tsfn) + .set(function) .inspect_err(|_| warn!("Failed to set URL callback - register_url_callback called twice?")); Ok(()) } +#[napi] +pub fn register_prompt_toast_callback(callback: JsFunction) -> napi_ohos::Result<()> { + // We can submit alerts in a non-blocking fashion, but alerts will always come from the + // embedder thread. Specifying 4 as a max queue size seems reasonable for now, and can + // be adjusted later. + const PROMPT_QUEUE_SIZE: usize = 4; + debug!("register_prompt_toast_callback called!"); + let function = callback.create_threadsafe_function(PROMPT_QUEUE_SIZE, |ctx| { + Ok(vec![ctx + .env + .create_string_from_std(ctx.value) + .inspect_err(|e| { + error!("Failed to create JsString: {e:?}") + })?]) + })?; + // We ignore any error for now - but probably we should propagate it back to the TS layer. + let _ = PROMPT_TOAST + .set(function) + .inspect_err(|_| error!("Failed to set prompt toast callback.")); + Ok(()) +} + #[napi] pub fn init_servo(init_opts: InitOpts) -> napi_ohos::Result<()> { info!("Servo is being initialised with the following Options: "); @@ -513,8 +535,18 @@ impl HostCallbacks { #[allow(unused)] impl HostTrait for HostCallbacks { - fn prompt_alert(&self, msg: String, trusted: bool) { - warn!("prompt_alert not implemented. Cancelled. {}", msg); + fn prompt_alert(&self, msg: String, _trusted: bool) { + debug!("prompt_alert: {msg}"); + match PROMPT_TOAST.get() { + Some(prompt_fn) => { + let status = prompt_fn.call(msg, ThreadsafeFunctionCallMode::NonBlocking); + if status != napi_ohos::Status::Ok { + // Queue could be full. + error!("prompt_alert failed with {status}"); + } + }, + None => error!("PROMPT_TOAST not set. Dropping msg {msg}"), + } } fn prompt_yes_no(&self, msg: String, trusted: bool) -> PromptResult { @@ -541,7 +573,7 @@ impl HostTrait for HostCallbacks { } fn on_load_ended(&self) { - warn!("on_load_ended not implemented") + self.prompt_alert("Page finished loading!".to_string(), true); } fn on_title_changed(&self, title: Option) { @@ -554,10 +586,14 @@ impl HostTrait for HostCallbacks { fn on_url_changed(&self, url: String) { debug!("Hosttrait `on_url_changed` called with new url: {url}"); - if let Some(cb) = SET_URL_BAR_CB.get() { - cb.call(url, ThreadsafeFunctionCallMode::Blocking); - } else { - warn!("`on_url_changed` called without a registered callback") + match SET_URL_BAR_CB.get() { + Some(update_url_fn) => { + let status = update_url_fn.call(url, ThreadsafeFunctionCallMode::Blocking); + if status != napi_ohos::Status::Ok { + error!("on_url_changed failed with {status}"); + } + }, + None => error!("`on_url_changed` called without a registered callback"), } } @@ -621,5 +657,7 @@ impl HostTrait for HostCallbacks { if let Some(bt) = backtrace { error!("Backtrace: {bt:?}") } + self.prompt_alert("Servo crashed!".to_string(), true); + self.prompt_alert(reason, true); } } diff --git a/support/openharmony/entry/src/main/ets/pages/Index.ets b/support/openharmony/entry/src/main/ets/pages/Index.ets index 277fcc826e2..715b4aa9007 100644 --- a/support/openharmony/entry/src/main/ets/pages/Index.ets +++ b/support/openharmony/entry/src/main/ets/pages/Index.ets @@ -1,12 +1,14 @@ import { common } from '@kit.AbilityKit'; import display from '@ohos.display'; import deviceInfo from '@ohos.deviceInfo'; +import promptAction from '@ohos.promptAction'; interface ServoXComponentInterface { loadURL(url: string): void; goBack(): void; goForward(): void; registerURLcallback(callback: (url: string) => void): void; + registerPromptToastCallback(callback: (msg: string) => void): void initServo(options: InitOpts): void; } @@ -40,6 +42,13 @@ function get_device_type(): string { return device_type; } +function prompt_toast(msg: string) { + promptAction.showToast({ + message: msg, + duration: 2000 + }); +} + // Use the getShared API to obtain the LocalStorage instance shared by stage. let storage = LocalStorage.getShared() @@ -107,6 +116,7 @@ struct Index { console.info('New URL from native: ', new_url) this.urlToLoad = new_url }) + this.xComponentContext.registerPromptToastCallback(prompt_toast) }) } .width('100%')