Skip to content

fix(android): return openWebView id exactly once#476

Merged
riderx merged 2 commits intomainfrom
codex/webview-id-return
Mar 17, 2026
Merged

fix(android): return openWebView id exactly once#476
riderx merged 2 commits intomainfrom
codex/webview-id-return

Conversation

@riderx
Copy link
Member

@riderx riderx commented Mar 17, 2026

What

  • route Android openWebView() promise resolution through WebViewDialog so every success path returns { id: instanceId }
  • add a single guard to prevent duplicate resolve attempts between presentWebView() and onPageFinished()
  • document the Android vs iOS isPresentAfterPageLoad promise timing in the generated API docs

Why

How

  • removed the eager call.resolve({ id }) from InAppBrowserPlugin.openWebView()
  • added resolveOpenWebViewIfNeeded() in WebViewDialog to build the { id: instanceId } payload once and reuse it from both immediate-present and page-finish flows
  • updated the JSDoc for openWebView / isPresentAfterPageLoad, then regenerated README.md with docgen

Testing

  • bunx eslint . --fix
  • bunx prettier-pretty-check "**/*.{css,html,ts,js,java}" --plugin=prettier-plugin-java --write
  • bunx node-swiftlint --fix --format
  • bun run build
  • bun run verify:ios
  • bun run verify:android

Not Tested

  • manual device validation of hidden and isPresentAfterPageLoad=true webview flows on Android

Summary by CodeRabbit

  • Bug Fixes

    • Fixed openWebView Promise resolution: Android now resolves when the webview is ready for control (timing varies by presentation mode); iOS resolves when the native webview is created. Ensures the call is resolved or rejected once and surfaces initial-load or external-handler failures.
  • Documentation

    • Added platform-specific Promise timing details for openWebView/openWebViewOptions on Android and iOS.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 17, 2026

📝 Walkthrough

Walkthrough

Deferred the Android openWebView() promise resolution: immediate resolve was removed from InAppBrowserPlugin; WebViewDialog now tracks resolution state and resolves or rejects the original PluginCall from specific lifecycle points (show, first page load, shouldOverrideUrlLoading, error paths). Documentation updated with platform timing notes.

Changes

Cohort / File(s) Summary
Documentation
README.md, src/definitions.ts
Added platform-specific timing for openWebView()/isPresentAfterPageLoad: Android resolves when the WebView is ready to be controlled (varies by presentation mode); iOS resolves as soon as the native WebView is created.
Android Plugin Entry
android/src/main/java/ee/forgr/capacitor_inappbrowser/InAppBrowserPlugin.java
Removed immediate PluginCall.resolve() for openWebView() so the call is no longer resolved at open time.
Android WebView Dialog
android/src/main/java/ee/forgr/capacitor_inappbrowser/WebViewDialog.java
Added openWebViewResolved flag, resolveOpenWebViewIfNeeded() and rejectOpenWebViewIfNeeded() helpers; replaced direct resolves with guarded resolve/reject calls across show, onPageFinished, shouldOverrideUrlLoading, and error paths to ensure single resolve/reject and propagate failures.

Sequence Diagram(s)

sequenceDiagram
    participant JS as Client (JS)
    participant Plugin as InAppBrowserPlugin
    participant Dialog as WebViewDialog
    participant WebView as NativeWebView
    JS->>Plugin: openWebView()
    Plugin->>Dialog: create & show (no immediate resolve)
    Dialog->>WebView: create native webview
    alt iOS
      WebView-->>Dialog: created
      Dialog->>Plugin: resolveOpenWebViewIfNeeded({id})
      Plugin-->>JS: promise resolved (id)
    else Android (hidden/immediate)
      WebView-->>Dialog: ready for control
      Dialog->>Plugin: resolveOpenWebViewIfNeeded({id})
      Plugin-->>JS: promise resolved (id)
    else Android (isPresentAfterPageLoad = true)
      WebView->>WebView: load first page
      WebView-->>Dialog: onPageFinished
      Dialog->>Plugin: resolveOpenWebViewIfNeeded({id})
      Plugin-->>JS: promise resolved (id)
    end
    alt Error / external URL handler missing
      Dialog->>Plugin: rejectOpenWebViewIfNeeded(error)
      Plugin-->>JS: promise rejected (error)
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

  • Merge PR #418 #426: Modifies Android openWebView resolution flow and WebViewDialog handling—overlaps in deferred resolution logic.
  • Gate WebView JS hide/show via config #420: Touches Android WebViewDialog and InAppBrowserPlugin lifecycle/state around open/presentation and JS visibility injection.
  • feat: implement hidden webview #419: Changes WebViewDialog openWebView resolution behavior and where the PluginCall is handled; closely related to the resolve/reject adjustments here.

Poem

🐰
A hop, a wait, a patient cheer—
I hold the promise, not yet here.
Android times the webview's dawn,
iOS greets it as soon as it's born.
Resolve with care — the rabbit's song, light and clear.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: resolving the openWebView promise exactly once on Android by consolidating all resolution paths through WebViewDialog.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/webview-id-return
📝 Coding Plan
  • Generate coding plan for human review comments

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 85a54eaf64

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 2808 to +2811
boolean usePreShowScript = _options.getPreShowScript() != null && !_options.getPreShowScript().isEmpty();
if (!usePreShowScript) {
show();
_options.getPluginCall().resolve();
resolveOpenWebViewIfNeeded();

Choose a reason for hiding this comment

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

P1 Badge Resolve openWebView on deferred-load failures

With isPresentAfterPageLoad=true, this change makes openWebView() resolve only from onPageFinished, so any first-load failure path that does not reach that callback leaves the promise pending forever. In the same WebViewClient, failure branches like onReceivedError(...) and the custom-scheme ActivityNotFoundException path only emit pageLoadError and never resolve/reject the original plugin call, which means callers never get the { id } needed to recover (close/retry) when initial navigation fails.

Useful? React with 👍 / 👎.

Copy link
Member Author

Choose a reason for hiding this comment

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

Addressed in d26c6c7. The deferred-load path now rejects the original openWebView() call on initial main-frame failures before first initialization, specifically for custom-scheme ActivityNotFoundException handling and onReceivedError(...). That preserves the single-return contract while avoiding a hung promise when isPresentAfterPageLoad=true.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@android/src/main/java/ee/forgr/capacitor_inappbrowser/WebViewDialog.java`:
- Around line 170-177: In resolveOpenWebViewIfNeeded, don't silently return when
the plugin call is missing or resolve with an empty id: ensure _options != null
and obtain PluginCall call = _options.getPluginCall(); if call is null, set
openWebViewResolved = true and call.reject(...) (or otherwise send an explicit
error) so the promise won't hang; likewise validate instanceId is
non-null/non-empty before constructing the JSObject and if invalid reject the
call with a clear error instead of resolving; only mark openWebViewResolved
after a definitive resolve or reject to preserve the “exactly once” contract.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4c4566d8-acce-4062-a8bb-996d8d8f7eff

📥 Commits

Reviewing files that changed from the base of the PR and between 2feb465 and 85a54ea.

📒 Files selected for processing (4)
  • README.md
  • android/src/main/java/ee/forgr/capacitor_inappbrowser/InAppBrowserPlugin.java
  • android/src/main/java/ee/forgr/capacitor_inappbrowser/WebViewDialog.java
  • src/definitions.ts
💤 Files with no reviewable changes (1)
  • android/src/main/java/ee/forgr/capacitor_inappbrowser/InAppBrowserPlugin.java

@sonarqubecloud
Copy link

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
android/src/main/java/ee/forgr/capacitor_inappbrowser/WebViewDialog.java (1)

2464-2468: ⚠️ Potential issue | 🟠 Major

Handle preventDeeplink blocked initial navigation by rejecting once.

When preventDeeplink=true and the initial URL is non-http(s), this path returns early without reject. If isPresentAfterPageLoad=true, openWebView() can remain unresolved.

💡 Proposed fix
 if (_options.getPreventDeeplink()) {
     Log.d("InAppBrowser", "preventDeeplink is true");
     if (isNotHttpOrHttps) {
+        if (request.isForMainFrame() && !isInitialized) {
+            if (_options.getCallbacks() != null) {
+                _options.getCallbacks().pageLoadError();
+            }
+            rejectOpenWebViewIfNeeded("Blocked non-http(s) URL because preventDeeplink=true: " + url);
+        }
         return true;
     }
 }

Also applies to: 2508-2508

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@android/src/main/java/ee/forgr/capacitor_inappbrowser/WebViewDialog.java`
around lines 2464 - 2468, When preventing deeplinks in WebViewDialog (check of
_options.getPreventDeeplink() and isNotHttpOrHttps), the code currently returns
early without rejecting the pending openWebView promise; update the branch to
explicitly reject the promise returned by openWebView() (or call the stored
reject handler for that promise) with a clear error message when blocking the
initial navigation, and guard against double-rejection (only reject if the
promise hasn’t been settled). Apply the same fix to the equivalent block
referenced around the other occurrence (the second branch at ~2508).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@android/src/main/java/ee/forgr/capacitor_inappbrowser/WebViewDialog.java`:
- Around line 2464-2468: When preventing deeplinks in WebViewDialog (check of
_options.getPreventDeeplink() and isNotHttpOrHttps), the code currently returns
early without rejecting the pending openWebView promise; update the branch to
explicitly reject the promise returned by openWebView() (or call the stored
reject handler for that promise) with a clear error message when blocking the
initial navigation, and guard against double-rejection (only reject if the
promise hasn’t been settled). Apply the same fix to the equivalent block
referenced around the other occurrence (the second branch at ~2508).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c6fa3af8-8346-4c8f-bb76-29bb02e5de3d

📥 Commits

Reviewing files that changed from the base of the PR and between 85a54ea and d26c6c7.

📒 Files selected for processing (1)
  • android/src/main/java/ee/forgr/capacitor_inappbrowser/WebViewDialog.java

@riderx riderx merged commit 50a9fb4 into main Mar 17, 2026
9 checks passed
@riderx riderx deleted the codex/webview-id-return branch March 17, 2026 13:00
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.

1 participant