diff --git a/.idea/workspace.xml b/.idea/workspace.xml index fa9db498..f04d5e7c 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -11,21 +11,9 @@ - - - + - - - - - - - - - - @@ -57,8 +45,8 @@ - - + + @@ -431,7 +419,6 @@ - @@ -449,9 +436,9 @@ - + - + @@ -857,8 +844,8 @@ - - + + diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserFlutterPlugin.java b/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserFlutterPlugin.java index 732cf54a..9a722d30 100644 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserFlutterPlugin.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserFlutterPlugin.java @@ -25,6 +25,9 @@ import android.annotation.TargetApi; import android.app.Activity; import android.annotation.SuppressLint; import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.os.Parcelable; import android.provider.Browser; import android.net.Uri; import android.os.Build; @@ -40,7 +43,9 @@ import android.util.Log; import java.io.IOException; import java.io.StringReader; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import io.flutter.plugin.common.MethodCall; @@ -308,14 +313,51 @@ public class InAppBrowserFlutterPlugin implements MethodCallHandler { intent.setData(uri); } intent.putExtra(Browser.EXTRA_APPLICATION_ID, activity.getPackageName()); - activity.startActivity(intent); + // CB-10795: Avoid circular loops by preventing it from opening in the current app + this.openExternalExcludeCurrentApp(intent); // not catching FileUriExposedException explicitly because buildtools<24 doesn't know about it } catch (java.lang.RuntimeException e) { Log.d(LOG_TAG, "InAppBrowserFlutterPlugin: Error loading url "+url+":"+ e.toString()); } } - @TargetApi(8) + /** + * Opens the intent, providing a chooser that excludes the current app to avoid + * circular loops. + */ + private void openExternalExcludeCurrentApp(Intent intent) { + String currentPackage = activity.getPackageName(); + boolean hasCurrentPackage = false; + PackageManager pm = activity.getPackageManager(); + List activities = pm.queryIntentActivities(intent, 0); + ArrayList targetIntents = new ArrayList(); + for (ResolveInfo ri : activities) { + if (!currentPackage.equals(ri.activityInfo.packageName)) { + Intent targetIntent = (Intent)intent.clone(); + targetIntent.setPackage(ri.activityInfo.packageName); + targetIntents.add(targetIntent); + } + else { + hasCurrentPackage = true; + } + } + // If the current app package isn't a target for this URL, then use + // the normal launch behavior + if (hasCurrentPackage == false || targetIntents.size() == 0) { + activity.startActivity(intent); + } + // If there's only one possible intent, launch it directly + else if (targetIntents.size() == 1) { + activity.startActivity(targetIntents.get(0)); + } + // Otherwise, show a custom chooser without the current app listed + else if (targetIntents.size() > 0) { + Intent chooser = Intent.createChooser(targetIntents.remove(targetIntents.size()-1), null); + chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetIntents.toArray(new Parcelable[] {})); + activity.startActivity(chooser); + } + } + private void open(final String url, InAppBrowserOptions options) { Intent intent = new Intent(activity, WebViewActivity.class); diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserWebViewClient.java b/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserWebViewClient.java index 0a0c4f94..315b503f 100644 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserWebViewClient.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserWebViewClient.java @@ -3,10 +3,12 @@ package com.pichillilorenzo.flutter_inappbrowser; import android.content.Intent; import android.graphics.Bitmap; import android.net.Uri; +import android.net.http.SslError; import android.util.Log; import android.webkit.CookieManager; import android.webkit.CookieSyncManager; import android.webkit.HttpAuthHandler; +import android.webkit.SslErrorHandler; import android.webkit.WebView; import android.webkit.WebViewClient; @@ -142,6 +144,41 @@ public class InAppBrowserWebViewClient extends WebViewClient { InAppBrowserFlutterPlugin.channel.invokeMethod("loaderror", obj); } + public void onReceivedSslError (WebView view, SslErrorHandler handler, SslError error) { + super.onReceivedSslError(view, handler, error); + + Map obj = new HashMap<>(); + obj.put("url", error.getUrl()); + obj.put("code", 0); + obj.put("sslerror", error.getPrimaryError()); + String message; + switch (error.getPrimaryError()) { + case SslError.SSL_DATE_INVALID: + message = "The date of the certificate is invalid"; + break; + case SslError.SSL_EXPIRED: + message = "The certificate has expired"; + break; + case SslError.SSL_IDMISMATCH: + message = "Hostname mismatch"; + break; + default: + case SslError.SSL_INVALID: + message = "A generic error occurred"; + break; + case SslError.SSL_NOTYETVALID: + message = "The certificate is not yet valid"; + break; + case SslError.SSL_UNTRUSTED: + message = "The certificate authority is not trusted"; + break; + } + obj.put("message", message); + InAppBrowserFlutterPlugin.channel.invokeMethod("loaderror", obj); + + handler.cancel(); + } + /** * On received http auth request. */ diff --git a/ios/Classes/InAppBrowserWebViewController.swift b/ios/Classes/InAppBrowserWebViewController.swift index 65448fd8..0910065c 100644 --- a/ios/Classes/InAppBrowserWebViewController.swift +++ b/ios/Classes/InAppBrowserWebViewController.swift @@ -480,7 +480,6 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio } func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { - //func webViewDidFinishLoad(_ theWebView: WKWebView) { // update url, stop spinner, update back/forward currentURL = webView.url updateUrlTextField(url: (currentURL?.absoluteString)!) @@ -490,10 +489,18 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio navigationDelegate?.webViewDidFinishLoad(webView) } + func webView(_ webView: WKWebView, + didFailProvisionalNavigation navigation: WKNavigation!, + withError error: Error) { + print("webView:didFailProvisionalNavigationWithError - \(Int(error._code)): \(error.localizedDescription)") + backButton.isEnabled = webView.canGoBack + forwardButton.isEnabled = webView.canGoForward + spinner.stopAnimating() + navigationDelegate?.webView(webView, didFailLoadWithError: error) + } + func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) { - //func webView(_ theWebView: WKWebView, didFailLoadWithError error: Error) { - // log fail message, stop spinner, update back/forward - print("webView:didFailLoadWithError - \(Int(error._code)): \(error.localizedDescription)") + print("webView:didFailNavigationWithError - \(Int(error._code)): \(error.localizedDescription)") backButton.isEnabled = webView.canGoBack forwardButton.isEnabled = webView.canGoForward spinner.stopAnimating()