]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/blobdiff - app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java
Fix “Waiting for Orbot”. https://redmine.stoutner.com/issues/307
[PrivacyBrowserAndroid.git] / app / src / main / java / com / stoutner / privacybrowser / activities / MainWebViewActivity.java
index 6e296677f8c01102e66d5050766679e29d5437b8..191e56aa2cee5cca7a6cb45a19533e9807df55ae 100644 (file)
@@ -149,17 +149,17 @@ import java.util.Set;
 
 // AppCompatActivity from android.support.v7.app.AppCompatActivity must be used to have access to the SupportActionBar until the minimum API is >= 21.
 public class MainWebViewActivity extends AppCompatActivity implements CreateBookmarkDialog.CreateBookmarkListener, CreateBookmarkFolderDialog.CreateBookmarkFolderListener,
-        CreateHomeScreenShortcutDialog.CreateHomeScreenSchortcutListener, DownloadFileDialog.DownloadFileListener, DownloadImageDialog.DownloadImageListener,
+        CreateHomeScreenShortcutDialog.CreateHomeScreenShortcutListener, DownloadFileDialog.DownloadFileListener, DownloadImageDialog.DownloadImageListener,
         DownloadLocationPermissionDialog.DownloadLocationPermissionDialogListener, EditBookmarkDialog.EditBookmarkListener, EditBookmarkFolderDialog.EditBookmarkFolderListener,
         HttpAuthenticationDialog.HttpAuthenticationListener, NavigationView.OnNavigationItemSelectedListener, PinnedSslCertificateMismatchDialog.PinnedSslCertificateMismatchListener,
         SslCertificateErrorDialog.SslCertificateErrorListener, UrlHistoryDialog.UrlHistoryListener {
 
-    // `darkTheme` is public static so it can be accessed from `AboutActivity`, `GuideActivity`, `AddDomainDialog`, `SettingsActivity`, `DomainsActivity`, `DomainsListFragment`, `BookmarksActivity`,
-    // `BookmarksDatabaseViewActivity`, `CreateBookmarkDialog`, `CreateBookmarkFolderDialog`, `DownloadFileDialog`, `DownloadImageDialog`, `EditBookmarkDialog`, `EditBookmarkFolderDialog`,
-    // `EditBookmarkDatabaseViewDialog`, `HttpAuthenticationDialog`, `MoveToFolderDialog`, `SslCertificateErrorDialog`, `UrlHistoryDialog`, `ViewSslCertificateDialog`, `CreateHomeScreenShortcutDialog`,
-    //  and `OrbotProxyHelper`. It is also used in `onCreate()`, `applyAppSettings()`, `applyDomainSettings()`, and `updatePrivacyIcons()`.
+    // `darkTheme` is public static so it can be accessed from everywhere.
     public static boolean darkTheme;
 
+    // `allowScreenshots` is public static so it can be accessed from everywhere.  It is also used in `onCreate()`.
+    public static boolean allowScreenshots;
+
     // `favoriteIconBitmap` is public static so it can be accessed from `CreateHomeScreenShortcutDialog`, `BookmarksActivity`, `BookmarksDatabaseViewActivity`, `CreateBookmarkDialog`,
     // `CreateBookmarkFolderDialog`, `EditBookmarkDialog`, `EditBookmarkFolderDialog`, `EditBookmarkDatabaseViewDialog`, and `ViewSslCertificateDialog`.  It is also used in `onCreate()`,
     // `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onCreateHomeScreenShortcutCreate()`, `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, and `applyDomainSettings()`.
@@ -173,7 +173,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     // and `ViewSslCertificateDialog`.  It is also used in `onCreate()`.
     public static SslCertificate sslCertificate;
 
-    // `orbotStatus` is public static so it can be accessed from `OrbotProxyHelper`.  It is also used in `onCreate()`.
+    // `orbotStatus` is public static so it can be accessed from `OrbotProxyHelper`.  It is also used in `onCreate()` and `onResume()`.
     public static String orbotStatus;
 
     // `webViewTitle` is public static so it can be accessed from `CreateBookmarkDialog` and `CreateHomeScreenShortcutDialog`.  It is also used in `onCreate()`.
@@ -194,8 +194,51 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     // The block list versions are public static so they can be accessed from `AboutTabFragment`.  They are also used in `onCreate()`.
     public static String easyListVersion;
     public static String easyPrivacyVersion;
-    public static String fanboyAnnoyanceVersion;
-    public static String fanboySocialVersion;
+    public static String fanboysAnnoyanceVersion;
+    public static String fanboysSocialVersion;
+
+    // The request items are public static so they can be accessed by `BlockListHelper`, `RequestsArrayAdapter`, and `ViewRequestsDialog`.  They are also used in `onCreate()`.
+    public static List<String[]> resourceRequests;
+    public static String[] whiteListResultStringArray;
+    public final static int REQUEST_DISPOSITION = 0;
+    public final static int REQUEST_URL = 1;
+    public final static int REQUEST_BLOCKLIST = 2;
+    public final static int REQUEST_SUBLIST = 3;
+    public final static int REQUEST_BLOCKLIST_ENTRIES = 4;
+    public final static int REQUEST_BLOCKLIST_ORIGINAL_ENTRY = 5;
+
+    public final static int REQUEST_DEFAULT = 0;
+    public final static int REQUEST_ALLOWED = 1;
+    public final static int REQUEST_THIRD_PARTY = 2;
+    public final static int REQUEST_BLOCKED = 3;
+
+    public final static int MAIN_WHITELIST = 1;
+    public final static int FINAL_WHITELIST = 2;
+    public final static int DOMAIN_WHITELIST = 3;
+    public final static int DOMAIN_INITIAL_WHITELIST = 4;
+    public final static int DOMAIN_FINAL_WHITELIST = 5;
+    public final static int THIRD_PARTY_WHITELIST = 6;
+    public final static int THIRD_PARTY_DOMAIN_WHITELIST = 7;
+    public final static int THIRD_PARTY_DOMAIN_INITIAL_WHITELIST = 8;
+
+    public final static int MAIN_BLACKLIST = 9;
+    public final static int INITIAL_BLACKLIST = 10;
+    public final static int FINAL_BLACKLIST = 11;
+    public final static int DOMAIN_BLACKLIST = 12;
+    public final static int DOMAIN_INITIAL_BLACKLIST = 13;
+    public final static int DOMAIN_FINAL_BLACKLIST = 14;
+    public final static int DOMAIN_REGULAR_EXPRESSION_BLACKLIST = 15;
+    public final static int THIRD_PARTY_BLACKLIST = 16;
+    public final static int THIRD_PARTY_INITIAL_BLACKLIST = 17;
+    public final static int THIRD_PARTY_DOMAIN_BLACKLIST = 18;
+    public final static int THIRD_PARTY_DOMAIN_INITIAL_BLACKLIST = 19;
+    public final static int THIRD_PARTY_REGULAR_EXPRESSION_BLACKLIST = 20;
+    public final static int THIRD_PARTY_DOMAIN_REGULAR_EXPRESSION_BLACKLIST = 21;
+    public final static int REGULAR_EXPRESSION_BLACKLIST = 22;
+
+    // `blockAllThirdPartyRequests` is public static so it can be accessed from `RequestsActivity`.
+    // It is also used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `applyAppSettings()`
+    public static boolean blockAllThirdPartyRequests;
 
     // `currentBookmarksFolder` is public static so it can be accessed from `BookmarksActivity`.  It is also used in `onCreate()`, `onBackPressed()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`,
     // `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, and `loadBookmarksFolder()`.
@@ -245,7 +288,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     // `fullScreenVideoFrameLayout` is used in `onCreate()` and `onConfigurationChanged()`.
     private FrameLayout fullScreenVideoFrameLayout;
 
-    // `swipeRefreshLayout` is used in `onCreate()`, `onPrepareOptionsMenu`, and `onRestart()`.
+    // `swipeRefreshLayout` is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `onRestart()`.
     private SwipeRefreshLayout swipeRefreshLayout;
 
     // `urlAppBarRelativeLayout` is used in `onCreate()` and `applyDomainSettings()`.
@@ -272,15 +315,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     // `domStorageEnabled` is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `applyDomainSettings()`.
     private boolean domStorageEnabled;
 
-    // `saveFormDataEnabled` is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `applyDomainSettings()`.
+    // `saveFormDataEnabled` is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `applyDomainSettings()`.  It can be removed once the minimum API >= 26.
     private boolean saveFormDataEnabled;
 
     // `nightMode` is used in `onCreate()` and  `applyDomainSettings()`.
     private boolean nightMode;
 
-    // `swipeToRefreshEnabled` is used in `onPrepareOptionsMenu()` and `applyAppSettings()`.
-    private boolean swipeToRefreshEnabled;
-
     // `displayWebpageImagesBoolean` is used in `applyAppSettings()` and `applyDomainSettings()`.
     private boolean displayWebpageImagesBoolean;
 
@@ -290,7 +330,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     // `searchURL` is used in `loadURLFromTextBox()` and `applyAppSettings()`.
     private String searchURL;
 
-    // The block list variables are used in `onCreate()` and `applyAppSettings()`.
+    // The block list variables are used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, and `applyAppSettings()`.
     private boolean easyListEnabled;
     private boolean easyPrivacyEnabled;
     private boolean fanboysAnnoyanceListEnabled;
@@ -329,7 +369,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     // `ignorePinnedSslCertificateForDomain` is used in `onCreate()`, `onSslMismatchProceed()`, and `applyDomainSettings()`.
     private boolean ignorePinnedSslCertificate;
 
-    // `waitingForOrbot` is used in `onCreate()` and `applyAppSettings()`.
+    // `waitingForOrbot` is used in `onCreate()`, `onResume()`, and `applyAppSettings()`.
     private boolean waitingForOrbot;
 
     // `domainSettingsApplied` is used in `prepareOptionsMenu()`, `applyDomainSettings()`, and `setDisplayWebpageImages()`.
@@ -432,11 +472,17 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     // Remove Android Studio's warning about deprecations.  We have to use the deprecated `getColor()` until API >= 23.
     @SuppressWarnings("deprecation")
     protected void onCreate(Bundle savedInstanceState) {
-        // Get a handle for `sharedPreferences`.  `this` references the current context.
+        // Get a handle for the shared preferences.
         SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
 
-        // Get the theme preference.
+        // Get the theme and screenshot preferences.
         darkTheme = sharedPreferences.getBoolean("dark_theme", false);
+        allowScreenshots = sharedPreferences.getBoolean("allow_screenshots", false);
+
+        // Disable screenshots if not allowed.
+        if (!allowScreenshots) {
+            getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
+        }
 
         // Set the activity theme.
         if (darkTheme) {
@@ -748,6 +794,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         final MenuItem navigationBackMenuItem = navigationMenu.getItem(1);
         final MenuItem navigationForwardMenuItem = navigationMenu.getItem(2);
         final MenuItem navigationHistoryMenuItem = navigationMenu.getItem(3);
+        final MenuItem navigationRequestsMenuItem = navigationMenu.getItem(4);
 
         // Initialize the bookmarks database helper.  `this` specifies the context.  The two `nulls` do not specify the database name or a `CursorFactory`.
         // The `0` specifies a database version, but that is ignored and set instead using a constant in `BookmarksDatabaseHelper`.
@@ -810,7 +857,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             return true;
         });
 
-        // The `DrawerListener` allows us to update the Navigation Menu.
+        // The `DrawerListener` is used to update the Navigation Menu.
         drawerLayout.addDrawerListener(new DrawerLayout.DrawerListener() {
             @Override
             public void onDrawerSlide(@NonNull View drawerView, float slideOffset) {
@@ -827,10 +874,27 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             @Override
             public void onDrawerStateChanged(int newState) {
                 if ((newState == DrawerLayout.STATE_SETTLING) || (newState == DrawerLayout.STATE_DRAGGING)) {  // The drawer is opening or closing.
-                    // Update the `Back`, `Forward`, and `History` menu items.
+                    // Initialize a the blocked requests counter.
+                    int blockedRequests = 0;
+
+                    // Count the number of blocked requests.
+                    for (int i = 0; i < resourceRequests.size(); i++) {
+                        // Add the blocked requests.
+                        if (Integer.valueOf(resourceRequests.get(i)[REQUEST_DISPOSITION]) == REQUEST_BLOCKED) {
+                            blockedRequests++;
+                        }
+
+                        // Add the third-party requests if they are blocked.
+                        if (blockAllThirdPartyRequests && (Integer.valueOf(resourceRequests.get(i)[REQUEST_DISPOSITION]) == REQUEST_THIRD_PARTY)) {
+                            blockedRequests++;
+                        }
+                    }
+
+                    // Update the back, forward, history, and requests menu items.
                     navigationBackMenuItem.setEnabled(mainWebView.canGoBack());
                     navigationForwardMenuItem.setEnabled(mainWebView.canGoForward());
                     navigationHistoryMenuItem.setEnabled((mainWebView.canGoBack() || mainWebView.canGoForward()));
+                    navigationRequestsMenuItem.setTitle(getResources().getString(R.string.requests) + " - " + blockedRequests);
 
                     // Hide the keyboard (if displayed) so we can see the navigation menu.  `0` indicates no additional flags.
                     inputMethodManager.hideSoftInputFromWindow(mainWebView.getWindowToken(), 0);
@@ -894,7 +958,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                         mainWebView.setVisibility(View.VISIBLE);
                     }
 
-                    //Stop the `SwipeToRefresh` indicator if it is running
+                    //Stop the swipe to refresh indicator if it is running
                     swipeRefreshLayout.setRefreshing(false);
                 }
             }
@@ -943,6 +1007,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 // Set `rootCoordinatorLayout` to fill the entire screen.
                 rootCoordinatorLayout.setFitsSystemWindows(false);
 
+                // Disable the sliding drawers.
+                drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
+
                 // Add `view` to `fullScreenVideoFrameLayout` and display it on the screen.
                 fullScreenVideoFrameLayout.addView(view);
                 fullScreenVideoFrameLayout.setVisibility(View.VISIBLE);
@@ -955,6 +1022,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 fullScreenVideoFrameLayout.removeAllViews();
                 fullScreenVideoFrameLayout.setVisibility(View.GONE);
 
+                // Enable the sliding drawers.
+                drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
+
                 // Add the translucent status flag.  This also resets `drawerLayout's` `View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN`.
                 getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
 
@@ -1070,7 +1140,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         firstPartyCookiesEnabled = false;
         thirdPartyCookiesEnabled = false;
         domStorageEnabled = false;
-        saveFormDataEnabled = false;
+        saveFormDataEnabled = false;  // Form data can be removed once the minimum API >= 26.
         nightMode = false;
 
         // Initialize the WebView title.
@@ -1097,17 +1167,20 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Instantiate the block list helper.
         BlockListHelper blockListHelper = new BlockListHelper();
 
+        // Initialize the list of resource requests.
+        resourceRequests = new ArrayList<>();
+
         // Parse the block lists.
         final ArrayList<List<String[]>> easyList = blockListHelper.parseBlockList(getAssets(), "blocklists/easylist.txt");
         final ArrayList<List<String[]>> easyPrivacy = blockListHelper.parseBlockList(getAssets(), "blocklists/easyprivacy.txt");
-        final ArrayList<List<String[]>> fanboyAnnoyance = blockListHelper.parseBlockList(getAssets(), "blocklists/fanboy-annoyance.txt");
-        final ArrayList<List<String[]>> fanboySocial = blockListHelper.parseBlockList(getAssets(), "blocklists/fanboy-social.txt");
+        final ArrayList<List<String[]>> fanboysAnnoyanceList = blockListHelper.parseBlockList(getAssets(), "blocklists/fanboy-annoyance.txt");
+        final ArrayList<List<String[]>> fanboysSocialList = blockListHelper.parseBlockList(getAssets(), "blocklists/fanboy-social.txt");
 
         // Store the list versions.
         easyListVersion = easyList.get(0).get(0)[0];
         easyPrivacyVersion = easyPrivacy.get(0).get(0)[0];
-        fanboyAnnoyanceVersion = fanboyAnnoyance.get(0).get(0)[0];
-        fanboySocialVersion = fanboySocial.get(0).get(0)[0];
+        fanboysAnnoyanceVersion = fanboysAnnoyanceList.get(0).get(0)[0];
+        fanboysSocialVersion = fanboysSocialList.get(0).get(0)[0];
 
         mainWebView.setWebViewClient(new WebViewClient() {
             // `shouldOverrideUrlLoading` makes this `WebView` the default handler for URLs inside the app, so that links are not kicked out to other apps.
@@ -1116,10 +1189,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             @Override
             public boolean shouldOverrideUrlLoading(WebView view, String url) {
                 if (url.startsWith("http")) {  // Load the URL in Privacy Browser.
-                    // Apply the domain settings for the new URL.
+                    // Reset the formatted URL string so the page will load correctly if blocking of third-party requests is enabled.
+                    formattedUrlString = "";
+
+                    // Apply the domain settings for the new URL.  `applyDomainSettings` doesn't do anything if the domain has not changed.
                     applyDomainSettings(url, true, false);
 
-                    // Returning false causes the current `WebView` to handle the URL and prevents it from adding redirects to the history list.
+                    // Returning false causes the current WebView to handle the URL and prevents it from adding redirects to the history list.
                     return false;
                 } else if (url.startsWith("mailto:")) {  // Load the email address in an external email program.
                     // Use `ACTION_SENDTO` instead of `ACTION_SEND` so that only email programs are launched.
@@ -1176,16 +1252,66 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 }
             }
 
-            // Check requests against the block lists.  The deprecated `shouldInterceptRequest` must be used until minimum API >= 21.
+            // Check requests against the block lists.  The deprecated `shouldInterceptRequest()` must be used until minimum API >= 21.
             @SuppressWarnings("deprecation")
             @Override
             public WebResourceResponse shouldInterceptRequest(WebView view, String url){
                 // Create an empty web resource response to be used if the resource request is blocked.
                 WebResourceResponse emptyWebResourceResponse = new WebResourceResponse("text/plain", "utf8", new ByteArrayInputStream("".getBytes()));
 
+                // Reset `whiteListResultStringArray`.
+                whiteListResultStringArray = null;
+
+                // Initialize the third party request tracker.
+                boolean isThirdPartyRequest = false;
+
+                // Initialize the current domain string.
+                String currentDomain = "";
+
+                // Nobody is happy when comparing null strings.
+                if (!(formattedUrlString == null) && !(url == null)) {
+                    // Get the domain strings to URIs.
+                    Uri currentDomainUri = Uri.parse(formattedUrlString);
+                    Uri requestDomainUri = Uri.parse(url);
+
+                    // Get the domain host names.
+                    String currentBaseDomain = currentDomainUri.getHost();
+                    String requestBaseDomain = requestDomainUri.getHost();
+
+                    // Update the current domain variable.
+                    currentDomain = currentBaseDomain;
+
+                    // Only compare the current base domain and the request base domain if neither is null.
+                    if (!(currentBaseDomain == null) && !(requestBaseDomain == null)) {
+                        // Determine the current base domain.
+                        while (currentBaseDomain.indexOf(".", currentBaseDomain.indexOf(".") + 1) > 0) {  // There is at least one subdomain.
+                            // Remove the first subdomain.
+                            currentBaseDomain = currentBaseDomain.substring(currentBaseDomain.indexOf(".") + 1);
+                        }
+
+                        // Determine the request base domain.
+                        while (requestBaseDomain.indexOf(".", requestBaseDomain.indexOf(".") + 1) > 0) {  // There is at least one subdomain.
+                            // Remove the first subdomain.
+                            requestBaseDomain = requestBaseDomain.substring(requestBaseDomain.indexOf(".") + 1);
+                        }
+
+                        // Update the third party request tracker.
+                        isThirdPartyRequest = !currentBaseDomain.equals(requestBaseDomain);
+                    }
+                }
+
+                // Block third-party requests if enabled.
+                if (isThirdPartyRequest && blockAllThirdPartyRequests) {
+                    // Add the request to the log.
+                    resourceRequests.add(new String[]{String.valueOf(REQUEST_THIRD_PARTY), url});
+
+                    // Return an empty web resource response.
+                    return emptyWebResourceResponse;
+                }
+
                 // Check EasyList if it is enabled.
                 if (easyListEnabled) {
-                    if (blockListHelper.isBlocked(formattedUrlString, url, easyList)) {
+                    if (blockListHelper.isBlocked(currentDomain, url, isThirdPartyRequest, easyList)) {
                         // The resource request was blocked.  Return an empty web resource response.
                         return emptyWebResourceResponse;
                     }
@@ -1193,7 +1319,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                 // Check EasyPrivacy if it is enabled.
                 if (easyPrivacyEnabled) {
-                    if (blockListHelper.isBlocked(formattedUrlString, url, easyPrivacy)) {
+                    if (blockListHelper.isBlocked(currentDomain, url, isThirdPartyRequest, easyPrivacy)) {
                         // The resource request was blocked.  Return an empty web resource response.
                         return emptyWebResourceResponse;
                     }
@@ -1201,17 +1327,24 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                 // Check Fanboy’s Annoyance List if it is enabled.
                 if (fanboysAnnoyanceListEnabled) {
-                    if (blockListHelper.isBlocked(formattedUrlString, url, fanboyAnnoyance)) {
+                    if (blockListHelper.isBlocked(currentDomain, url, isThirdPartyRequest, fanboysAnnoyanceList)) {
                         // The resource request was blocked.  Return an empty web resource response.
                         return emptyWebResourceResponse;
                     }
                 } else if (fanboysSocialBlockingListEnabled){  // Only check Fanboy’s Social Blocking List if Fanboy’s Annoyance List is disabled.
-                    if (blockListHelper.isBlocked(formattedUrlString, url, fanboySocial)) {
+                    if (blockListHelper.isBlocked(currentDomain, url, isThirdPartyRequest, fanboysSocialList)) {
                         // The resource request was blocked.  Return an empty web resource response.
                         return emptyWebResourceResponse;
                     }
                 }
 
+                // Add the request to the log because it hasn't been processed by any of the previous checks.
+                if (whiteListResultStringArray != null ) {  // The request was processed by a whitelist.
+                    resourceRequests.add(whiteListResultStringArray);
+                } else {  // The request didn't match any blocklist entry.  Log it as a defult request.
+                    resourceRequests.add(new String[]{String.valueOf(REQUEST_DEFAULT), url});
+                }
+
                 // The resource request has not been blocked.  `return null` loads the requested resource.
                 return null;
             }
@@ -1229,7 +1362,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
             // Update the URL in urlTextBox when the page starts to load.
             @Override
-            public void onPageStarted(WebView view, String url, Bitmap favicon) {// If night mode is enabled, hide `mainWebView` until after the night mode CSS is applied.
+            public void onPageStarted(WebView view, String url, Bitmap favicon) {
+                // Reset the list of resource requests.
+                resourceRequests.clear();
+
+                // If night mode is enabled, hide `mainWebView` until after the night mode CSS is applied.
                 if (nightMode) {
                     mainWebView.setVisibility(View.INVISIBLE);
                 }
@@ -1237,7 +1374,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 // Hide the keyboard.  `0` indicates no additional flags.
                 inputMethodManager.hideSoftInputFromWindow(mainWebView.getWindowToken(), 0);
 
-                // Check to see if we are waiting on Orbot.
+                // Check to see if Privacy Browser is waiting on Orbot.
                 if (!waitingForOrbot) {  // We are not waiting on Orbot, so we need to process the URL.
                     // We need to update `formattedUrlString` at the beginning of the load, so that if the user toggles JavaScript during the load the new website is reloaded.
                     formattedUrlString = url;
@@ -1250,6 +1387,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                     // Apply any custom domain settings if the URL was loaded by navigating history.
                     if (navigatingHistory) {
+                        // Reset `navigatingHistory`.
+                        navigatingHistory = false;
+
+                        // Apply the domain settings.
                         applyDomainSettings(url, true, false);
                     }
 
@@ -1435,12 +1576,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
         // Check to see if the intent contains a new URL.
         if (intent.getData() != null) {
-            // Get the intent data and convert it to a string.
+            // Get the intent data.
             final Uri intentUriData = intent.getData();
-            formattedUrlString = intentUriData.toString();
 
             // Load the website.
-            loadUrl(formattedUrlString);
+            loadUrl(intentUriData.toString());
 
             // Close the navigation drawer if it is open.
             if (drawerLayout.isDrawerVisible(GravityCompat.START)) {
@@ -1543,6 +1683,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             // The AdView is destroyed and recreated, which changes the ID, every time it is reloaded to handle possible rotations.
             AdHelper.resumeAd(findViewById(R.id.adview));
         }
+
+        // Display a message to the user if waiting for Orbot.
+        if (waitingForOrbot && !orbotStatus.equals("ON")) {
+            // Load a waiting page.  `null` specifies no encoding, which defaults to ASCII.
+            mainWebView.loadData(waitingForOrbotHTMLString, "text/html", null);
+        }
     }
 
     @Override
@@ -1577,23 +1723,33 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         MenuItem toggleFirstPartyCookiesMenuItem = menu.findItem(R.id.toggle_first_party_cookies);
         MenuItem toggleThirdPartyCookiesMenuItem = menu.findItem(R.id.toggle_third_party_cookies);
         MenuItem toggleDomStorageMenuItem = menu.findItem(R.id.toggle_dom_storage);
-        MenuItem toggleSaveFormDataMenuItem = menu.findItem(R.id.toggle_save_form_data);
+        MenuItem toggleSaveFormDataMenuItem = menu.findItem(R.id.toggle_save_form_data);  // Form data can be removed once the minimum API >= 26.
+        MenuItem clearFormDataMenuItem = menu.findItem(R.id.clear_form_data);  // Form data can be removed once the minimum API >= 26.
+        MenuItem refreshMenuItem = menu.findItem(R.id.refresh);
+        MenuItem adConsentMenuItem = menu.findItem(R.id.ad_consent);
 
-        // Only display third-party cookies if SDK >= 21
+        // Only display third-party cookies if API >= 21
         toggleThirdPartyCookiesMenuItem.setVisible(Build.VERSION.SDK_INT >= 21);
 
+        // Only display the form data menu items if the API < 26.
+        toggleSaveFormDataMenuItem.setVisible(Build.VERSION.SDK_INT < 26);
+        clearFormDataMenuItem.setVisible(Build.VERSION.SDK_INT < 26);
+
+        // Only show Ad Consent if this is the free flavor.
+        adConsentMenuItem.setVisible(BuildConfig.FLAVOR.contentEquals("free"));
+
         // Get the shared preference values.  `this` references the current context.
         SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
 
-        // Set the status of the additional app bar icons.  The default is `false`.
+        // Set the status of the additional app bar icons.  Setting the refresh menu item to `SHOW_AS_ACTION_ALWAYS` makes it appear even on small devices like phones.
         if (sharedPreferences.getBoolean("display_additional_app_bar_icons", false)) {
             toggleFirstPartyCookiesMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
             toggleDomStorageMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
-            toggleSaveFormDataMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+            refreshMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
         } else { //Do not display the additional icons.
             toggleFirstPartyCookiesMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
             toggleDomStorageMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
-            toggleSaveFormDataMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+            refreshMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
         }
 
         return true;
@@ -1606,15 +1762,19 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         MenuItem toggleFirstPartyCookiesMenuItem = menu.findItem(R.id.toggle_first_party_cookies);
         MenuItem toggleThirdPartyCookiesMenuItem = menu.findItem(R.id.toggle_third_party_cookies);
         MenuItem toggleDomStorageMenuItem = menu.findItem(R.id.toggle_dom_storage);
-        MenuItem toggleSaveFormDataMenuItem = menu.findItem(R.id.toggle_save_form_data);
+        MenuItem toggleSaveFormDataMenuItem = menu.findItem(R.id.toggle_save_form_data);  // Form data can be removed once the minimum API >= 26.
         MenuItem clearDataMenuItem = menu.findItem(R.id.clear_data);
         MenuItem clearCookiesMenuItem = menu.findItem(R.id.clear_cookies);
         MenuItem clearDOMStorageMenuItem = menu.findItem(R.id.clear_dom_storage);
-        MenuItem clearFormDataMenuItem = menu.findItem(R.id.clear_form_data);
+        MenuItem clearFormDataMenuItem = menu.findItem(R.id.clear_form_data);  // Form data can be removed once the minimum API >= 26.
+        MenuItem easyListMenuItem = menu.findItem(R.id.easylist);
+        MenuItem easyPrivacyMenuItem = menu.findItem(R.id.easyprivacy);
+        MenuItem fanboysAnnoyanceListMenuItem = menu.findItem(R.id.fanboys_annoyance_list);
+        MenuItem fanboysSocialBlockingListMenuItem = menu.findItem(R.id.fanboys_social_blocking_list);
+        MenuItem blockAllThirdParyRequestsMenuItem = menu.findItem(R.id.block_all_third_party_requests);
         MenuItem fontSizeMenuItem = menu.findItem(R.id.font_size);
+        MenuItem swipeToRefreshMenuItem = menu.findItem(R.id.swipe_to_refresh);
         MenuItem displayImagesMenuItem = menu.findItem(R.id.display_images);
-        MenuItem refreshMenuItem = menu.findItem(R.id.refresh);
-        MenuItem adConsentMenuItem = menu.findItem(R.id.ad_consent);
 
         // Set the text for the domain menu item.
         if (domainSettingsApplied) {
@@ -1627,42 +1787,53 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         toggleFirstPartyCookiesMenuItem.setChecked(firstPartyCookiesEnabled);
         toggleThirdPartyCookiesMenuItem.setChecked(thirdPartyCookiesEnabled);
         toggleDomStorageMenuItem.setChecked(domStorageEnabled);
-        toggleSaveFormDataMenuItem.setChecked(saveFormDataEnabled);
+        toggleSaveFormDataMenuItem.setChecked(saveFormDataEnabled);  // Form data can be removed once the minimum API >= 26.
+        easyListMenuItem.setChecked(easyListEnabled);
+        easyPrivacyMenuItem.setChecked(easyPrivacyEnabled);
+        fanboysAnnoyanceListMenuItem.setChecked(fanboysAnnoyanceListEnabled);
+        fanboysSocialBlockingListMenuItem.setChecked(fanboysSocialBlockingListEnabled);
+        blockAllThirdParyRequestsMenuItem.setChecked(blockAllThirdPartyRequests);
+        swipeToRefreshMenuItem.setChecked(swipeRefreshLayout.isEnabled());
         displayImagesMenuItem.setChecked(mainWebView.getSettings().getLoadsImagesAutomatically());
 
         // Enable third-party cookies if first-party cookies are enabled.
         toggleThirdPartyCookiesMenuItem.setEnabled(firstPartyCookiesEnabled);
 
-        // Enable `DOM Storage` if JavaScript is enabled.
+        // Enable DOM Storage if JavaScript is enabled.
         toggleDomStorageMenuItem.setEnabled(javaScriptEnabled);
 
-        // Enable `Clear Cookies` if there are any.
+        // Enable Clear Cookies if there are any.
         clearCookiesMenuItem.setEnabled(cookieManager.hasCookies());
 
-        // Get a count of the number of files in the `Local Storage` directory.
+        // Get a count of the number of files in the Local Storage directory.
         File localStorageDirectory = new File (privateDataDirectoryString + "/app_webview/Local Storage/");
         int localStorageDirectoryNumberOfFiles = 0;
         if (localStorageDirectory.exists()) {
             localStorageDirectoryNumberOfFiles = localStorageDirectory.list().length;
         }
 
-        // Get a count of the number of files in the `IndexedDB` directory.
+        // Get a count of the number of files in the IndexedDB directory.
         File indexedDBDirectory = new File (privateDataDirectoryString + "/app_webview/IndexedDB");
         int indexedDBDirectoryNumberOfFiles = 0;
         if (indexedDBDirectory.exists()) {
             indexedDBDirectoryNumberOfFiles = indexedDBDirectory.list().length;
         }
 
-        // Enable `Clear DOM Storage` if there is any.
+        // Enable Clear DOM Storage if there is any.
         clearDOMStorageMenuItem.setEnabled(localStorageDirectoryNumberOfFiles > 0 || indexedDBDirectoryNumberOfFiles > 0);
 
-        // Enable `Clear Form Data` is there is any.
-        WebViewDatabase mainWebViewDatabase = WebViewDatabase.getInstance(this);
-        clearFormDataMenuItem.setEnabled(mainWebViewDatabase.hasFormData());
+        // Enable Clear Form Data is there is any.  This can be removed once the minimum API >= 26.
+        if (Build.VERSION.SDK_INT < 26) {
+            WebViewDatabase mainWebViewDatabase = WebViewDatabase.getInstance(this);
+            clearFormDataMenuItem.setEnabled(mainWebViewDatabase.hasFormData());
+        }
 
-        // Enable `Clear Data` if any of the submenu items are enabled.
+        // Enable Clear Data if any of the submenu items are enabled.
         clearDataMenuItem.setEnabled(clearCookiesMenuItem.isEnabled() || clearDOMStorageMenuItem.isEnabled() || clearFormDataMenuItem.isEnabled());
 
+        // Disable Fanboy's Social Blocking List if Fanboy's Annoyance List is checked.
+        fanboysSocialBlockingListMenuItem.setEnabled(!fanboysAnnoyanceListEnabled);
+
         // Initialize font size variables.
         int fontSize = mainWebView.getSettings().getTextZoom();
         String fontSizeTitle;
@@ -1720,12 +1891,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         fontSizeMenuItem.setTitle(fontSizeTitle);
         selectedFontSizeMenuItem.setChecked(true);
 
-        // Only show Refresh if `swipeToRefresh` is disabled.
-        refreshMenuItem.setVisible(!swipeToRefreshEnabled);
-
-        // Only show Ad Consent if this is the free flavor.
-        adConsentMenuItem.setVisible(BuildConfig.FLAVOR.contentEquals("free"));
-
         // Run all the other default commands.
         super.onPrepareOptionsMenu(menu);
 
@@ -1744,6 +1909,29 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
         // Set the commands that relate to the menu entries.
         switch (menuItemId) {
+            case R.id.toggle_javascript:
+                // Switch the status of javaScriptEnabled.
+                javaScriptEnabled = !javaScriptEnabled;
+
+                // Apply the new JavaScript status.
+                mainWebView.getSettings().setJavaScriptEnabled(javaScriptEnabled);
+
+                // Update the privacy icon.  `true` runs `invalidateOptionsMenu` as the last step.
+                updatePrivacyIcons(true);
+
+                // Display a `Snackbar`.
+                if (javaScriptEnabled) {  // JavaScrip is enabled.
+                    Snackbar.make(findViewById(R.id.main_webview), R.string.javascript_enabled, Snackbar.LENGTH_SHORT).show();
+                } else if (firstPartyCookiesEnabled) {  // JavaScript is disabled, but first-party cookies are enabled.
+                    Snackbar.make(findViewById(R.id.main_webview), R.string.javascript_disabled, Snackbar.LENGTH_SHORT).show();
+                } else {  // Privacy mode.
+                    Snackbar.make(findViewById(R.id.main_webview), R.string.privacy_mode, Snackbar.LENGTH_SHORT).show();
+                }
+
+                // Reload the WebView.
+                mainWebView.reload();
+                return true;
+
             case R.id.add_or_edit_domain:
                 if (domainSettingsApplied) {  // Edit the current domain settings.
                     // Reapply the domain settings on returning to `MainWebViewActivity`.
@@ -1786,29 +1974,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 }
                 return true;
 
-            case R.id.toggle_javascript:
-                // Switch the status of javaScriptEnabled.
-                javaScriptEnabled = !javaScriptEnabled;
-
-                // Apply the new JavaScript status.
-                mainWebView.getSettings().setJavaScriptEnabled(javaScriptEnabled);
-
-                // Update the privacy icon.  `true` runs `invalidateOptionsMenu` as the last step.
-                updatePrivacyIcons(true);
-
-                // Display a `Snackbar`.
-                if (javaScriptEnabled) {  // JavaScrip is enabled.
-                    Snackbar.make(findViewById(R.id.main_webview), R.string.javascript_enabled, Snackbar.LENGTH_SHORT).show();
-                } else if (firstPartyCookiesEnabled) {  // JavaScript is disabled, but first-party cookies are enabled.
-                    Snackbar.make(findViewById(R.id.main_webview), R.string.javascript_disabled, Snackbar.LENGTH_SHORT).show();
-                } else {  // Privacy mode.
-                    Snackbar.make(findViewById(R.id.main_webview), R.string.privacy_mode, Snackbar.LENGTH_SHORT).show();
-                }
-
-                // Reload the WebView.
-                mainWebView.reload();
-                return true;
-
             case R.id.toggle_first_party_cookies:
                 // Switch the status of firstPartyCookiesEnabled.
                 firstPartyCookiesEnabled = !firstPartyCookiesEnabled;
@@ -1882,6 +2047,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 mainWebView.reload();
                 return true;
 
+            // Form data can be removed once the minimum API >= 26.
             case R.id.toggle_save_form_data:
                 // Switch the status of saveFormDataEnabled.
                 saveFormDataEnabled = !saveFormDataEnabled;
@@ -1974,6 +2140,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                         .show();
                 return true;
 
+            // Form data can be remove once the minimum API >= 26.
             case R.id.clear_form_data:
                 Snackbar.make(findViewById(R.id.main_webview), R.string.form_data_deleted, Snackbar.LENGTH_LONG)
                         .setAction(R.string.undo, v -> {
@@ -2031,6 +2198,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 mainWebView.getSettings().setTextZoom(200);
                 return true;
 
+            case R.id.swipe_to_refresh:
+                // Toggle swipe to refresh.
+                swipeRefreshLayout.setEnabled(!swipeRefreshLayout.isEnabled());
+                return true;
+
             case R.id.display_images:
                 if (mainWebView.getSettings().getLoadsImagesAutomatically()) {  // Images are currently loaded automatically.
                     mainWebView.getSettings().setLoadsImagesAutomatically(false);
@@ -2043,6 +2215,71 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 onTheFlyDisplayImagesSet = true;
                 return true;
 
+            case R.id.view_source:
+                // Launch the View Source activity.
+                Intent viewSourceIntent = new Intent(this, ViewSourceActivity.class);
+                startActivity(viewSourceIntent);
+                return true;
+
+            case R.id.easylist:
+                // Toggle the EasyList status.
+                easyListEnabled = !easyListEnabled;
+
+                // Update the menu checkbox.
+                menuItem.setChecked(easyListEnabled);
+
+                // Reload the main WebView.
+                mainWebView.reload();
+                return true;
+
+            case R.id.easyprivacy:
+                // Toggle the EasyPrivacy status.
+                easyPrivacyEnabled = !easyPrivacyEnabled;
+
+                // Update the menu checkbox.
+                menuItem.setChecked(easyPrivacyEnabled);
+
+                // Reload the main WebView.
+                mainWebView.reload();
+                return true;
+
+            case R.id.fanboys_annoyance_list:
+                // Toggle Fanboy's Annoyance List status.
+                fanboysAnnoyanceListEnabled = !fanboysAnnoyanceListEnabled;
+
+                // Update the menu checkbox.
+                menuItem.setChecked(fanboysAnnoyanceListEnabled);
+
+                // Update the staus of Fanboy's Social Blocking List.
+                MenuItem fanboysSocialBlockingListMenuItem = mainMenu.findItem(R.id.fanboys_social_blocking_list);
+                fanboysSocialBlockingListMenuItem.setEnabled(!fanboysAnnoyanceListEnabled);
+
+                // Reload the main WebView.
+                mainWebView.reload();
+                return true;
+
+            case R.id.fanboys_social_blocking_list:
+                // Toggle Fanboy's Social Blocking List status.
+                fanboysSocialBlockingListEnabled = !fanboysSocialBlockingListEnabled;
+
+                // Update the menu checkbox.
+                menuItem.setChecked(fanboysSocialBlockingListEnabled);
+
+                // Reload the main WebView.
+                mainWebView.reload();
+                return true;
+
+            case R.id.block_all_third_party_requests:
+                //Toggle the third-party requests blocker status.
+                blockAllThirdPartyRequests = !blockAllThirdPartyRequests;
+
+                // Update the menu checkbox.
+                menuItem.setChecked(blockAllThirdPartyRequests);
+
+                // Reload the main WebView.
+                mainWebView.reload();
+                return true;
+
             case R.id.share:
                 // Setup the share string.
                 String shareString = webViewTitle + " – " + urlTextBox.getText().toString();
@@ -2089,12 +2326,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 printManager.print(getString(R.string.privacy_browser_web_page), printDocumentAdapter, null);
                 return true;
 
-            case R.id.view_source:
-                // Launch the Vew Source activity.
-                Intent viewSourceIntent = new Intent(this, ViewSourceActivity.class);
-                startActivity(viewSourceIntent);
-                return true;
-
             case R.id.add_to_homescreen:
                 // Show the `CreateHomeScreenShortcutDialog` `AlertDialog` and name this instance `R.string.create_shortcut`.
                 AppCompatDialogFragment createHomeScreenShortcutDialogFragment = new CreateHomeScreenShortcutDialog();
@@ -2132,6 +2363,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
             case R.id.back:
                 if (mainWebView.canGoBack()) {
+                    // Reset the formatted URL string so the page will load correctly if blocking of third-party requests is enabled.
+                    formattedUrlString = "";
+
                     // Set `navigatingHistory` so that the domain settings are applied when the new URL is loaded.
                     navigatingHistory = true;
 
@@ -2142,6 +2376,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
             case R.id.forward:
                 if (mainWebView.canGoForward()) {
+                    // Reset the formatted URL string so the page will load correctly if blocking of third-party requests is enabled.
+                    formattedUrlString = "";
+
                     // Set `navigatingHistory` so that the domain settings are applied when the new URL is loaded.
                     navigatingHistory = true;
 
@@ -2159,6 +2396,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 urlHistoryDialogFragment.show(getSupportFragmentManager(), getString(R.string.history));
                 break;
 
+            case R.id.requests:
+                // Launch the requests activity.
+                Intent requestsIntent = new Intent(this, RequestsActivity.class);
+                startActivity(requestsIntent);
+                break;
+
             case R.id.downloads:
                 // Launch the system Download Manager.
                 Intent downloadManagerIntent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS);
@@ -2174,7 +2417,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 reapplyDomainSettingsOnRestart = true;
                 currentDomainName = "";
 
-                // Launch `DomainsActivity`.
+                // Launch the domains activity.
                 Intent domainsIntent = new Intent(this, DomainsActivity.class);
                 startActivity(domainsIntent);
                 break;
@@ -2187,7 +2430,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 reapplyDomainSettingsOnRestart = true;
                 currentDomainName = "";
 
-                // Launch `SettingsActivity`.
+                // Launch the settings activity.
                 Intent settingsIntent = new Intent(this, SettingsActivity.class);
                 startActivity(settingsIntent);
                 break;
@@ -2250,8 +2493,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                     }
                 }
 
-                // Clear form data.
-                if (clearEverything || sharedPreferences.getBoolean("clear_form_data", true)) {
+                // Clear form data if the API < 26.
+                if ((Build.VERSION.SDK_INT < 26) && (clearEverything || sharedPreferences.getBoolean("clear_form_data", true))) {
                     WebViewDatabase webViewDatabase = WebViewDatabase.getInstance(this);
                     webViewDatabase.clearFormData();
 
@@ -2969,6 +3212,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     @Override
     public void onSslMismatchBack() {
         if (mainWebView.canGoBack()) {  // There is a back page in the history.
+            // Reset the formatted URL string so the page will load correctly if blocking of third-party requests is enabled.
+            formattedUrlString = "";
+
             // Set `navigatingHistory` so that the domain settings are applied when the new URL is loaded.
             navigatingHistory = true;
 
@@ -2988,6 +3234,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
     @Override
     public void onUrlHistoryEntrySelected(int moveBackOrForwardSteps) {
+        // Reset the formatted URL string so the page will load correctly if blocking of third-party requests is enabled.
+        formattedUrlString = "";
+
         // Set `navigatingHistory` so that the domain settings are applied when the new URL is loaded.
         navigatingHistory = true;
 
@@ -3020,6 +3269,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             }
 
         } else if (mainWebView.canGoBack()) {  // There is at least one item in the `WebView` history.
+            // Reset the formatted URL string so the page will load correctly if blocking of third-party requests is enabled.
+            formattedUrlString = "";
+
             // Set `navigatingHistory` so that the domain settings are applied when the new URL is loaded.
             navigatingHistory = true;
 
@@ -3075,7 +3327,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
             // Decode `formattedUri` as a `String` in `UTF-8`.
             formattedUrlString = URLDecoder.decode(formattedUri.build().toString(), "UTF-8");
-        } else {
+        } else if (unformattedUrlString.isEmpty()){  // Load a blank web site.
+            // Load a blank string.
+            formattedUrlString = "";
+        } else {  // Search for the contents of the URL box.
             // Sanitize the search input and convert it to a search.
             final String encodedUrlString = URLEncoder.encode(unformattedUrlString, "UTF-8");
 
@@ -3086,19 +3341,22 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Clear the focus from the URL text box.  Otherwise, proximate typing in the box will retain the colorized formatting instead of being reset during refocus.
         urlTextBox.clearFocus();
 
+        // Make it so.
         loadUrl(formattedUrlString);
     }
 
+    private void loadUrl(String url) {// Apply any custom domain settings.
+        // Set the URL as the formatted URL string so that checking third-party requests works correctly.
+        formattedUrlString = url;
 
-    private void loadUrl(String url) {
-        // Apply any custom domain settings.
+        // Apply the domain settings.
         applyDomainSettings(url, true, false);
 
-        // Load the URL.
-        mainWebView.loadUrl(url, customHeaders);
-
         // Set `urlIsLoading` to prevent changes in the user agent on websites with redirects from reloading the current website.
         urlIsLoading = true;
+
+        // Load the URL.
+        mainWebView.loadUrl(url, customHeaders);
     }
 
     public void findPreviousOnPage(View view) {
@@ -3145,7 +3403,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         fullScreenBrowsingModeEnabled = sharedPreferences.getBoolean("full_screen_browsing_mode", false);
         hideSystemBarsOnFullscreen = sharedPreferences.getBoolean("hide_system_bars", false);
         translucentNavigationBarOnFullscreen = sharedPreferences.getBoolean("translucent_navigation_bar", true);
-        swipeToRefreshEnabled = sharedPreferences.getBoolean("swipe_to_refresh", false);
         displayWebpageImagesBoolean = sharedPreferences.getBoolean("display_webpage_images", true);
 
         // Set the homepage, search, and proxy options.
@@ -3175,7 +3432,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 appBar.setBackgroundDrawable(ContextCompat.getDrawable(this, R.color.blue_50));
             }
 
-            // Display a message to the user if we are waiting on Orbot.
+            // Display a message to the user if waiting for Orbot.
             if (!orbotStatus.equals("ON")) {
                 // Set `waitingForOrbot`.
                 waitingForOrbot = true;
@@ -3213,9 +3470,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             waitingForOrbot = false;
         }
 
-        // Set swipe to refresh.
-        swipeRefreshLayout.setEnabled(swipeToRefreshEnabled);
-
         // Set Do Not Track status.
         if (doNotTrackEnabled) {
             customHeaders.put("DNT", "1");
@@ -3285,9 +3539,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     // The deprecated `.getDrawable()` must be used until the minimum API >= 21.
     @SuppressWarnings("deprecation")
     private void applyDomainSettings(String url, boolean resetFavoriteIcon, boolean reloadWebsite) {
-        // Reset `navigatingHistory`.
-        navigatingHistory = false;
-
         // Parse the URL into a URI.
         Uri uri = Uri.parse(url);
 
@@ -3306,6 +3557,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             loadingNewDomainName = !hostName.equals(currentDomainName);
         }
 
+        // Strings don't like to be null.
+        if (hostName == null) {
+            hostName = "";
+        }
+
         // Only apply the domain settings if a new domain is being loaded.  This allows the user to set temporary settings for JavaScript, cookies, DOM storage, etc.
         if (loadingNewDomainName) {
             // Set the new `hostname` as the `currentDomainName`.
@@ -3355,26 +3611,29 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 domainNameInDatabase = hostName;
             }
 
-            // If `hostName` is not `null`, check all the subdomains of `hostName` against wildcard domains in `domainCursor`.
-            if (hostName != null) {
-                while (hostName.contains(".") && !domainSettingsApplied) {  // Stop checking if we run out of  `.` or if we already know that `domainSettingsApplied` is `true`.
-                    if (domainSettingsSet.contains("*." + hostName)) {  // Check the host name prepended by `*.`.
-                        domainSettingsApplied = true;
-                        domainNameInDatabase = "*." + hostName;
-                    }
+            // Check all the subdomains of the host name against wildcard domains in the domain cursor.
+            while (!domainSettingsApplied && hostName.contains(".")) {  // Stop checking if domain settings are already applied or there are no more `.` in the host name.
+                if (domainSettingsSet.contains("*." + hostName)) {  // Check the host name prepended by `*.`.
+                    // Apply the domain settings.
+                    domainSettingsApplied = true;
 
-                    // Strip out the lowest subdomain of `host`.
-                    hostName = hostName.substring(hostName.indexOf(".") + 1);
+                    // Store the applied domain names as it appears in the database.
+                    domainNameInDatabase = "*." + hostName;
                 }
+
+                // Strip out the lowest subdomain of of the host name.
+                hostName = hostName.substring(hostName.indexOf(".") + 1);
             }
 
-            // Get a handle for the shared preference.  `this` references the current context.
+
+            // Get a handle for the shared preference.
             SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
 
             // Store the general preference information.
             String defaultFontSizeString = sharedPreferences.getString("default_font_size", "100");
             String defaultUserAgentName = sharedPreferences.getString("user_agent", "Privacy Browser");
             String defaultCustomUserAgentString = sharedPreferences.getString("custom_user_agent", "PrivacyBrowser/1.0");
+            boolean defaultSwipeToRefresh = sharedPreferences.getBoolean("swipe_to_refresh", true);
             nightMode = sharedPreferences.getBoolean("night_mode", false);
 
             if (domainSettingsApplied) {  // The url we are loading has custom domain settings.
@@ -3388,15 +3647,18 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 firstPartyCookiesEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FIRST_PARTY_COOKIES)) == 1);
                 thirdPartyCookiesEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_THIRD_PARTY_COOKIES)) == 1);
                 domStorageEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_DOM_STORAGE)) == 1);
+                // Form data can be removed once the minimum API >= 26.
                 saveFormDataEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FORM_DATA)) == 1);
                 easyListEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_EASYLIST)) == 1);
                 easyPrivacyEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_EASYPRIVACY)) == 1);
                 fanboysAnnoyanceListEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FANBOYS_ANNOYANCE_LIST)) == 1);
                 fanboysSocialBlockingListEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST)) == 1);
+                blockAllThirdPartyRequests = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.BLOCK_ALL_THIRD_PARTY_REQUESTS)) == 1);
                 String userAgentName = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.USER_AGENT));
                 int fontSize = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.FONT_SIZE));
-                displayWebpageImagesInt = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.DISPLAY_IMAGES));
+                int swipeToRefreshInt = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SWIPE_TO_REFRESH));
                 int nightModeInt = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.NIGHT_MODE));
+                displayWebpageImagesInt = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.DISPLAY_IMAGES));
                 pinnedDomainSslCertificate = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE)) == 1);
                 pinnedDomainSslIssuedToCNameString = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME));
                 pinnedDomainSslIssuedToONameString = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATION));
@@ -3442,7 +3704,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 mainWebView.getSettings().setJavaScriptEnabled(javaScriptEnabled);
                 cookieManager.setAcceptCookie(firstPartyCookiesEnabled);
                 mainWebView.getSettings().setDomStorageEnabled(domStorageEnabled);
-                mainWebView.getSettings().setSaveFormData(saveFormDataEnabled);
+
+                // Apply the form data setting if the API < 26.
+                if (Build.VERSION.SDK_INT < 26) {
+                    mainWebView.getSettings().setSaveFormData(saveFormDataEnabled);
+                }
 
                 // Apply the font size.
                 if (fontSize == 0) {  // Apply the default font size.
@@ -3505,6 +3771,23 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                         }
                     }
 
+                    // Set swipe to refresh.
+                    switch (swipeToRefreshInt) {
+                        case DomainsDatabaseHelper.SWIPE_TO_REFRESH_SYSTEM_DEFAULT:
+                            // Set swipe to refresh according to the default.
+                            swipeRefreshLayout.setEnabled(defaultSwipeToRefresh);
+                            break;
+
+                        case DomainsDatabaseHelper.SWIPE_TO_REFRESH_ENABLED:
+                            // Enable swipe to refresh.
+                            swipeRefreshLayout.setEnabled(true);
+                            break;
+
+                        case DomainsDatabaseHelper.SWIPE_TO_REFRESH_DISABLED:
+                            // Disable swipe to refresh.
+                            swipeRefreshLayout.setEnabled(false);
+                    }
+
                     // Store the applied user agent string, which is used in the View Source activity.
                     appliedUserAgentString = mainWebView.getSettings().getUserAgentString();
                 }
@@ -3515,17 +3798,18 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 } else {
                     urlAppBarRelativeLayout.setBackground(getResources().getDrawable(R.drawable.url_bar_background_light_green));
                 }
-            } else {  // The URL we are loading does not have custom domain settings.  Load the defaults.
+            } else {  // The new URL does not have custom domain settings.  Load the defaults.
                 // Store the values from `sharedPreferences` in variables.
                 javaScriptEnabled = sharedPreferences.getBoolean("javascript_enabled", false);
                 firstPartyCookiesEnabled = sharedPreferences.getBoolean("first_party_cookies_enabled", false);
                 thirdPartyCookiesEnabled = sharedPreferences.getBoolean("third_party_cookies_enabled", false);
                 domStorageEnabled = sharedPreferences.getBoolean("dom_storage_enabled", false);
-                saveFormDataEnabled = sharedPreferences.getBoolean("save_form_data_enabled", false);
+                saveFormDataEnabled = sharedPreferences.getBoolean("save_form_data_enabled", false);  // Form data can be removed once the minimum API >= 26.
                 easyListEnabled = sharedPreferences.getBoolean("easylist", true);
                 easyPrivacyEnabled = sharedPreferences.getBoolean("easyprivacy", true);
                 fanboysAnnoyanceListEnabled = sharedPreferences.getBoolean("fanboy_annoyance_list", true);
                 fanboysSocialBlockingListEnabled = sharedPreferences.getBoolean("fanboy_social_blocking_list", true);
+                blockAllThirdPartyRequests = sharedPreferences.getBoolean("block_all_third_party_requests", false);
 
                 // Set `javaScriptEnabled` to be `true` if `night_mode` is `true`.
                 if (nightMode) {
@@ -3536,8 +3820,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 mainWebView.getSettings().setJavaScriptEnabled(javaScriptEnabled);
                 cookieManager.setAcceptCookie(firstPartyCookiesEnabled);
                 mainWebView.getSettings().setDomStorageEnabled(domStorageEnabled);
-                mainWebView.getSettings().setSaveFormData(saveFormDataEnabled);
                 mainWebView.getSettings().setTextZoom(Integer.valueOf(defaultFontSizeString));
+                swipeRefreshLayout.setEnabled(defaultSwipeToRefresh);
+
+                // Apply the form data setting if the API < 26.
+                if (Build.VERSION.SDK_INT < 26) {
+                    mainWebView.getSettings().setSaveFormData(saveFormDataEnabled);
+                }
 
                 // Reset the pinned SSL certificate information.
                 domainSettingsDatabaseId = -1;
@@ -3603,11 +3892,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             if (mainMenu != null) {
                 updatePrivacyIcons(true);
             }
+        }
 
-            // Reload the website if returning from the Domains activity.
-            if (reloadWebsite) {
-                mainWebView.reload();
-            }
+        // Reload the website if returning from the Domains activity.
+        if (reloadWebsite) {
+            mainWebView.reload();
         }
     }
 
@@ -3634,58 +3923,54 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     }
 
     private void updatePrivacyIcons(boolean runInvalidateOptionsMenu) {
-        // Get handles for the icons.
-        MenuItem privacyIconMenuItem = mainMenu.findItem(R.id.toggle_javascript);
-        MenuItem firstPartyCookiesIconMenuItem = mainMenu.findItem(R.id.toggle_first_party_cookies);
-        MenuItem domStorageIconMenuItem = mainMenu.findItem(R.id.toggle_dom_storage);
-        MenuItem formDataIconMenuItem = mainMenu.findItem(R.id.toggle_save_form_data);
+        // Get handles for the menu items.
+        MenuItem privacyMenuItem = mainMenu.findItem(R.id.toggle_javascript);
+        MenuItem firstPartyCookiesMenuItem = mainMenu.findItem(R.id.toggle_first_party_cookies);
+        MenuItem domStorageMenuItem = mainMenu.findItem(R.id.toggle_dom_storage);
+        MenuItem refreshMenuItem = mainMenu.findItem(R.id.refresh);
 
-        // Update `privacyIcon`.
+        // Update the privacy icon.
         if (javaScriptEnabled) {  // JavaScript is enabled.
-            privacyIconMenuItem.setIcon(R.drawable.javascript_enabled);
+            privacyMenuItem.setIcon(R.drawable.javascript_enabled);
         } else if (firstPartyCookiesEnabled) {  // JavaScript is disabled but cookies are enabled.
-            privacyIconMenuItem.setIcon(R.drawable.warning);
+            privacyMenuItem.setIcon(R.drawable.warning);
         } else {  // All the dangerous features are disabled.
-            privacyIconMenuItem.setIcon(R.drawable.privacy_mode);
+            privacyMenuItem.setIcon(R.drawable.privacy_mode);
         }
 
-        // Update `firstPartyCookiesIcon`.
+        // Update the first-party cookies icon.
         if (firstPartyCookiesEnabled) {  // First-party cookies are enabled.
-            firstPartyCookiesIconMenuItem.setIcon(R.drawable.cookies_enabled);
+            firstPartyCookiesMenuItem.setIcon(R.drawable.cookies_enabled);
         } else {  // First-party cookies are disabled.
             if (darkTheme) {
-                firstPartyCookiesIconMenuItem.setIcon(R.drawable.cookies_disabled_dark);
+                firstPartyCookiesMenuItem.setIcon(R.drawable.cookies_disabled_dark);
             } else {
-                firstPartyCookiesIconMenuItem.setIcon(R.drawable.cookies_disabled_light);
+                firstPartyCookiesMenuItem.setIcon(R.drawable.cookies_disabled_light);
             }
         }
 
-        // Update `domStorageIcon`.
+        // Update the DOM storage icon.
         if (javaScriptEnabled && domStorageEnabled) {  // Both JavaScript and DOM storage are enabled.
-            domStorageIconMenuItem.setIcon(R.drawable.dom_storage_enabled);
+            domStorageMenuItem.setIcon(R.drawable.dom_storage_enabled);
         } else if (javaScriptEnabled) {  // JavaScript is enabled but DOM storage is disabled.
             if (darkTheme) {
-                domStorageIconMenuItem.setIcon(R.drawable.dom_storage_disabled_dark);
+                domStorageMenuItem.setIcon(R.drawable.dom_storage_disabled_dark);
             } else {
-                domStorageIconMenuItem.setIcon(R.drawable.dom_storage_disabled_light);
+                domStorageMenuItem.setIcon(R.drawable.dom_storage_disabled_light);
             }
         } else {  // JavaScript is disabled, so DOM storage is ghosted.
             if (darkTheme) {
-                domStorageIconMenuItem.setIcon(R.drawable.dom_storage_ghosted_dark);
+                domStorageMenuItem.setIcon(R.drawable.dom_storage_ghosted_dark);
             } else {
-                domStorageIconMenuItem.setIcon(R.drawable.dom_storage_ghosted_light);
+                domStorageMenuItem.setIcon(R.drawable.dom_storage_ghosted_light);
             }
         }
 
-        // Update `formDataIcon`.
-        if (saveFormDataEnabled) {  // Form data is enabled.
-            formDataIconMenuItem.setIcon(R.drawable.form_data_enabled);
-        } else {  // Form data is disabled.
-            if (darkTheme) {
-                formDataIconMenuItem.setIcon(R.drawable.form_data_disabled_dark);
-            } else {
-                formDataIconMenuItem.setIcon(R.drawable.form_data_disabled_light);
-            }
+        // Update the refresh icon.
+        if (darkTheme) {
+            refreshMenuItem.setIcon(R.drawable.refresh_enabled_dark);
+        } else {
+            refreshMenuItem.setIcon(R.drawable.refresh_enabled_light);
         }
 
         // `invalidateOptionsMenu` calls `onPrepareOptionsMenu()` and redraws the icons in the `AppBar`.