diff --git a/README.md b/README.md
index c9155d8a..f33b62b2 100644
--- a/README.md
+++ b/README.md
@@ -460,6 +460,9 @@ When you open a webview with this method, a JavaScript interface is automaticall
- `window.mobileApp.close()`: Closes the webview from JavaScript
- `window.mobileApp.postMessage({detail: {message: "myMessage"}})`: Sends a message from the webview to the app, detail object is the data you want to send to the webview
+Promise timing differs by platform when `isPresentAfterPageLoad` is used.
+Android resolves with `{ id }` after the dialog is ready to control, while iOS resolves with `{ id }` immediately after creating the native webview.
+
| Param | Type |
| ------------- | ----------------------------------------------------------------- |
| **`options`** | OpenWebViewOptions |
@@ -868,7 +871,7 @@ And in the AndroidManifest.xml file:
| **`backgroundColor`** | BackgroundColor | Background color of the browser | BackgroundColor.BLACK | 0.1.0 |
| **`activeNativeNavigationForWebview`** | boolean | If true, enables native navigation gestures within the webview. - Android: Native back button navigates within webview history - iOS: Enables swipe left/right gestures for back/forward navigation | false (Android), true (iOS - enabled by default) | |
| **`disableGoBackOnNativeApplication`** | boolean | Disable the possibility to go back on native application, useful to force user to stay on the webview, Android only | false | |
-| **`isPresentAfterPageLoad`** | boolean | Open url in a new window fullscreen isPresentAfterPageLoad: if true, the browser will be presented after the page is loaded, if false, the browser will be presented immediately. | false | 0.1.0 |
+| **`isPresentAfterPageLoad`** | boolean | Open url in a new window fullscreen isPresentAfterPageLoad: if true, the browser will be presented after the page is loaded, if false, the browser will be presented immediately. Promise timing: on Android, `openWebView()` resolves with the webview id when the webview is ready to be controlled (immediately for hidden/immediate presentation, after the first page load when `isPresentAfterPageLoad` is `true`). On iOS, the promise resolves with the id as soon as the native webview is created, even if presentation is deferred. | false | 0.1.0 |
| **`isInspectable`** | boolean | Whether the website in the webview is inspectable or not, ios only | false | |
| **`isAnimated`** | boolean | Whether the webview opening is animated or not, ios only | true | |
| **`showReloadButton`** | boolean | Shows a reload button that reloads the web page | false | 1.0.15 |
diff --git a/android/src/main/java/ee/forgr/capacitor_inappbrowser/InAppBrowserPlugin.java b/android/src/main/java/ee/forgr/capacitor_inappbrowser/InAppBrowserPlugin.java
index 7a183ea3..e26d7292 100644
--- a/android/src/main/java/ee/forgr/capacitor_inappbrowser/InAppBrowserPlugin.java
+++ b/android/src/main/java/ee/forgr/capacitor_inappbrowser/InAppBrowserPlugin.java
@@ -874,9 +874,6 @@ public void run() {
dialog.activity = InAppBrowserPlugin.this.getActivity();
registerWebView(webViewId, dialog);
dialog.presentWebView();
- JSObject result = new JSObject();
- result.put("id", webViewId);
- call.resolve(result);
}
}
);
diff --git a/android/src/main/java/ee/forgr/capacitor_inappbrowser/WebViewDialog.java b/android/src/main/java/ee/forgr/capacitor_inappbrowser/WebViewDialog.java
index bb50c226..78ab8226 100644
--- a/android/src/main/java/ee/forgr/capacitor_inappbrowser/WebViewDialog.java
+++ b/android/src/main/java/ee/forgr/capacitor_inappbrowser/WebViewDialog.java
@@ -65,6 +65,7 @@
import com.caverock.androidsvg.SVG;
import com.caverock.androidsvg.SVGParseException;
import com.getcapacitor.JSObject;
+import com.getcapacitor.PluginCall;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
@@ -135,6 +136,7 @@ public ProxiedRequest() {
public static final int FILE_CHOOSER_REQUEST_CODE = 1000;
public ValueCallback mUploadMessage;
public ValueCallback mFilePathCallback;
+ private boolean openWebViewResolved;
// Temporary URI for storing camera capture
public Uri tempCameraUri;
@@ -154,6 +156,7 @@ public WebViewDialog(Context context, int theme, Options options, PermissionHand
this._context = context;
this.permissionHandler = permissionHandler;
this.isInitialized = false;
+ this.openWebViewResolved = false;
this.capacitorWebView = capacitorWebView;
}
@@ -165,6 +168,41 @@ public String getInstanceId() {
return instanceId;
}
+ private void resolveOpenWebViewIfNeeded() {
+ if (openWebViewResolved || _options == null) {
+ return;
+ }
+ PluginCall call = _options.getPluginCall();
+ if (call == null) {
+ Log.e("InAppBrowser", "Cannot resolve openWebView: plugin call is null");
+ openWebViewResolved = true;
+ return;
+ }
+ if (instanceId == null || instanceId.isEmpty()) {
+ call.reject("Cannot resolve openWebView: missing webview id");
+ openWebViewResolved = true;
+ return;
+ }
+ JSObject result = new JSObject();
+ result.put("id", instanceId);
+ call.resolve(result);
+ openWebViewResolved = true;
+ }
+
+ private void rejectOpenWebViewIfNeeded(String message) {
+ if (openWebViewResolved || _options == null) {
+ return;
+ }
+ PluginCall call = _options.getPluginCall();
+ if (call == null) {
+ Log.e("InAppBrowser", "Cannot reject openWebView: plugin call is null");
+ openWebViewResolved = true;
+ return;
+ }
+ call.reject(message);
+ openWebViewResolved = true;
+ }
+
// Add this class to provide safer JavaScript interface
private class JavaScriptInterface {
@@ -978,10 +1016,10 @@ public boolean shouldOverrideUrlLoading(WebView view, String url) {
show();
applyHiddenMode();
}
- _options.getPluginCall().resolve();
+ resolveOpenWebViewIfNeeded();
} else if (!this._options.isPresentAfterPageLoad()) {
show();
- _options.getPluginCall().resolve();
+ resolveOpenWebViewIfNeeded();
}
}
@@ -2467,6 +2505,7 @@ public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request
// Notify that a page load error occurred
if (_options.getCallbacks() != null && request.isForMainFrame()) {
_options.getCallbacks().pageLoadError();
+ rejectOpenWebViewIfNeeded("No handler available for external URL: " + url);
}
return true; // prevent WebView from attempting to load the custom scheme
}
@@ -2796,7 +2835,7 @@ public void onPageFinished(WebView view, String url) {
boolean usePreShowScript = _options.getPreShowScript() != null && !_options.getPreShowScript().isEmpty();
if (!usePreShowScript) {
show();
- _options.getPluginCall().resolve();
+ resolveOpenWebViewIfNeeded();
} else {
executorService.execute(
new Runnable() {
@@ -2811,7 +2850,7 @@ public void run() {
@Override
public void run() {
show();
- _options.getPluginCall().resolve();
+ resolveOpenWebViewIfNeeded();
}
}
);
@@ -2889,6 +2928,11 @@ public void onReceivedError(WebView view, WebResourceRequest request, WebResourc
return;
}
_options.getCallbacks().pageLoadError();
+ if (request != null && request.isForMainFrame() && !isInitialized) {
+ CharSequence description = error != null ? error.getDescription() : null;
+ String message = description != null ? "Initial page load failed: " + description : "Initial page load failed";
+ rejectOpenWebViewIfNeeded(message);
+ }
}
@SuppressLint("WebViewClientOnReceivedSslError")
diff --git a/src/definitions.ts b/src/definitions.ts
index 11f51995..557b07f3 100644
--- a/src/definitions.ts
+++ b/src/definitions.ts
@@ -400,6 +400,9 @@ export interface OpenWebViewOptions {
/**
* Open url in a new window fullscreen
* isPresentAfterPageLoad: if true, the browser will be presented after the page is loaded, if false, the browser will be presented immediately.
+ * Promise timing: on Android, `openWebView()` resolves with the webview id when the webview is ready to be controlled
+ * (immediately for hidden/immediate presentation, after the first page load when `isPresentAfterPageLoad` is `true`).
+ * On iOS, the promise resolves with the id as soon as the native webview is created, even if presentation is deferred.
* @since 0.1.0
* @default false
* @example
@@ -852,6 +855,9 @@ export interface InAppBrowserPlugin {
* - `window.mobileApp.close()`: Closes the webview from JavaScript
* - `window.mobileApp.postMessage({detail: {message: "myMessage"}})`: Sends a message from the webview to the app, detail object is the data you want to send to the webview
*
+ * Promise timing differs by platform when `isPresentAfterPageLoad` is used.
+ * Android resolves with `{ id }` after the dialog is ready to control, while iOS resolves with `{ id }` immediately after creating the native webview.
+ *
* @returns Promise that resolves with the created webview id.
* @since 0.1.0
*/