Skip to content

fix(windows): honor navigation success/failure + include title in nav events - #3982

Open
FaithfulAudio wants to merge 2 commits into
react-native-webview:masterfrom
FacilitronWorks:wv2-navigation-error-and-title
Open

FaithfulAudio wants to merge 2 commits into
react-native-webview:masterfrom
FacilitronWorks:wv2-navigation-error-and-title

Conversation

@FaithfulAudio

Copy link
Copy Markdown

Part of the hardening series offered in #3980. This is WV-2: honor navigation success/failure, and populate title in nav events.

Problem

OnNavigationCompleted never reads args.IsSuccess() / args.WebErrorStatus() — it unconditionally dispatches onLoadingFinish. Separately, every nav-event struct in the codegen (RCTWebView2.g.h) already has a title field, but nothing on Windows ever sets it.

Real-world failure mode

A DNS failure, TLS error, or the device going offline mid-navigation all report as onLoadingFinish to JS — a page that failed to load looks identical to one that succeeded. Apps that branch on onLoadingError to show a retry/offline UI (a common pattern, and one this monorepo's own offline-first FIT app relies on) never see it fire on Windows. Apps that display nativeEvent.title (e.g. for a browser-chrome-style header) get an empty string forever on Windows.

Fix

  • OnNavigationCompleted branches on args.IsSuccess(). On failure it emits onLoadingError with event.code set from args.WebErrorStatus() (cast to int32_t) instead of onLoadingFinish, and skips the message-bridge injection that follows.
  • CurrentTitle() (new, best-effort, empty string if CoreWebView2 isn't ready or the read throws) is wired into onLoadingStart, onLoadingFinish, onLoadingError, and onSourceChanged.

Validation status

Not compiled against this repo. No Windows build was run this pass — verified by hand against RCTWebView2ComponentView.cpp, not by building.

Both behaviors — checking IsSuccess()/WebErrorStatus() on nav-completed, and populating title from DocumentTitle() — are production-proven in a parallel, independently-built Windows new-arch WebView2 Fabric component running in Facilitron FIT (react-native-windows 0.83.2, WinAppSDK 1.8, ARM64) — see the production twin: FacilitronWorks/react-native-webview-windows. Renamed symbols, hand-ported to this repo's codegen/class shape — not a copy-paste.

Related: #3980

… events

OnNavigationCompleted never consulted args.IsSuccess() / args.WebErrorStatus(),
so DNS failures, TLS errors, and offline states all fired onLoadingFinish --
a failed load and a successful one were indistinguishable to JS. This
branches on IsSuccess() and emits onLoadingError with the
CoreWebView2WebErrorStatus code on failure instead.

Also includes DocumentTitle() in onLoadingStart / onLoadingFinish /
onLoadingError / onSourceChanged -- the `title` field already exists on
every one of these event structs in the codegen (RCTWebView2.g.h) but was
never populated on Windows.

Validation: both behaviors match a parallel production Windows new-arch
WebView2 Fabric component (Facilitron FIT, react-native-windows 0.83.2) --
see FacilitronWorks/react-native-webview-windows. Not compiled against this
repo in this change; needs CI here to validate.
event.url = winrt::to_string(m_webView.Source().AbsoluteCanonicalUri());
}
event.loading = true;
event.title = CurrentTitle();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an extra string copy, right?
Can we have CurrentTitle return the hstring, then do the to_string here? - I think that avoids the extra string copy.

Per review: defer the UTF-8 conversion to the boundary that needs it instead of
doing it inside the accessor. CurrentTitle() now returns the WinRT hstring
straight from CoreWebView2::DocumentTitle(), and the four event-emitter call
sites do winrt::to_string() where they assign to the std::string field.

Honest note on the saving: on today's four call sites - all of which assign to
a std::string - this is the same single allocation either way, since the old
version returned by value and therefore moved rather than copied. What it does
buy is the right shape for a WinRT accessor: hstring is a refcounted handle, so
any future caller that only needs to compare, test-empty, or forward the title
pays no allocation at all, and the conversion now happens once at the point of
use rather than unconditionally inside the getter.
@FaithfulAudio

Copy link
Copy Markdown
Author

Good catch — done. CurrentTitle() now returns the hstring straight from CoreWebView2::DocumentTitle(), and the four call sites do winrt::to_string() where they assign to the std::string event field.

One honest correction on the saving, since I'd rather you have the real number: on today's four call sites — all of which assign to a std::string — it's the same single allocation either way. The old version returned by value, so that was a move, not a copy. What the change actually buys is the right shape for a WinRT accessor: hstring is a refcounted handle, so any future caller that only needs to compare, test-empty or forward the title pays no allocation at all, and the conversion happens at the point of use rather than unconditionally inside the getter. Worth doing for that reason — just not a copy I can claim to have removed today.

@FaithfulAudio

Copy link
Copy Markdown
Author

Friendly bump — this has been open for a while without a decision. Is there anything you need from me to move it along (rebase onto current main, splitting it, extra tests)? Happy to update it either way.

This branch has not been deployed

No deployments
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.

2 participants