Skip to content

feat(ohos): bridge plugin architecture + 3-round audit fixes + dead-code cleanup - #79

Draft
ljy9812 wants to merge 79 commits into
harmony-contrib:mainfrom
ljy9812:ohdev
Draft

feat(ohos): bridge plugin architecture + 3-round audit fixes + dead-code cleanup#79
ljy9812 wants to merge 79 commits into
harmony-contrib:mainfrom
ljy9812:ohdev

Conversation

@ljy9812

@ljy9812 ljy9812 commented Aug 20, 2026

Copy link
Copy Markdown

tauri-ohos适配

MingyuChen1 and others added 30 commits June 8, 2026 19:00
… version detection, autostart, and WebView transparency

This commit brings a comprehensive set of new features and improvements
to openharmony-ability, transforming it from a basic single-window
ability framework into a feature-rich platform for OpenHarmony/HarmonyOS
native applications.

74 files changed, ~8,500 lines added, ~875 lines removed.

New Features:

1. Menu System (crates/ability/src/menu/ — 6 new files, ~1,640 lines)
   Complete menu system with menubar and context popup support:
   - mod.rs: Channel-based architecture using crossbeam_channel for
     bidirectional Rust (muda) <-> ArkTS communication. Background
     forwarder thread dispatches via TSFN callback. Per-window menubar
     visibility state in RwLock<HashMap>. Buffered menubar JSON per
     window for late ArkTS callback registration.
   - types.rs: MenuRequest/MenuRequestData (NAPI object), MenuItemData
     (recursive submenu), AboutMetadataData, NAPI classes Menu,
     MenuItem, Submenu.
   - event.rs: MenuEvent struct with MenuEventDispatcher and global
     GLOBAL_DISPATCHER (LazyLock<Mutex>).
   - predefined.rs: PredefinedType enum (Copy, Cut, Paste, SelectAll,
     Undo, Redo, Minimize, Maximize, Quit, etc.) with factory methods
     and default accelerators (Ctrl+C, Ctrl+V, etc.).
   - state.rs: MenuStateController NAPI class for create/append/destroy.
   - popup.rs: MenuPopup NAPI class for context menu show/hide.

2. System Tray / StatusBar (crates/ability/src/statusbar/ — 5 new files, ~855 lines)
   Full system tray integration:
   - types.rs: StatusBarItem, StatusBarIcon (RGBA pixel data),
     QuickOperation, StatusBarMenuItem/SubMenuItem, StatusBarMenuAction,
     StatusBarClickEvent enum.
   - event.rs: Dual crossbeam_channel pairs for icon click and menu
     click events with register/unregister handlers.
   - manager.rs: 6 TSFNs (Add, Remove, UpdateIcon, UpdateMenu,
     UpdateTips, PredefinedAction) with thread-safe main-thread dispatch.
   - validate.rs: Input validation (max 20 menu items, max 20 sub-items,
     hover tips 1-128 chars, height > 0).

3. Clipboard (crates/ability/src/clipboard/mod.rs — 178 lines)
   clipboard_write_image(rgba, width, height) async function writing
   RGBA image data to system clipboard via TSFN bridge to ArkTS
   writeImageToClipboard (Promise-based with 10-second timeout).

4. App Updater / AppGallery Integration (crates/ability/src/updater.rs + helper/updater.rs — ~420 lines)
   - Updater::check() queries AppGallery for available updates, returns
     CheckResult with version, body, and date.
   - Updater::download_and_install() shows update dialog and drives
     download+install flow.
   - 3 TSFNs for check, show dialog, and download/install operations.

5. Version Detection (crates/ability/src/version.rs — 316 lines)
   - sdk_api_version(): OpenHarmony base API Level (e.g., 12, 14, 20).
   - distribution_api_version(): HarmonyOS distribution version
     (M*10000 + S*100 + F).
   - can_i_use(syscap): queries device system capabilities via ArkTS
     canIUse().
   - Includes device tests for worker thread safety.

6. Multi-Window Support (crates/ability/src/window/mod.rs — 200+ lines)
   - create_os_window(name, window_type) creates OS-level sub-windows
     with global atomic NEXT_WINDOW_ID for unique ID generation.
   - Window decorations support (title bar, border, etc.).
   - WindowManager with destroyWindow() for proper resource cleanup.

7. Float Window (native_ability/.../FloatPage.ets — 300 lines)
   Floating window page component with drag and resize functionality
   using createSubWindow API.

8. Autostart Support (crates/ability/src/autostart.rs + helper/autostart.rs — 412 lines)
   - AutostartManager for managing app auto-start on device boot.
   - TSFN bridge for ArkTS autostart registration/unregistration.
   - Integration with HarmonyOS AbilityManagerService.

9. WebView Transparency (native_ability/.../DefaultWebview.ets — 71+ lines)
   - Transparent background support for WebView.
   - Window transparency synchronization.
   - TransparencyTest demo page for validation.

Improvements & Refactoring:

App Enhancements (app.rs):
- restart(): hard process restart via TSFN with 3-second cooldown.
- set_color_mode(mode: ColorMode): dark/light/system theme switching.
- updater(): get updater handle for AppGallery integration.
- AbilityInitContext now includes sdk_api_version and
  distribution_api_version.
- New NAPI export: is_desktop_device() for compile-time device type
  detection.

Helper Infrastructure (helper/):
- Refactored from thread_local to global Mutex for HELPER storage,
  enables cross-thread access via SendableHelper wrapper.
- New helper/restart.rs: restart TSFN with LazyLock<RwLock> pattern.
- New helper/updater.rs: three TSFN pairs for updater operations.
- New helper/autostart.rs: autostart TSFN management.

WebView Enhancements (webview/):
- window_id field on WebViewInitData and WebViewBuilder for per-window
  webview creation.
- on_page_begin / on_page_end lifecycle callbacks.
- Fixed callback argument indexing (off-by-one in NAPI extraction).
- Added 'static bound to custom protocol callback closures.
- Fixed closure invocation to avoid holding lock during callback.
- Full URL matching for close-window intercept.

Lifecycle & Events:
- onWindowStageDestroy lifecycle hook.
- NewWant event support.
- runJavaScript error handling.

ArkTS Side (Native Layer — ~2,500+ lines, 18 new files):
- ArkHelper.ets (650+ lines): Full ArkHelper interface covering exit,
  restart, updater, permissions, window, webview, statusbar, clipboard,
  autostart.
- MenuBarComponent.ets (451 lines): Per-window menubar UI with click
  handlers and keyboard accelerator support.
- FloatPage.ets (300 lines): Floating window page component.
- menu.ets (239 lines): MenuManager and PredefinedActionExecutor.
- WindowManager.ets (350+ lines): Multi-window management singleton
  with proper destroyWindow() cleanup.
- StatusBarUtils.ets (182 lines): Status bar pixel map creation.
- accelerator_matcher.ets (142 lines): Keyboard shortcut matching.
- Utils.ets (140+ lines): JsHelper and ProxyJsHelper for deferred
  webview init.
- updater.ets (115 lines): AppGallery updater using AppGalleryKit.
- predefined.ets (109 lines): Predefined menu action implementations.
- menu_state.ets (85 lines): Menu state management.
- MenuPopup.ets (80 lines): Context popup menu component.
- menu_types.ets (74 lines): Type definitions for menu system.
- autostart.ets (63 lines): Autostart registration helper.
- os.ets (37 lines): OS-level utility functions.
- ClipboardHelper.ets (33 lines): Clipboard write image implementation.
- TransparencyTest.ets (167 lines): Demo page for transparency testing.

Bug Fixes:
- fix(statusbar): re-register click handlers after addToStatusBar to
  survive receiver rebuilds.
- fix(statusbar): prevent FloatPage from overwriting main window
  helperRef.
- fix(ohos): tray icon click events — abilityName null check and
  one-time on() guard, support relaunch.
- fix(tray): close/fullscreen behavior, menubar startup timing.
- fix(webview): callback argument off-by-one in NAPI extraction.
- fix(webview): closure lock held during callback (potential deadlock).
- fix(webview): use full URL for close-window intercept.
- fix(NativeAbility): duplicate onWindowStageDestroy method.
- fix(WindowManager): proper destroyWindow() cleanup on sub-window close.
- refactor: renamed TAURI_OHOS_DEVICE_TYPE to OHOS_DEVICE_TYPE for
  framework-agnostic naming.
- docs: changed Chinese doc comments to English in helper/mod.rs.

Design Patterns:
- TSFN Bridge Pattern for safe ArkTS calls from Rust worker threads.
- Channel-based Event Distribution via crossbeam_channel.
- OnceLock/LazyLock for thread-safe lazy initialization.
- Per-Window State Maps (HashMap<String, bool>) for menubar state.
- Promise-to-Future Bridge via oneshot::channel + PromiseRaw + timeout.

Testing:
- Added device tests for the version module.
- Added TransparencyTest demo page for WebView transparency validation.
…dedWebBuilder

Call ctrl.setCustomUserAgent(data.userAgent) in onControllerAttached
callback when userAgent is provided. Enables OHOS WebView custom
User-Agent via WebviewController API.
Round 1:
- Extract shared DOMAIN constant into helper/constants.ets (10 files)
- Use system icon for FloatPage close button
- Replace eprintln! with crate::error! in xcomponent.rs and app.rs

Round 2:
- Add feature flags for modular compilation (statusbar, updater, version, window, clipboard, webview, menu)
- Define cfg-gated crate::error!/info!/warn!/debug! macros replacing direct log::*/hilog_*! usage
- Make log/hilog dependencies optional behind 'log' feature flag
- Add cfg gates for version-dependent and module-dependent code paths

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
fix: address upstream PR harmony-contrib#63 review feedback and code quality cleanups
…-user-agent

feat(webview): add setCustomUserAgent support in WebBuilder and EmbeddedWebBuilder
feat(ohos): add window close lifecycle mechanism via NAPI queue
- Extend onNewWant ArkTS callback to extract and serialize want.parameters
- Log parametersJson.length in NativeAbility for debugging
- Define NewWantData interface in type.ets for type-safe parameter passing
- Update NAPI bridge from Function<String> to Function<Object>
- Add #[cfg(target_env = "ohos")] gate on WANT_PARAMETERS static and functions
- Add static Mutex storage with store/take semantics and poison-safe error logging
- Use ? for property extraction (consistent with other lifecycle callbacks)
- Document concurrency contract: store on ArkTS main thread, take on tauri event loop
- Update index.d.ts to match new onNewWant signature
- Add single combined unit test for store/take/clear/overwrite (avoids parallel races)
feat(ohos): forward want.parameters through onNewWant event chain
- Add SnapshotData interface and JsHelper.webPageSnapshot method (ArkTS)
- Implement webPageSnapshot with PixelMap to RGBA conversion and resource cleanup
- Add Rust web_page_snapshot() callback API on Webview struct
- Add unsafe impl Send for Webview (main thread only access)
feat(webview): add webPageSnapshot support for WebView content capture
- Restore PdfConfig struct with to_napi_map() for NAPI transport
- Add scale field to PdfConfig (SDK PdfConfiguration.scale support)
- Restore config parameter in ArkTS createPdf with merge logic
- Update JsHelper/ProxyJsHelper interface to include config
- Revert pdfArrayBuffer().buffer removal (Uint8Array -> ArrayBuffer)
- Revert async fileIo.write back to fileIo.writeSync
- Restore explanatory NAPI callback behavior comments
feat: add PdfConfig, createPdf NAPI + ArkTS implementation
- Add predefined menu actions: hide, close, minimize, showAll, bringAllToFront
- Add clipboard ops (copy, cut, paste, selectAll, undo, redo) using target window webview
- Align predefined menu actions with macOS Key Window semantics
- Add MAIN_WINDOW_ID constant for main window identification
- Centralize resetUserInteractionTracking in WindowManager wrapper methods
- Add getPrimaryWebviewController for clipboard operations on primary webview
- Fix FloatPage aboutToAppear async declaration
- Remove Bug 6 references from comments
feat(menu): predefined multi-window clipboard ops and WindowManager fixes
…lper pending path fix, setBounds in controllers
feat(ohos): add WebViewStyle width/height, set_bounds NAPI, ProxyJsHelper pending path fix
Add Webview::set_cookie NAPI method and ArkTS setCookie (JsHelper
interface, ProxyJsHelper pending-replay, DefaultWebview buildJsHelper)
delegating to WebCookieManager.configCookieSync.
Add comprehensive mouse input handling for OHOS 2in1 desktop devices:

- MouseEventData struct wrapping OH_NativeXComponent_MouseEvent FFI
- MouseAction enum with Press/Release/Move/HoverEnter/HoverLeave variants
- dispatch_mouse_event: NDK DispatchMouseEvent callback
- dispatch_hover_event: NDK DispatchHoverEvent callback for cursor enter/leave
- AxisEventData struct for scroll wheel delta (delta_x, delta_y)
- dispatch_axis_event: ArkUI UIInputEvent AXIS callback for scroll wheel
- register_mouse_callbacks: registers all three callbacks with NDK
- InputEvent::MouseEvent and InputEvent::AxisEvent variants
- Added ohos-xcomponent-sys and ohos-arkui-sys as direct dependencies

Co-Authored-By: Claude <noreply@anthropic.com>
- Add InputSourceType enum (Mouse/TouchScreen/Touchpad/Joystick/Keyboard)
- Add pinch_scale and source_type fields to AxisEventData
- Extract pinch scale via OH_ArkUI_AxisEvent_GetPinchAxisScaleValue
- Extract source type via OH_ArkUI_UIInputEvent_GetSourceType
- Skip axis events with no scroll delta AND no pinch data

Co-Authored-By: Claude <noreply@anthropic.com>
feat(ohos): add setCookie NAPI bridge for webview cookie management
OHOS NDK DispatchMouseEvent doesn't fire when cursor is over WebView,
so we track cursor position from ArkTS onMouse events instead.

- Add CURSOR_POSITION_X/Y atomic statics in app.rs
- Add update_cursor_position() NAPI function for ArkTS to call
- Add .onMouse() handler in MainPage.ets that calls NAPI on Move/Press
- Export CURSOR_POSITION_X/Y for tao to read

Co-Authored-By: Claude <noreply@anthropic.com>
Add a native ArkTS overlay for testing key repeat detection:
- Floating '⌨ Key Test' button (bottom-right corner)
- Focusable overlay panel with HashSet-based repeat tracking
- .onKeyEvent() captures keys at ArkUI level (bypasses WebView)
- ESC or ✕ to close overlay
- repeat=true shown in green bold when key hold is detected

Co-Authored-By: Claude <noreply@anthropic.com>
Add Webview::set_web_debugging_access(bool) and is_web_debugging_access()
NAPI methods. ArkTS side (Utils.ets) tracks webDebuggingEnabled state
(setWebDebuggingAccess const calls the static API first then updates the
var; isWebDebuggingAccess returns the var). DefaultWebview.ets init
routes through the const to keep state in sync. Uses domain socket
(setWebDebuggingAccess(true), no port) — connect Chrome DevTools via
examples/api/devtools.bat which auto-discovers the socket and forwards
via hdc fport.
feat(ohos): add setWebDebuggingAccess NAPI bridge for devtools toggle
…ents

feat(ohos): add mouse event, hover event, and scroll wheel support
…mer API

- Forwarder thread + crossbeam channel + run_on_main_thread architecture
- inputConsumer API (API 14+) for hotkey registration
- 60+ key code mapping (Rust/ArkTS consistent)
- Error code handling (801/4200002/4200003)
- Duplicate modifier dedup, blocking send in unregister_all
- Documented fire-and-forget design and synthetic Released event
- SAFETY comments, dispatch_to_main_thread returns Result

Co-Authored-By: Claude <noreply@anthropic.com>
feat(global_shortcut): add OHOS global shortcut bridge via inputConsumer API
- Phase 6/7: on_window_new Create + window focus NAPI bridge
- Fix crash from napi_reference_unref on tokio worker threads:
  napi_ohos::Error's Drop calls napi_reference_unref which must run
  on main thread. Convert Error to String before sending through
  oneshot channels (clipboard, autostart, updater). Wrap ObjectRef
  in ManuallyDrop in thread-local cache.
feat(ohos): add window focus NAPI and fix napi_reference_unref crash
…on and plugin migrations

- pluginize native bridge capabilities (harmony-contrib#67), normalize node surface (harmony-contrib#70),
  transparent XComponent background (harmony-contrib#73), webview controller release
  safety (harmony-contrib#69)
- plugin module agnostic refactor (harmony-contrib#75), serialize ability plugin
  lifecycle (harmony-contrib#74)
- account/updater bridge migration and clipboard write-html API
- dead code cleanup superseded by the bridge plugin architecture
- deps upgrade, format + git hook

Co-Authored-By: Claude <noreply@anthropic.com>
ddxwzc-boop and others added 28 commits August 24, 2026 19:07
…ableDrag, fix setPointerStyle C5

- instanceKey: 'specified launchType' mechanism doesn't exist (template is
  standard, SDK 12 has no abilityStage/onAcceptWant) — delete the whole
  passthrough (window/mod.rs want.set, ArkHelper.ets, type.ets field) and
  correct the type.ets comment back to launchType:standard
- setWindowDraggable: enableDrag is API20+; add deviceInfo.sdkApiVersion < 20
  guard (safeLogError + return) matching the startMoving/setWindowTopmost pattern
- setPointerStyle: bare hilog in the NAPI-reentrant sync body throws 'Argc
  mismatch' (C5) — success log misreported failures, catch log swallowed the
  original err; switch to console fallback like safeLogError/getRealWindowId
feat(ohos): window ops bridge + status readback + FloatPage decorations
…on and plugin migrations

- pluginize native bridge capabilities (harmony-contrib#67), normalize node surface (harmony-contrib#70),
  transparent XComponent background (harmony-contrib#73), webview controller release
  safety (harmony-contrib#69)
- plugin module agnostic refactor (harmony-contrib#75), serialize ability plugin
  lifecycle (harmony-contrib#74)
- account/updater bridge migration and clipboard write-html API
- dead code cleanup superseded by the bridge plugin architecture
- deps upgrade, format + git hook

Co-Authored-By: Claude <noreply@anthropic.com>
Replace AppInner.window_rect (single shared field) with
window_rects: HashMap<i64, Rect> keyed by windowId (0 = main window,
>0 = Float sub-window via NEXT_WINDOW_ID). Add window_rect_for /
set_window_rect; release_render_owner clears only key 0.

ArkTS: wrap native window.RectChangeOptions / size-change options into
{ windowId, reason, rect } before invoking the Rust lifecycle closures
(NativeAbility.ets main window id=0, BridgeHost.ets component-window
second registration id=0). Float sub-windows never reach attachComponent
(DefaultXComponent early-returns), so register windowRectChange in
WindowManager.createSubWindow with callback injection
(registerRectChangeCallback) and off() cleanup in removeWindow.

Rust closures (lifecycle.rs) read windowId from the wrapped options and
store each rect under its own key; MainEvent::WindowResize and
ContentRect now carry window_id for tao event routing (Phase 3).

Part of openspec change p1-window-state-per-window-rect (Phase 2, D1-D5).

Co-Authored-By: Claude <noreply@anthropic.com>
…+trailing)

openspec p2-mainthread-event-hygiene layer 2: high-frequency window
operations (30+ subwindow create/destroy bursts) previously queued 12+
Immediate/Low events on the main thread and could trip the 6s watchdog
even without lock contention.

- WindowManager: per-windowId leading+trailing 16ms throttle for
  windowRectChange/windowSizeChange (rect and size have independent
  throttle state; leading keeps first-event immediacy, trailing
  guarantees final-state delivery — required for window-state cache
  freshness), WindowEntry.destroying flag drops events for windows
  being torn down, timer cleanup in removeWindow/unregisterUIAbilityStage
- NativeAbility/BridgeHost: all four registration points routed through
  the same throttledRectDispatch; RECOVER→menubar restore stays
  synchronous (not throttled)
- typed WindowSizeEventWrap/WindowRectEventWrap interfaces replace
  ESObject (arkts-limited-esobj WARN eliminated)

Verified on device: THROTTLE 104 IN / 98 OUT (5.8% suppression, 0
failures), full suite 281 pass / 1 fail / 1 skip, zero new appfreeze.

Co-Authored-By: Claude <noreply@anthropic.com>
Feature-gated behind `fault-injection` (zero overhead when off):
- Rust wire types (FaultRuleWire etc.) + OpenHarmonyApp facade
  (set_fault_rule/clear_fault_rules, auto-enables the registry on first
  call) + call_fault_injection on the bridge client
- ArkTS FaultInjection.ets: always compiled but short-circuits at
  match() when disabled — injects failures into bridge calls to light
  up error-handling branches that never run naturally

Co-Authored-By: Claude <noreply@anthropic.com>
…ge ramp-up)

Appended to existing test modules: expect_engine_phase /
engine_scheme_pairs variants, plugin-window and mouse-event
pure-transform cases — host-side inputs for branches that never occur
naturally on device.

Co-Authored-By: Claude <noreply@anthropic.com>
Rebase of upstream/ohdev (through a25848a) onto the pluginized local
architecture; upstream features were built on the old ArkHelper TSFN
framework, so they are ported semantically instead of textually:

- window/mod.rs: cursor grab FFI (dlopen libnative_window_manager.so,
  OH_WindowManager_LockCursor/UnlockCursor) with set_cursor_grab taking
  the real OHOS window id directly
- app.rs: notify_window_status NAPI + PENDING_WINDOW_STATUS queue,
  mirroring the notify_window_close drain pattern
- WindowPlugin.ets: 7 new bridge actions — set-topmost, set-title,
  set-limits (delegate to ported WindowManager methods),
  request-user-attention (static notificationManager import, monotonic
  id, 1600004 enable-retry), set-ime-position (await updateCursor,
  result returned directly — no poll), set-draggable (API20 guard),
  get-real-window-id
- plugin-window WindowClient: matching async facade methods
- DefaultWebview: naturalLayout guard ported from upstream d530828 —
  explicit child webview bounds restored, natural-layout webviews keep
  100% sizing and strip set_bounds width/height
- windowCommand 'show': delegate to WindowManager.showWindowMethod so a
  minimized main window is restored instead of no-op showWindow()

cargo check passes on host and aarch64-unknown-linux-ohos (0 errors).

Co-Authored-By: Claude <noreply@anthropic.com>
…e actions

Two facade actions tao needs beyond the PR#45 port set:

- set-cursor-icon: delegates to WindowManager.setPointerStyle (resolves
  the real OHOS window id internally, pointer.setPointerStyleSync).
  Fire-and-forget — per-window failures are logged on the ArkTS side,
  the ack only confirms dispatch (tao's setter is void).
- set-decoration-flags: applies the FLAG bit-field (closable=1,
  maximizable=2, minimizable=4, resizable=8) to a Float sub-window's
  LocalStorage; the UIAbility main window is a system-managed no-op.

WindowClient gains the matching async methods; requests are validated
windowId + integer style / flags in [0, 15].

Co-Authored-By: Claude <noreply@anthropic.com>
…e escape

pack.bat invokes this script as "...pack-plugins.ps1" "%SCRIPT_DIR%"; the
trailing backslash escapes the closing quote in cmd's parser, so
arrives with a literal trailing quote. Join-Path bakes that quote into every
derived path and Test-Path fails with ItemExistsArgumentError, aborting the
whole pack at "plugin aggregation failed". Trim both quotes and backslashes
from the incoming value.

Co-Authored-By: Claude <noreply@anthropic.com>
…usChange

The rebase-brought windowStatusChange registration in onWindowStageCreate
referenced a bare 'windowId' identifier (upstream defines it via readWindowId()
for multi-UIAbility; local single-UIAbility architecture has no such binding),
failing ArkTS compilation. The main window is always tao windowId=0 (same
sentinel as the surrounding WindowSizeEventWrap/WindowRectEventWrap wraps), and
runtime-wry routes by tao window_id, so notify with the literal 0. Float
sub-windows keep using the NEXT_WINDOW_ID(>=1) virtual id via FloatPage.

Co-Authored-By: Claude <noreply@anthropic.com>
The WM rect (windowRectChange) and the XComponent surface rect update
asynchronously; a live window_rect - content_rect diff read in the gap
produced garbage decor estimates (824/770/292 instead of the real 146),
which corrupted tao's inner_size reads and compounded through
window-state save/restore into a shrinking main window.

- latch decor height ONLY on surface events (activate/update), where both
  rects are consistent: diff==0 -> 0, 0<diff<=320 -> latch, otherwise keep
  the previous estimate
- add decor_change_callbacks: listeners fired while the app write lock is
  held whenever the latched value actually changes (contract: lock-free
  listeners only — channel sends/atomics); returning false removes the
  listener after the call
- expose register/remove_decor_change_callback for tao's event-driven
  set_inner_size self-correction
- unit tests for the latch rules and the callback firing semantics

Co-Authored-By: Claude <noreply@anthropic.com>
…en to WindowManager

1. Restore CURSOR_POSITION_X/Y atomics + update_cursor_position NAPI in
   app.rs (dropped during the pluginize refactor, restored from 5941dfb).
   The ArkTS MainPage.onMouse handler is the only viable cursor tracking
   source — the NDK DispatchMouseEvent path never fires while the cursor
   is over the WebView. tao's cursor_position() reads these atomics.
2. WindowPlugin.ets set-fullscreen: delegate to WindowManager.setFullscreen
   instead of the inline setWindowLayoutFullScreen mobile-only path the
   pluginize migration left behind. WindowManager implements the dual
   path (desktop/2in1: maximize(ENTER_IMMERSIVE) + setTitleAndDockHoverShown;
   mobile: setWindowLayoutFullScreen + setWindowSystemBarEnable with
   serializeOp race guard); the inline version was a visual no-op on
   desktop while still acking OK.

Verified on HUAWEI MateBook Pro (2in1): cursorPosition() non-zero;
fullscreen toggle enters (ENTER_IMMERSIVE) and exits (recover()).

Co-Authored-By: Claude <noreply@anthropic.com>
Remote-only commit is the pre-rebase push of work that was replayed
(and further refined) during the upstream-ohdev rebase; local content
is authoritative (-s ours). See upstream-ohdev-rebase-window-ops.
…swap

JsStatusbarManager builds its ScbServerMessageReceiver asynchronously
(~14ms) after the sync addToStatusBar() call returns. Registering
click handlers on the sync return binds them to the previous receiver,
which the new receiver then replaces - rightMenuClick and
statusBarIconClick listeners were silently dropped on re-add
(set_title/set_visible/set_quick_operation all re-run add).

Switch to the AsyncCallback overload (API 14+) and re-register in the
completion callback, which runs strictly after the new receiver
exists. The sync registration is kept as a baseline (harmless off+on)
in case the callback never fires.

Co-Authored-By: Claude <noreply@anthropic.com>
Predefined menu fullscreen (tray/menubar Fullscreen items) used an
inline implementation that only did a bare immersive maximize on
desktop - the system title bar / Dock stayed visible, diverging from
WindowPlugin set-fullscreen. Delegate both 'fullscreen' and 'recover'
predefined actions to WindowManager.setFullscreen so every entry point
shares the dual-path (desktop maximize(ENTER_IMMERSIVE) + hide title
bar/Dock hover, mobile layout fullscreen + hide system bars) and the
serializeOp race guard.

WindowManager.setFullscreen now also drives the Tauri menubar via MW-5
callbacks (macOS semantics, design.md D3): hide on entry, restore on
exit. Previously only the predefined path hid the menubar, so the
window-API Toggle Fullscreen button kept it visible - and Esc could
not exit because the MainPage Esc handler is gated on
menubar-invisible. Centralizing the callbacks makes Esc exit work for
all fullscreen flavours through the existing condition. The Esc
recoverFn (NativeAbility) delegates to setFullscreen(0, false) so the
title bar / Dock hidden on entry are restored as well.

Verified on device (MateBook Pro): button/tray/menubar fullscreen all
hide system chrome + menubar; Esc and button toggle both restore.

Co-Authored-By: Claude <noreply@anthropic.com>
Restores set_cursor_visible, which the bridge facade migration (tao
73212e1e) dropped to a no-op on the Rust side while the ArkTS
WindowManager.setPointerVisible implementation survived. Adds the
set-cursor-visible action to the WindowPlugin (delegating to
WindowManager.setPointerVisible) and a CursorVisibleRequest facade
method. The request carries no window id - pointer.setPointerVisible
is process-wide (global vs window-level semantics documented as
遗留问题六).

Verified on device (MateBook Pro): setCursorVisible(false) (3s) hides
the pointer globally and restores it after 3s.

Co-Authored-By: Claude <noreply@anthropic.com>
…store

- createSubWindow: on API19+ use createSubWindowWithOptions with
  maximizeSupported:true — sub-window win.maximize() is rejected by WMS
  (1300004) unless maximize capability was declared at creation;
  decorEnabled:false keeps FloatPage's custom title bar
- FloatPage un-maximize: use recoverWindow() (MAXIMIZE/FULL_SCREEN →
  FLOATING, the documented inverse of maximize) instead of
  restoreWindow() which is main-window-only

Co-Authored-By: Claude <noreply@anthropic.com>
…g while maximized

- WMS recover() re-anchors the floating rect to the pointer
  (GetFullScreenToFloatingRect), so programmatic restore landed the
  window near the cursor instead of its pre-maximize position.
  Snapshot the floating rect before maximize (preMaximizeRects,
  has-guard against double-maximize overwriting with the fullscreen
  rect) and moveTo() back after recover, on both the FloatPage button
  path and the WindowPlugin bridge path (shared
  snapshotPreMaximizeRect/restorePreMaximizeRect helpers; the bridge
  keeps its await-completion semantics and cannot delegate to the
  fire-and-forget maximizeWindow).
- FloatPage title-bar onTouch(Down)->startMoving() also fires for
  child button touches (ArkUI touch bubbling); while maximized it
  triggered WMS drag-off-maximize at touch-down, moving the window
  away so the touch-up landed out of region and the click gesture was
  rejected — onClick never ran. Skip startMoving while maximized
  (drag-off restore sacrificed; restore via the button).

Device-verified 2026-08-27: maximizeWindow 1 OK -> recoverWindow 1 OK
(restored pre-maximize rect), window returns to its original position;
bridge path verified via WMS rect chain [0,0]->[0,0,3120,1955]->[0,0,1140,760].

Co-Authored-By: Claude <noreply@anthropic.com>
Bridge-side counterpart to the wry https-scheme fix. The previous bridge
migration dropped the create-time https_intercept_protocols field from
WebviewCreateRequest, causing the ArkTS httpsInterceptProtocols Set to be
empty at first loadUrl and onInterceptRequest to early-return null.

- WebviewCreateRequest (lib.rs:385): add https_intercept_protocol_list:
  Option<Vec<String>>, populated by wry when use_https && protocols exist.
- WebviewCreatePayload (WebviewPlugin.ets:148): add matching
  httpsInterceptProtocolList field; seed httpsInterceptProtocols Set at
  create (WebviewPlugin.ets:1625) so the Set is non-empty before loadUrl.
- callbacks.rs / WebviewPlugin.ets: add full [bridge https-intercept] and
  [https-intercept] hilog chain (received/enter/extracted/reverted/calling/
  returned/success/passthrough) for observability — the previous path was
  silent, masking the empty-Set early-return.
- plugin-webview Cargo.toml: add log = "0.4" dependency (callbacks.rs/lib.rs
  use log::warn!/log::info! but the crate never declared log -> E0433).

Verified 2026-08-28 on HUAWEI MateBook Pro desktop with the wry fix: all
4 https-scheme cases PASS (page-load/secure-context/subresource/external).

Co-Authored-By: Claude <noreply@anthropic.com>
…t want.uri

The file manager (com.huawei.hmos.filemanager MainAbility) reads the
target from want.parameters.fileUri + want.parameters.external_storage_uuid,
NOT from top-level want.uri. The previous code put the virtual uri in
want.uri, which FM ignores -> FM defaulted to home (stayed on the homepage
instead of navigating to the parent directory). This was the real root
cause of §22-3 failing; the earlier "subPath empty" hypothesis was wrong
(subPath-non-empty paths also stayed on home).

Fix: move the mapped uri into parameters.fileUri and add
external_storage_uuid = "LOCAL" (the local disk id, verified via FM
diskNameArray; cloud = "cloud").

Device experiment (2026-08-28, aa start matrix): with parameters.fileUri +
external_storage_uuid="LOCAL", FM does a two-stage setCurrentUri (myPC
intermediate -> target uri) and navigates to Documents/Download/Desktop.
In app semantics Rust takes path.parent() so reveal always opens the
top-level public folder containing the file — this is within OHOS FM
capabilities (the launch entry only resolves top-level virtual folder
names; deeper subpaths hit isFolder error and fall back).

Note: the 2026-08-20 "PASS" was a misread — FM actually stayed on the
home/My Computer page; "empty uri when get uuid" is per-startup noise, not
a URI rejection.

Verified 2026-08-28 on HUAWEI MateBook Pro desktop: setCurrentUri reaches
file://docs/storage/Users/currentUser/Documents (was: home).

Co-Authored-By: Claude <noreply@anthropic.com>
ProcessInitializer.initialize() never set __native_module__ in AppStorage,
so every AppStorage.get(NATIVE_MODULE_STORAGE_KEY) reader (FloatPage × button
/ aboutToDisappear, menu.ets closeWindow, NativeAbility windowStatusChange)
returned undefined → notifyWindowClose / notifyWindowStatus were never
invoked. On Float sub-window close, the Rust tauri manager kept a zombie
entry → getByLabel returned a dead handle → same-label windows couldn't be
rebuilt (manual_tests §二十八 close lifecycle defect).

Fix: publish the primary native module to AppStorage at the end of
ProcessInitializer.initialize(). One write fixes all readers.

Verified on HUAWEI MateBook Pro (API 23): hilog shows "Window N close
notified via NAPI" (was "not available"); windowId increments 1→2→3 on
repeated create/close of the same-label overlay.

Co-Authored-By: Claude <noreply@anthropic.com>
ArkWeb routes physical keyboard input through the IME pipeline: native DOM
keydown/keyup are degenerate (empty key/code, no auto-repeat, fake D/U pair
per repeat cycle). MainPage.onKeyPreIme receives clean continuous Down events
with keyCode intact (~51ms interval) - the only viable synthesis mount point.

- helper/key_synthesis.ets: keyCode->DOM mapping table, Set-method repeat
  detection (mirrors tao PRESSED_KEYS), modifier self-tracking, controller
  registry (executor pattern), synthesizeDomKeyEvent entry + KEY_SYNTHESIS_SHIM
  (window capture-phase stopImmediatePropagation, no preventDefault so IME
  text insertion is unaffected)
- MainPage.onKeyPreIme: fall-through feeds synthesizer; consumed accelerators
  never leak into the page
- WebviewPlugin: register controllers (incl. main window) via
  notifyKeySynthesisController; append shim to javaScriptOnDocumentStart for
  main window only (Float sub-windows lack onKeyPreIme wiring - injecting
  there would drop all key events). scriptRules = union of app init-script
  rules (empty array matches no document; '*' does not match tauri:// custom
  protocol - both observed on device)

Verified on API 23 desktop: hold->continuous repeat=true with key/code,
no grey native pairs, no text doubling; tap->repeat=false; accelerator
interception consumes without leaking.
feat(webview): synthesize DOM key repeat events on OHOS (key-synthesis)
…ugins with lifecycle and window/webview/url fixes

- New facade crates: plugin-accessibility, plugin-screenshot,
  plugin-continuation; ArkTS accessibility plugin; pack-plugins now
  aggregates 16 bridge plugins
- app.rs: continuation snapshot store/take/peek APIs (pure Mutex, no
  bridge signalling) with unit tests; lifecycle wires optional
  isContinuation/parametersJson on onNewWant and onCreate
- webview: create_pdf accepts WebviewPdfConfig layout (forwarded from
  wry); NewWindowDialog/WebviewPlugin window fixes
- url: reveal-in-dir passes target via want.parameters.fileUri so the
  file manager navigates to the item
- window: WindowPlugin fixes (pre-maximize rect recovery, focusable)
…se class

The override must be (windowStage) => void without want/launchParam and
non-async, otherwise the framework does not invoke it and cold-start
continuation falls back to a blank window. Delegate to the full
window-stage setup via a fire-and-forget call to onWindowStageCreate.
…n menubar

StatusbarPlugin: answer remove/add only after the AsyncCallback settles,
fixing the double-click race when replacing a tray icon. MenuBarComponent:
extract nativeIconResource() with a sys.symbol.folder case and render
top-level nativeIcon items as SymbolGlyph (previously top-level only
rendered bitmap images, so mapped symbols were invisible).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants