From 64b8738e1072cfe40950aa6198fa6332162c9fce Mon Sep 17 00:00:00 2001 From: Alexandre Jacinto Date: Thu, 31 Aug 2023 09:54:11 +0100 Subject: [PATCH] Prepare release of version 4.0.0-OS14 (#43) * RMET-2119 InAppBrowser Plugin - Check for element before obtaining it (#40) * fix: check if there's a next token before trying to obtain it References: https://outsystemsrd.atlassian.net/browse/RMET-2119 * chore: update changelog * RMET-2802 InAppBrowser Plugin - Multiple minor fixes from the community (#42) * Fix memory leak - initially reported in https://github.com/apache/cordova-plugin-inappbrowser/issues/290 Whenever closing an InAppBrowser instance, a webview was left in memory with about:blank page. This change fixes the issue by destroying and freeing the inAppWebView object. * Fix beforeLoad not being called in some requests - initially reported in https://github.com/apache/cordova-plugin-inappbrowser/issues/686 The waitForBeforeload flag was preventing beforeLoad from being called on every GET request. * Do not lose callbackContext when opening a SYSTEM url This fixes a condition where it was not possible to open a SYSTEM url while an InAppBrowser instance is displayed. The callbackContext of the InAppBrowser instance was lost when the SYSTEM url was opened. This fixes the issue by not setting the callbackContext on SYSTEM urls. * Fix crash when pausing/resuming application after closing InAppBrowser * Fix _loadAfterBeforeload callback not working due to this.rootName not being defined. * Reset beforeload variable if a new instance of InAppBrowser is opened without beforeload setting * chore: update changelog References: https://outsystemsrd.atlassian.net/browse/RMET-2802 * refactor: remove empty lines * refactor: use ternary operator and remove unnecessary ones Why: We can refactor the if-else block that assigns a value to beforeload to use a ternary operator. On the other hand, we can remove the unnecessary ternary operators used to assign a boolean value. They are redundant. References: https://outsystemsrd.atlassian.net/browse/RMET-2802 --------- Co-authored-by: Leonardo Monteiro Fernandes Co-authored-by: Nelson Lopes Silva <5671236+nflsilva@users.noreply.github.com> --------- Co-authored-by: Leonardo Monteiro Fernandes Co-authored-by: Nelson Lopes Silva <5671236+nflsilva@users.noreply.github.com> --- CHANGELOG.md | 6 ++++ src/android/InAppBrowser.java | 67 ++++++++++++++++++----------------- www/inappbrowser.js | 2 +- 3 files changed, 42 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 94ee8e893..ffff97265 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ### [Unreleased] + +### Fixes +- Do not lose callbackContext when opening a SYSTEM url (https://outsystemsrd.atlassian.net/browse/RMET-2802). +- Fix beforeLoad not being called in some requests (https://outsystemsrd.atlassian.net/browse/RMET-2802). +- Fix memory leak with webview (https://outsystemsrd.atlassian.net/browse/RMET-2802). + ### Fix - Android - InAppBrowser not opening when no options are passed - check for next element before trying to obtain. (https://outsystemsrd.atlassian.net/browse/RMET-2119) diff --git a/src/android/InAppBrowser.java b/src/android/InAppBrowser.java index dad557268..7aa4af311 100644 --- a/src/android/InAppBrowser.java +++ b/src/android/InAppBrowser.java @@ -166,7 +166,6 @@ public class InAppBrowser extends CordovaPlugin { */ public boolean execute(String action, CordovaArgs args, final CallbackContext callbackContext) throws JSONException { if (action.equals("open")) { - this.callbackContext = callbackContext; final String url = args.getString(0); String t = args.optString(1); if (t == null || t.equals("") || t.equals(NULL)) { @@ -177,6 +176,11 @@ public boolean execute(String action, CordovaArgs args, final CallbackContext ca LOG.d(LOG_TAG, "target = " + target); + final boolean keepCallback = !SYSTEM.equals(target); + if (keepCallback) { + this.callbackContext = callbackContext; + } + this.cordova.getActivity().runOnUiThread(new Runnable() { @Override public void run() { @@ -254,7 +258,7 @@ else if (SYSTEM.equals(target)) { } PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, result); - pluginResult.setKeepCallback(true); + pluginResult.setKeepCallback(keepCallback); callbackContext.sendPluginResult(pluginResult); } }); @@ -271,12 +275,6 @@ else if (action.equals("loadAfterBeforeload")) { @SuppressLint("NewApi") @Override public void run() { - if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.O) { - currentClient.waitForBeforeload = false; - inAppWebView.setWebViewClient(currentClient); - } else { - ((InAppBrowserClient)inAppWebView.getWebViewClient()).waitForBeforeload = false; - } inAppWebView.loadUrl(url); } }); @@ -360,7 +358,7 @@ public void onReset() { */ @Override public void onPause(boolean multitasking) { - if (shouldPauseInAppBrowser) { + if (shouldPauseInAppBrowser && inAppWebView != null) { inAppWebView.onPause(); } } @@ -370,7 +368,7 @@ public void onPause(boolean multitasking) { */ @Override public void onResume(boolean multitasking) { - if (shouldPauseInAppBrowser) { + if (shouldPauseInAppBrowser && inAppWebView != null) { inAppWebView.onResume(); } } @@ -549,6 +547,16 @@ public void onPageFinished(WebView view, String url) { dialog.dismiss(); dialog = null; } + + // Fix for webView window not being destroyed correctly causing memory leak + // (https://github.com/apache/cordova-plugin-inappbrowser/issues/290) + if (url.equals("about:blank")) { + inAppWebView.onPause(); + inAppWebView.removeAllViews(); + inAppWebView.destroyDrawingCache(); + inAppWebView.destroy(); + inAppWebView = null; + } } }); // NB: From SDK 19: "If you call methods on WebView from any thread @@ -647,48 +655,48 @@ public String showWebPage(final String url, HashMap features) { if (features != null) { String show = features.get(LOCATION); if (show != null) { - showLocationBar = show.equals("yes") ? true : false; + showLocationBar = show.equals("yes"); } if(showLocationBar) { String hideNavigation = features.get(HIDE_NAVIGATION); String hideUrl = features.get(HIDE_URL); - if(hideNavigation != null) hideNavigationButtons = hideNavigation.equals("yes") ? true : false; - if(hideUrl != null) hideUrlBar = hideUrl.equals("yes") ? true : false; + if(hideNavigation != null) hideNavigationButtons = hideNavigation.equals("yes"); + if(hideUrl != null) hideUrlBar = hideUrl.equals("yes"); } String zoom = features.get(ZOOM); if (zoom != null) { - showZoomControls = zoom.equals("yes") ? true : false; + showZoomControls = zoom.equals("yes"); } String hidden = features.get(HIDDEN); if (hidden != null) { - openWindowHidden = hidden.equals("yes") ? true : false; + openWindowHidden = hidden.equals("yes"); } String hardwareBack = features.get(HARDWARE_BACK_BUTTON); if (hardwareBack != null) { - hadwareBackButton = hardwareBack.equals("yes") ? true : false; + hadwareBackButton = hardwareBack.equals("yes"); } else { hadwareBackButton = DEFAULT_HARDWARE_BACK; } String mediaPlayback = features.get(MEDIA_PLAYBACK_REQUIRES_USER_ACTION); if (mediaPlayback != null) { - mediaPlaybackRequiresUserGesture = mediaPlayback.equals("yes") ? true : false; + mediaPlaybackRequiresUserGesture = mediaPlayback.equals("yes"); } String cache = features.get(CLEAR_ALL_CACHE); if (cache != null) { - clearAllCache = cache.equals("yes") ? true : false; + clearAllCache = cache.equals("yes"); } else { cache = features.get(CLEAR_SESSION_CACHE); if (cache != null) { - clearSessionCache = cache.equals("yes") ? true : false; + clearSessionCache = cache.equals("yes"); } } String shouldPause = features.get(SHOULD_PAUSE); if (shouldPause != null) { - shouldPauseInAppBrowser = shouldPause.equals("yes") ? true : false; + shouldPauseInAppBrowser = shouldPause.equals("yes"); } String wideViewPort = features.get(USER_WIDE_VIEW_PORT); if (wideViewPort != null ) { - useWideViewPort = wideViewPort.equals("yes") ? true : false; + useWideViewPort = wideViewPort.equals("yes"); } String closeButtonCaptionSet = features.get(CLOSE_BUTTON_CAPTION); if (closeButtonCaptionSet != null) { @@ -713,18 +721,18 @@ public String showWebPage(final String url, HashMap features) { } String showFooterSet = features.get(FOOTER); if (showFooterSet != null) { - showFooter = showFooterSet.equals("yes") ? true : false; + showFooter = showFooterSet.equals("yes"); } String footerColorSet = features.get(FOOTER_COLOR); if (footerColorSet != null) { footerColor = footerColorSet; } - if (features.get(BEFORELOAD) != null) { - beforeload = features.get(BEFORELOAD); - } + + beforeload = features.get(BEFORELOAD) != null ? features.get(BEFORELOAD) : ""; + String fullscreenSet = features.get(FULLSCREEN); if (fullscreenSet != null) { - fullscreen = fullscreenSet.equals("yes") ? true : false; + fullscreen = fullscreenSet.equals("yes"); } } @@ -812,7 +820,6 @@ public class InAppBrowserClient extends WebViewClient { EditText edittext; CordovaWebView webView; String beforeload; - boolean waitForBeforeload; /** * Constructor. @@ -824,7 +831,6 @@ public InAppBrowserClient(CordovaWebView webView, EditText mEditText, String bef this.webView = webView; this.edittext = mEditText; this.beforeload = beforeload; - this.waitForBeforeload = beforeload != null; } /** @@ -885,7 +891,7 @@ public boolean shouldOverrideUrlLoading(String url, String method) { } // On first URL change, initiate JS callback. Only after the beforeload event, continue. - if (useBeforeload && this.waitForBeforeload) { + if (useBeforeload) { if(sendBeforeLoad(url, method)) { return true; } @@ -980,9 +986,6 @@ else if (!url.startsWith("http:") && !url.startsWith("https:") && url.matches("^ } } - if (useBeforeload) { - this.waitForBeforeload = true; - } return override; } diff --git a/www/inappbrowser.js b/www/inappbrowser.js index bb881378c..e499a3baa 100644 --- a/www/inappbrowser.js +++ b/www/inappbrowser.js @@ -41,7 +41,7 @@ _eventHandler: function (event) { if (event && (event.type in this.channels)) { if (event.type === 'beforeload') { - this.channels[event.type].fire(event, this._loadAfterBeforeload); + this.channels[event.type].fire(event, this._loadAfterBeforeload.bind(this)); } else { this.channels[event.type].fire(event); }