From 4bff178bca04d0e56d8f6a5f277b07eb1a72f51e Mon Sep 17 00:00:00 2001 From: sshropshire <58225613+sshropshire@users.noreply.github.com> Date: Thu, 25 Mar 2021 15:28:53 -0500 Subject: [PATCH] Wait for Window to Load Before Executing Javascript Callback (#42) * Run javascript event notifications after window has loaded. Signed-off-by: Steven Shropshire * Update CHANGELOG. Signed-off-by: Steven Shropshire * Modify CHANGELOG. Signed-off-by: Steven Shropshire * Fix javascript injection unit test. Signed-off-by: Steven Shropshire * Add TODOs. Signed-off-by: Steven Shropshire * Remove new line restriction from regex. Signed-off-by: Steven Shropshire * Remove trailing new lines from javascript source. Signed-off-by: Steven Shropshire Co-authored-by: Sarah Koop --- CHANGELOG.md | 1 + .../popupbridge/demo/PopupActivity.java | 4 +-- .../api/PopupBridgeClient.java | 31 ++++++++++++++++--- .../braintreepayments/api/MockWebView.java | 4 ++- .../api/PopupBridgeClientUnitTest.java | 8 ++--- 5 files changed, 37 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 03bba5a..2ea1d26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## unreleased * Update browser-switch to 2.0.0-beta2 +* Run `onComplete` and `onCancel` callbacks after WebView window has loaded (fixes #26) ## 4.0.0-beta1 diff --git a/Demo/src/main/java/com/braintreepayments/popupbridge/demo/PopupActivity.java b/Demo/src/main/java/com/braintreepayments/popupbridge/demo/PopupActivity.java index 44058e7..bc96783 100644 --- a/Demo/src/main/java/com/braintreepayments/popupbridge/demo/PopupActivity.java +++ b/Demo/src/main/java/com/braintreepayments/popupbridge/demo/PopupActivity.java @@ -5,10 +5,10 @@ import android.os.Bundle; import android.webkit.WebView; -import com.braintreepayments.api.PopupBridgeClient; - import androidx.appcompat.app.AppCompatActivity; +import com.braintreepayments.api.PopupBridgeClient; + public class PopupActivity extends AppCompatActivity { private WebView mWebView; diff --git a/PopupBridge/src/main/java/com/braintreepayments/api/PopupBridgeClient.java b/PopupBridge/src/main/java/com/braintreepayments/api/PopupBridgeClient.java index b51ae65..2b9a154 100644 --- a/PopupBridge/src/main/java/com/braintreepayments/api/PopupBridgeClient.java +++ b/PopupBridge/src/main/java/com/braintreepayments/api/PopupBridgeClient.java @@ -104,10 +104,20 @@ private void onBrowserSwitchResult(BrowserSwitchResult result) { Uri returnUri = result.getDeepLinkUrl(); if (result.getStatus() == BrowserSwitchStatus.CANCELED) { runJavaScriptInWebView("" - + "if (typeof window.popupBridge.onCancel === 'function') {" - + " window.popupBridge.onCancel();" + + "function notifyCanceled() {" + + " if (typeof window.popupBridge.onCancel === 'function') {" + + " window.popupBridge.onCancel();" + + " } else {" + + " window.popupBridge.onComplete(null, null);" + + " }" + + "}" + + "" + + "if (document.readyState === 'complete') {" + + " notifyCanceled();" + "} else {" - + " window.popupBridge.onComplete(null, null);" + + " window.addEventListener('load', function () {" + + " notifyCanceled();" + + " });" + "}"); return; } else if (result.getStatus() == BrowserSwitchStatus.SUCCESS) { @@ -140,7 +150,20 @@ private void onBrowserSwitchResult(BrowserSwitchResult result) { payload = json.toString(); } - runJavaScriptInWebView(String.format("window.popupBridge.onComplete(%s, %s);", error, payload)); + String successJavascript = String.format("" + + "function notifyComplete() {" + + " window.popupBridge.onComplete(%s, %s);" + + "}" + + "" + + "if (document.readyState === 'complete') {" + + " notifyComplete();" + + "} else {" + + " window.addEventListener('load', function () {" + + " notifyComplete();" + + " });" + + "}", error, payload); + + runJavaScriptInWebView(successJavascript); } @JavascriptInterface diff --git a/PopupBridge/src/test/java/com/braintreepayments/api/MockWebView.java b/PopupBridge/src/test/java/com/braintreepayments/api/MockWebView.java index 82d0403..683f1af 100644 --- a/PopupBridge/src/test/java/com/braintreepayments/api/MockWebView.java +++ b/PopupBridge/src/test/java/com/braintreepayments/api/MockWebView.java @@ -19,7 +19,9 @@ public MockWebView(Context context) { @Override public void evaluateJavascript(String script, ValueCallback resultCallback) { - String expression = "window\\.popupBridge\\.onComplete\\((.*), (.*)\\)"; + // match everything except semi-colon to capture each parameter as a whole + // without drifting into other lines + String expression = "window\\.popupBridge\\.onComplete\\(([^;]*), ([^;]*)\\);"; mJavascriptEval = script; Pattern pattern = Pattern.compile(expression); Matcher match = pattern.matcher(script); diff --git a/PopupBridge/src/test/java/com/braintreepayments/api/PopupBridgeClientUnitTest.java b/PopupBridge/src/test/java/com/braintreepayments/api/PopupBridgeClientUnitTest.java index c1e9e76..e7aa159 100644 --- a/PopupBridge/src/test/java/com/braintreepayments/api/PopupBridgeClientUnitTest.java +++ b/PopupBridge/src/test/java/com/braintreepayments/api/PopupBridgeClientUnitTest.java @@ -71,7 +71,7 @@ public void constructor_whenWebViewIsNull_throwsException() { try { new PopupBridgeClient(fragmentActivity, null, "my-custom-url-scheme"); fail("Should throw"); - } catch (IllegalArgumentException e){ + } catch (IllegalArgumentException e) { assertEquals(e.getMessage(), "WebView is null"); } } @@ -190,7 +190,7 @@ public void onBrowserSwitchResult_whenReturnUrlHasNoQueryParams_reportsPayloadWi } @Test - public void onBrowserSwitchResult_whenReturnUrlIncludesFragmentIdentifier_reportsPayloadWithFragmentIdentifier() + public void onBrowserSwitchResult_whenReturnUrlIncludesFragmentIdentifier_reportsPayloadWithFragmentIdentifier() throws JSONException { BrowserSwitchResult result = mock(BrowserSwitchResult.class); when(result.getStatus()).thenReturn(BrowserSwitchStatus.SUCCESS); @@ -242,8 +242,8 @@ public void onBrowserSwitchResult_whenNoPath_returnsEmptyString() throws JSONExc BrowserSwitchResult result = mock(BrowserSwitchResult.class); when(result.getStatus()).thenReturn(BrowserSwitchStatus.SUCCESS); - Uri uri = new Uri.Builder() - .scheme("my-custom-url-scheme") + Uri uri = new Uri.Builder() + .scheme("my-custom-url-scheme") .authority("popupbridgev1") .build(); when(result.getDeepLinkUrl()).thenReturn(uri);