Bug 1480095 - Add example custom error page to GeckoView Example r=esawin
authorJames Willcox <snorp@snorp.net>
Fri, 03 Aug 2018 14:53:49 -0500
changeset 487932 6bb8792d978ca0b0b84a0fd1218260392b897ed1
parent 487931 d49371d4563a373e0c701f385c59699f2f19dede
child 487933 3f1d6721cab0d1668a2441f7b0b7d018f1ac6475
push id9719
push userffxbld-merge
push dateFri, 24 Aug 2018 17:49:46 +0000
treeherdermozilla-beta@719ec98fba77 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersesawin
bugs1480095
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1480095 - Add example custom error page to GeckoView Example r=esawin MozReview-Commit-ID: HeYOkwNHYXq
mobile/android/geckoview_example/src/main/assets/error.html
mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview_example/src/main/assets/error.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>Boom!</title>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no">
+    <style>
+        body {
+            background-color: red;
+            color: white;
+            font-family: sans;
+        }
+
+        div.container {
+            width: 75%;
+            margin: auto;
+            text-align: center;
+        }
+    </style>
+</head>
+<body>
+    <div class="container">
+        <h1>Boom!</h1>
+        <p>Something bad happened...</p>
+        <p>$ERROR</p>
+    </div>
+</body>
+</html>
\ No newline at end of file
--- a/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java
+++ b/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java
@@ -33,16 +33,20 @@ import android.support.v7.widget.Toolbar
 import android.util.Log;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
 import android.view.WindowManager;
 import android.widget.ProgressBar;
 
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.util.LinkedList;
 import java.util.Locale;
 
 public class GeckoViewActivity extends AppCompatActivity {
     private static final String LOGTAG = "GeckoViewActivity";
     private static final String DEFAULT_URL = "https://mozilla.org";
     private static final String USE_MULTIPROCESS_EXTRA = "use_multiprocess";
     private static final String SEARCH_URI_BASE = "https://www.google.com/search?q=";
@@ -340,16 +344,58 @@ public class GeckoViewActivity extends A
         DownloadManager manager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
         DownloadManager.Request req = new DownloadManager.Request(uri);
         req.setMimeType(response.contentType);
         req.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename);
         req.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE);
         manager.enqueue(req);
     }
 
+    private String mErrorTemplate;
+    private String createErrorPage(final String error) {
+        if (mErrorTemplate == null) {
+            InputStream stream = null;
+            BufferedReader reader = null;
+            StringBuilder builder = new StringBuilder();
+            try {
+                stream = getResources().getAssets().open("error.html");
+                reader = new BufferedReader(new InputStreamReader(stream));
+
+                String line;
+                while ((line = reader.readLine()) != null) {
+                    builder.append(line);
+                    builder.append("\n");
+                }
+
+                mErrorTemplate = builder.toString();
+            } catch (IOException e) {
+                Log.d(LOGTAG, "Failed to open error page template", e);
+                return null;
+            } finally {
+                if (stream != null) {
+                    try {
+                        stream.close();
+                    } catch (IOException e) {
+                        Log.e(LOGTAG, "Failed to close error page template stream", e);
+                    }
+                }
+
+                if (reader != null) {
+                    try {
+                        reader.close();
+                    } catch (IOException e) {
+                        Log.e(LOGTAG, "Failed to close error page template reader", e);
+                    }
+                }
+            }
+        }
+
+        return mErrorTemplate.replace("$ERROR", error);
+    }
+
     private class ExampleContentDelegate implements GeckoSession.ContentDelegate {
         @Override
         public void onTitleChange(GeckoSession session, String title) {
             Log.i(LOGTAG, "Content title changed to " + title);
         }
 
         @Override
         public void onFullScreen(final GeckoSession session, final boolean fullScreen) {
@@ -586,21 +632,145 @@ public class GeckoViewActivity extends A
             intent.setData(Uri.parse(uri));
             intent.putExtra("session", newSession);
 
             startActivity(intent);
 
             return GeckoResult.fromValue(newSession);
         }
 
-        public void onLoadError(final GeckoSession session, final String uri,
-                                final int category, final int error) {
+        private String categoryToString(final int category) {
+            switch (category) {
+                case ERROR_CATEGORY_UNKNOWN:
+                    return "ERROR_CATEGORY_UNKNOWN";
+                case ERROR_CATEGORY_SECURITY:
+                    return "ERROR_CATEGORY_SECURITY";
+                case ERROR_CATEGORY_NETWORK:
+                    return "ERROR_CATEGORY_NETWORK";
+                case ERROR_CATEGORY_CONTENT:
+                    return "ERROR_CATEGORY_CONTENT";
+                case ERROR_CATEGORY_URI:
+                    return "ERROR_CATEGORY_URI";
+                case ERROR_CATEGORY_PROXY:
+                    return "ERROR_CATEGORY_PROXY";
+                case ERROR_CATEGORY_SAFEBROWSING:
+                    return "ERROR_CATEGORY_SAFEBROWSING";
+                default:
+                    return "UNKNOWN";
+            }
+        }
+
+        private String errorToString(final int error) {
+            switch (error) {
+                case ERROR_UNKNOWN:
+                    return "ERROR_UNKNOWN";
+                case ERROR_SECURITY_SSL:
+                    return "ERROR_SECURITY_SSL";
+                case ERROR_SECURITY_BAD_CERT:
+                    return "ERROR_SECURITY_BAD_CERT";
+                case ERROR_NET_RESET:
+                    return "ERROR_NET_RESET";
+                case ERROR_NET_INTERRUPT:
+                    return "ERROR_NET_INTERRUPT";
+                case ERROR_NET_TIMEOUT:
+                    return "ERROR_NET_TIMEOUT";
+                case ERROR_CONNECTION_REFUSED:
+                    return "ERROR_CONNECTION_REFUSED";
+                case ERROR_UNKNOWN_PROTOCOL:
+                    return "ERROR_UNKNOWN_PROTOCOL";
+                case ERROR_UNKNOWN_HOST:
+                    return "ERROR_UNKNOWN_HOST";
+                case ERROR_UNKNOWN_SOCKET_TYPE:
+                    return "ERROR_UNKNOWN_SOCKET_TYPE";
+                case ERROR_UNKNOWN_PROXY_HOST:
+                    return "ERROR_UNKNOWN_PROXY_HOST";
+                case ERROR_MALFORMED_URI:
+                    return "ERROR_MALFORMED_URI";
+                case ERROR_REDIRECT_LOOP:
+                    return "ERROR_REDIRECT_LOOP";
+                case ERROR_SAFEBROWSING_PHISHING_URI:
+                    return "ERROR_SAFEBROWSING_PHISHING_URI";
+                case ERROR_SAFEBROWSING_MALWARE_URI:
+                    return "ERROR_SAFEBROWSING_MALWARE_URI";
+                case ERROR_SAFEBROWSING_UNWANTED_URI:
+                    return "ERROR_SAFEBROWSING_UNWANTED_URI";
+                case ERROR_SAFEBROWSING_HARMFUL_URI:
+                    return "ERROR_SAFEBROWSING_HARMFUL_URI";
+                case ERROR_CONTENT_CRASHED:
+                    return "ERROR_CONTENT_CRASHED";
+                case ERROR_OFFLINE:
+                    return "ERROR_OFFLINE";
+                case ERROR_PORT_BLOCKED:
+                    return "ERROR_PORT_BLOCKED";
+                case ERROR_PROXY_CONNECTION_REFUSED:
+                    return "ERROR_PROXY_CONNECTION_REFUSED";
+                case ERROR_FILE_NOT_FOUND:
+                    return "ERROR_FILE_NOT_FOUND";
+                case ERROR_FILE_ACCESS_DENIED:
+                    return "ERROR_FILE_ACCESS_DENIED";
+                case ERROR_INVALID_CONTENT_ENCODING:
+                    return "ERROR_INVALID_CONTENT_ENCODING";
+                case ERROR_UNSAFE_CONTENT_TYPE:
+                    return "ERROR_UNSAFE_CONTENT_TYPE";
+                case ERROR_CORRUPTED_CONTENT:
+                    return "ERROR_CORRUPTED_CONTENT";
+                default:
+                    return "UNKNOWN";
+            }
+        }
+
+        private String createErrorPage(final int category, final int error) {
+            if (mErrorTemplate == null) {
+                InputStream stream = null;
+                BufferedReader reader = null;
+                StringBuilder builder = new StringBuilder();
+                try {
+                    stream = getResources().getAssets().open("error.html");
+                    reader = new BufferedReader(new InputStreamReader(stream));
+
+                    String line;
+                    while ((line = reader.readLine()) != null) {
+                        builder.append(line);
+                        builder.append("\n");
+                    }
+
+                    mErrorTemplate = builder.toString();
+                } catch (IOException e) {
+                    Log.d(LOGTAG, "Failed to open error page template", e);
+                    return null;
+                } finally {
+                    if (stream != null) {
+                        try {
+                            stream.close();
+                        } catch (IOException e) {
+                            Log.e(LOGTAG, "Failed to close error page template stream", e);
+                        }
+                    }
+
+                    if (reader != null) {
+                        try {
+                            reader.close();
+                        } catch (IOException e) {
+                            Log.e(LOGTAG, "Failed to close error page template reader", e);
+                        }
+                    }
+                }
+            }
+
+            return GeckoViewActivity.this.createErrorPage(categoryToString(category) + " : " + errorToString(error));
+        }
+
+        @Override
+        public GeckoResult<String> onLoadError(final GeckoSession session, final String uri,
+                                               final int category, final int error) {
             Log.d(LOGTAG, "onLoadError=" + uri +
                   " error category=" + category +
                   " error=" + error);
+
+            return GeckoResult.fromValue("data:text/html," + createErrorPage(category, error));
         }
     }
 
     private class ExampleTrackingProtectionDelegate implements GeckoSession.TrackingProtectionDelegate {
         private int mBlockedAds = 0;
         private int mBlockedAnalytics = 0;
         private int mBlockedSocial = 0;
         private int mBlockedContent = 0;