Skip to content

Commit e7141ed

Browse files
committed
feat(ohos): adapt webdriver and floating window features
- adapt WebDriver script runtime and capture behavior for HarmonyOS - fix toolbar mode native controls, geometry, and restore behavior
1 parent 66a9a4b commit e7141ed

14 files changed

Lines changed: 1380 additions & 491 deletions

File tree

‎Cargo.lock‎

Lines changed: 1238 additions & 466 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎src/apps/desktop/capabilities/default.json‎

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
"windows": ["main", "agent-companion-pet", "spotlight"],
66
"permissions": [
77
"log:default",
8-
"autostart:default",
98
"core:default",
109
"core:path:default",
1110
"core:event:default",
@@ -45,12 +44,6 @@
4544
"core:window:allow-unmaximize",
4645
"core:window:allow-unminimize",
4746
"core:window:allow-set-min-size",
48-
"dialog:default",
49-
"dialog:allow-open",
50-
"dialog:allow-save",
51-
"dialog:allow-ask",
52-
"dialog:allow-confirm",
53-
"dialog:allow-message",
5447
"opener:default",
5548
"opener:allow-open-url",
5649
{

‎src/apps/desktop/src/api/browser_control_api.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ fn default_cdp_port() -> u16 {
3535
/// no live endpoint to reattach to, it does nothing and leaves the on-demand
3636
/// path to handle it.
3737
pub fn init_on_startup() {
38-
tokio::spawn(async {
38+
tauri::async_runtime::spawn(async {
3939
if !auto_connect_on_startup_enabled().await {
4040
return;
4141
}

‎src/apps/ohos/entry/src/main/ets/entryability/EntryAbility.ets‎

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -275,11 +275,24 @@ export default class EntryAbility extends RustAbility {
275275
});
276276
RustModule.registerArktsFunction('set_decorations_ohos', async (err: Error, arg: string): Promise<string> => {
277277
const req: SetDecorationsArg = JSON.parse(arg);
278+
const mainWin = windowStage.getMainWindowSync();
278279
try {
279-
await windowStage.getMainWindowSync().setWindowDecorVisible(req.visible);
280+
// Showing native title buttons makes HarmonyOS fall back to hover-only
281+
// window decorations. Re-apply the same custom title-bar configuration
282+
// used at startup after restoring the buttons.
283+
await mainWin.setWindowTitleButtonVisible(
284+
req.visible,
285+
req.visible,
286+
req.visible,
287+
);
288+
if (req.visible) {
289+
mainWin.setWindowDecorHeight(44);
290+
mainWin.setWindowTitleMoveEnabled(false);
291+
mainWin.setWindowDecorVisible(false);
292+
}
280293
} catch (e) {
281294
const be = e as BusinessError;
282-
hilog.warn(DOMAIN, TAG, `setWindowDecorVisible failed: ${be.message}`);
295+
hilog.warn(DOMAIN, TAG, `setWindowTitleButtonVisible failed: ${be.message}`);
283296
}
284297
return '';
285298
});
@@ -334,17 +347,27 @@ export default class EntryAbility extends RustAbility {
334347
dispWidth = disp.width;
335348
dispHeight = disp.height;
336349
scale = disp.densityPixels > 0 ? disp.densityPixels : 1;
350+
// A floating window may not intersect the system bar when this query
351+
// runs, in which case getWindowAvoidArea() reports zero. The display's
352+
// available bounds still exclude the desktop bar, so keep that
353+
// right/bottom reservation as a monitor-level fallback.
354+
if (disp.availableWidth > 0 && disp.availableWidth <= disp.width) {
355+
avoidRight = Math.max(avoidRight, disp.width - disp.availableWidth);
356+
}
357+
if (disp.availableHeight > 0 && disp.availableHeight <= disp.height) {
358+
avoidBottom = Math.max(avoidBottom, disp.height - disp.availableHeight);
359+
}
337360
} catch (e) {
338361
const be = e as BusinessError;
339362
hilog.warn(DOMAIN, TAG, `getDefaultDisplaySync failed: ${be.message}`);
340363
}
341364
try {
342365
const avoidArea = windowStage.getMainWindowSync()
343366
.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM);
344-
avoidTop = avoidArea.topRect.height;
345-
avoidBottom = avoidArea.bottomRect.height;
346-
avoidLeft = avoidArea.leftRect.width;
347-
avoidRight = avoidArea.rightRect.width;
367+
avoidTop = Math.max(avoidTop, avoidArea.topRect.height);
368+
avoidBottom = Math.max(avoidBottom, avoidArea.bottomRect.height);
369+
avoidLeft = Math.max(avoidLeft, avoidArea.leftRect.width);
370+
avoidRight = Math.max(avoidRight, avoidArea.rightRect.width);
348371
} catch (e) {
349372
const be = e as BusinessError;
350373
hilog.warn(DOMAIN, TAG, `getWindowAvoidArea failed: ${be.message}`);
@@ -389,7 +412,7 @@ export default class EntryAbility extends RustAbility {
389412
});
390413
RustModule.registerArktsFunction('maximize_ohos', async (err: Error, arg: string): Promise<string> => {
391414
try {
392-
await windowStage.getMainWindowSync().maximize();
415+
await windowStage.getMainWindowSync().maximize(window.MaximizePresentation.EXIT_IMMERSIVE);
393416
} catch (e) {
394417
const be = e as BusinessError;
395418
hilog.warn(DOMAIN, TAG, `maximize failed: ${be.message}`);

‎src/crates/adapters/webdriver/src/platform/capture.rs‎

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -570,7 +570,7 @@ mod imp {
570570
}
571571
}
572572

573-
#[cfg(target_os = "linux")]
573+
#[cfg(all(target_os = "linux", not(target_env = "ohos")))]
574574
mod imp {
575575
use super::*;
576576

@@ -594,6 +594,30 @@ mod imp {
594594
}
595595
}
596596

597+
#[cfg(target_env = "ohos")]
598+
mod imp {
599+
use super::*;
600+
601+
pub(super) async fn take_screenshot<R: Runtime>(
602+
_webview: Webview<R>,
603+
_timeout_ms: u64,
604+
) -> Result<String, WebDriverErrorResponse> {
605+
Err(WebDriverErrorResponse::unsupported_operation(
606+
"HarmonyOS ArkWeb screenshots require a native capture provider",
607+
))
608+
}
609+
610+
pub(super) async fn print_page<R: Runtime>(
611+
_webview: Webview<R>,
612+
_timeout_ms: u64,
613+
_options: &PrintOptions,
614+
) -> Result<String, WebDriverErrorResponse> {
615+
Err(WebDriverErrorResponse::unsupported_operation(
616+
"HarmonyOS ArkWeb printing requires a native print provider",
617+
))
618+
}
619+
}
620+
597621
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "linux")))]
598622
mod imp {
599623
use super::*;

‎src/crates/adapters/webdriver/src/platform/evaluator/mod.rs‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ pub(crate) async fn evaluate_script<R: tauri::Runtime>(
2727
async_mode: bool,
2828
frame_context: &Value,
2929
) -> Result<Value, WebDriverErrorResponse> {
30+
// OpenHarmony's ArkWeb host currently reaches this generic evaluator via
31+
// the Tauri fork's Webview::eval implementation. The injected bridge is
32+
// pure page JavaScript, so pointer/wheel/key actions remain shared. A
33+
// future ArkWeb-native provider can replace only this branch when it needs
34+
// a direct runJavaScript/result callback instead of Tauri IPC.
3035
#[cfg(target_os = "macos")]
3136
{
3237
let _ = state;

‎src/crates/adapters/webdriver/src/runtime/mod.rs‎

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,5 +85,28 @@ pub(crate) async fn run_script(
8585
let timeout_ms = session.timeouts.script.max(5_000);
8686

8787
let frame_context = script::serialize_frame_context(&session.frame_context);
88-
Err(WebDriverErrorResponse::no_such_window("No such windows"))
88+
let window = state
89+
.app
90+
.get_webview_window(&session.current_window)
91+
.ok_or_else(|| {
92+
WebDriverErrorResponse::no_such_window(format!(
93+
"Window not found: {}",
94+
session.current_window
95+
))
96+
})?;
97+
98+
// The JavaScript bridge is platform-neutral. Each host only supplies the
99+
// WebView evaluator and result transport in `platform::evaluator` (the
100+
// OHOS target uses the generic Tauri bridge until an ArkWeb-native host
101+
// adapter is wired in).
102+
platform::evaluator::evaluate_script(
103+
state,
104+
window.as_ref().clone(),
105+
timeout_ms,
106+
script_source,
107+
&args,
108+
async_mode,
109+
&frame_context,
110+
)
111+
.await
89112
}

‎src/crates/adapters/webdriver/src/runtime/script.rs‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,23 @@ if (!window.__bitfunWd) {{
109109
core_tail = core::tail()
110110
)
111111
}
112+
113+
#[cfg(test)]
114+
mod tests {
115+
use super::bridge_helper_script;
116+
117+
#[test]
118+
fn bridge_includes_harmony_safe_pointer_actions() {
119+
let bridge = bridge_helper_script();
120+
121+
for symbol in [
122+
"performPointerSourceActions",
123+
"performWheelSourceActions",
124+
"performKeySourceActions",
125+
"dispatchPointerClick",
126+
"releaseActions",
127+
] {
128+
assert!(bridge.contains(symbol), "missing injected symbol: {symbol}");
129+
}
130+
}
131+
}

‎src/crates/adapters/webdriver/src/server/handlers/session.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ pub async fn create(
111111
let set_window_rect = cfg!(any(
112112
target_os = "macos",
113113
target_os = "windows",
114-
target_os = "linux"
114+
all(target_os = "linux", not(target_env = "ohos"))
115115
));
116116

117117
Ok(WebDriverResponse::success(json!({

‎src/crates/assembly/core/Cargo.toml‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ tokio-util = { workspace = true, optional = true }
1616
napi-ohos = { workspace = true }
1717
napi-derive-ohos = { workspace = true }
1818
tokio-stream = { workspace = true }
19-
tokio-util = { workspace = true }
2019
async-trait = { workspace = true }
2120
futures = { workspace = true, optional = true }
2221

0 commit comments

Comments
 (0)