]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/blobdiff - app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java
Remove the off-screen WebView layout in full-screen browsing mode if the app bar...
[PrivacyBrowserAndroid.git] / app / src / main / java / com / stoutner / privacybrowser / activities / MainWebViewActivity.java
index 89a73f25608c99c1254ccc32cff939477fe40e8e..7cbe286dcd81fdf529caed9acc2b8bc7547ea8f7 100644 (file)
@@ -31,6 +31,7 @@ import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
 import android.content.ClipData;
 import android.content.ClipboardManager;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -46,6 +47,7 @@ import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.net.http.SslCertificate;
 import android.net.http.SslError;
+import android.os.AsyncTask;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
@@ -102,11 +104,15 @@ import androidx.appcompat.widget.Toolbar;
 import androidx.coordinatorlayout.widget.CoordinatorLayout;
 import androidx.core.app.ActivityCompat;
 import androidx.core.content.ContextCompat;
+import androidx.core.content.FileProvider;
+import androidx.core.content.res.ResourcesCompat;
 import androidx.core.view.GravityCompat;
 import androidx.drawerlayout.widget.DrawerLayout;
 import androidx.fragment.app.DialogFragment;
 import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
 import androidx.viewpager.widget.ViewPager;
+import androidx.webkit.WebSettingsCompat;
+import androidx.webkit.WebViewFeature;
 
 import com.google.android.material.appbar.AppBarLayout;
 import com.google.android.material.floatingactionbutton.FloatingActionButton;
@@ -132,7 +138,7 @@ import com.stoutner.privacybrowser.dialogs.HttpAuthenticationDialog;
 import com.stoutner.privacybrowser.dialogs.OpenDialog;
 import com.stoutner.privacybrowser.dialogs.ProxyNotInstalledDialog;
 import com.stoutner.privacybrowser.dialogs.PinnedMismatchDialog;
-import com.stoutner.privacybrowser.dialogs.SaveDialog;
+import com.stoutner.privacybrowser.dialogs.SaveWebpageDialog;
 import com.stoutner.privacybrowser.dialogs.SslCertificateErrorDialog;
 import com.stoutner.privacybrowser.dialogs.StoragePermissionDialog;
 import com.stoutner.privacybrowser.dialogs.UrlHistoryDialog;
@@ -142,7 +148,6 @@ import com.stoutner.privacybrowser.fragments.WebViewTabFragment;
 import com.stoutner.privacybrowser.helpers.AdHelper;
 import com.stoutner.privacybrowser.helpers.BlocklistHelper;
 import com.stoutner.privacybrowser.helpers.BookmarksDatabaseHelper;
-import com.stoutner.privacybrowser.helpers.CheckPinnedMismatchHelper;
 import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper;
 import com.stoutner.privacybrowser.helpers.FileNameHelper;
 import com.stoutner.privacybrowser.helpers.ProxyHelper;
@@ -166,22 +171,23 @@ import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 
 public class MainWebViewActivity extends AppCompatActivity implements CreateBookmarkDialog.CreateBookmarkListener, CreateBookmarkFolderDialog.CreateBookmarkFolderListener,
         EditBookmarkFolderDialog.EditBookmarkFolderListener, FontSizeDialog.UpdateFontSizeListener, NavigationView.OnNavigationItemSelectedListener, OpenDialog.OpenListener,
-        PinnedMismatchDialog.PinnedMismatchListener, PopulateBlocklists.PopulateBlocklistsListener, SaveDialog.SaveWebpageListener, StoragePermissionDialog.StoragePermissionDialogListener,
+        PinnedMismatchDialog.PinnedMismatchListener, PopulateBlocklists.PopulateBlocklistsListener, SaveWebpageDialog.SaveWebpageListener, StoragePermissionDialog.StoragePermissionDialogListener,
         UrlHistoryDialog.NavigateHistoryListener, WebViewTabFragment.NewTabListener {
 
+    // The executor service handles background tasks.  It is accessed from `ViewSourceActivity`.
+    public static ExecutorService executorService = Executors.newFixedThreadPool(4);
+
     // `orbotStatus` is public static so it can be accessed from `OrbotProxyHelper`.  It is also used in `onCreate()`, `onResume()`, and `applyProxy()`.
     public static String orbotStatus = "unknown";
 
     // The WebView pager adapter is accessed from `HttpAuthenticationDialog`, `PinnedMismatchDialog`, and `SslCertificateErrorDialog`.  It is also used in `onCreate()`, `onResume()`, and `addTab()`.
     public static WebViewPagerAdapter webViewPagerAdapter;
 
-    // The load URL on restart variables are public static so they can be accessed from `BookmarksActivity`.  They are used in `onRestart()`.
-    public static boolean loadUrlOnRestart;
-    public static String urlToLoadOnRestart;
-
     // `restartFromBookmarksActivity` is public static so it can be accessed from `BookmarksActivity`.  It is also used in `onRestart()`.
     public static boolean restartFromBookmarksActivity;
 
@@ -207,12 +213,21 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     // It will be updated in `applyAppSettings()`, but it needs to be initialized here or the first run of `onPrepareOptionsMenu()` crashes.
     public static String proxyMode = ProxyHelper.NONE;
 
+    // Define the saved instance state constants.
+    private final String SAVED_STATE_ARRAY_LIST = "saved_state_array_list";
+    private final String SAVED_NESTED_SCROLL_WEBVIEW_STATE_ARRAY_LIST = "saved_nested_scroll_webview_state_array_list";
+    private final String SAVED_TAB_POSITION = "saved_tab_position";
+    private final String PROXY_MODE = "proxy_mode";
 
-    // The permission result request codes are used in `onCreateContextMenu()`, `onRequestPermissionResult()`, `onSaveWebpage()`, `onCloseStoragePermissionDialog()`, and `initializeWebView()`.
-    private final int PERMISSION_OPEN_REQUEST_CODE = 0;
-    private final int PERMISSION_SAVE_URL_REQUEST_CODE = 1;
-    private final int PERMISSION_SAVE_AS_ARCHIVE_REQUEST_CODE = 2;
-    private final int PERMISSION_SAVE_AS_IMAGE_REQUEST_CODE = 3;
+    // Define the saved instance state variables.
+    private ArrayList<Bundle> savedStateArrayList;
+    private ArrayList<Bundle> savedNestedScrollWebViewStateArrayList;
+    private int savedTabPosition;
+    private String savedProxyMode;
+
+    // Define the class variables.
+    @SuppressWarnings("rawtypes")
+    AsyncTask populateBlocklists;
 
     // The current WebView is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onRestart()`, `onCreateContextMenu()`, `findPreviousOnPage()`,
     // `findNextOnPage()`, `closeFindOnPage()`, `loadUrlFromTextBox()`, `onSslMismatchBack()`, `applyProxy()`, and `applyDomainSettings()`.
@@ -308,6 +323,16 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     private String saveWebpageUrl;
     private String saveWebpageFilePath;
 
+    // Declare the class views.
+    private DrawerLayout drawerLayout;
+    private AppBarLayout appBarLayout;
+    private Toolbar toolbar;
+    private LinearLayout findOnPageLinearLayout;
+    private LinearLayout tabsLinearLayout;
+    private TabLayout tabLayout;
+    private SwipeRefreshLayout swipeRefreshLayout;
+    private ViewPager webViewPager;
+
     @Override
     // Remove the warning about needing to override `performClick()` when using an `OnTouchListener` with `WebView`.
     @SuppressLint("ClickableViewAccessibility")
@@ -315,6 +340,15 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Run the default commands.
         super.onCreate(savedInstanceState);
 
+        // Check to see if the activity has been restarted.
+        if (savedInstanceState != null) {
+            // Store the saved instance state variables.
+            savedStateArrayList = savedInstanceState.getParcelableArrayList(SAVED_STATE_ARRAY_LIST);
+            savedNestedScrollWebViewStateArrayList = savedInstanceState.getParcelableArrayList(SAVED_NESTED_SCROLL_WEBVIEW_STATE_ARRAY_LIST);
+            savedTabPosition = savedInstanceState.getInt(SAVED_TAB_POSITION);
+            savedProxyMode = savedInstanceState.getString(PROXY_MODE);
+        }
+
         // Initialize the default preference values the first time the program is run.  `false` keeps this command from resetting any current preferences back to default.
         PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
 
@@ -323,7 +357,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
         // Get the screenshot preference.
         String appTheme = sharedPreferences.getString("app_theme", getString(R.string.app_theme_default_value));
-        boolean allowScreenshots = sharedPreferences.getBoolean("allow_screenshots", false);
+        boolean allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false);
 
         // Get the theme entry values string array.
         String[] appThemeEntryValuesStringArray = getResources().getStringArray(R.array.app_theme_entry_values);
@@ -361,10 +395,15 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Set the content view.
         setContentView(R.layout.main_framelayout);
 
-        // Get handles for the views that need to be modified.
-        DrawerLayout drawerLayout = findViewById(R.id.drawerlayout);
-        Toolbar toolbar = findViewById(R.id.toolbar);
-        ViewPager webViewPager = findViewById(R.id.webviewpager);
+        // Get handles for the views.
+        drawerLayout = findViewById(R.id.drawerlayout);
+        appBarLayout = findViewById(R.id.appbar_layout);
+        toolbar = findViewById(R.id.toolbar);
+        findOnPageLinearLayout = findViewById(R.id.find_on_page_linearlayout);
+        tabsLinearLayout = findViewById(R.id.tabs_linearlayout);
+        tabLayout = findViewById(R.id.tablayout);
+        swipeRefreshLayout = findViewById(R.id.swiperefreshlayout);
+        webViewPager = findViewById(R.id.webviewpager);
 
         // Get a handle for the app compat delegate.
         AppCompatDelegate appCompatDelegate = getDelegate();
@@ -397,8 +436,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Store up to 100 tabs in memory.
         webViewPager.setOffscreenPageLimit(100);
 
+        // Initialize the app.
+        initializeApp();
+
+        // Apply the app settings from the shared preferences.
+        applyAppSettings();
+
         // Populate the blocklists.
-        new PopulateBlocklists(this, this).execute();
+        populateBlocklists = new PopulateBlocklists(this, this).execute();
     }
 
     @Override
@@ -409,8 +454,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Replace the intent that started the app with this one.
         setIntent(intent);
 
-        // Process the intent here if Privacy Browser is fully initialized.  If the process has been killed by the system while sitting in the background, this will be handled in `initializeWebView()`.
-        if (ultraPrivacy != null) {
+        // Check to see if the app is being restarted.
+        if (savedStateArrayList == null || savedStateArrayList.size() == 0) {  // The activity is running for the first time.
             // Get the information from the intent.
             String intentAction = intent.getAction();
             Uri intentUriData = intent.getData();
@@ -427,7 +472,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 String url;
 
                 // If the intent action is a web search, perform the search.
-                if (isWebSearch) {
+                if (isWebSearch) {  // The intent is a web search.
                     // Create an encoded URL string.
                     String encodedUrlString;
 
@@ -457,9 +502,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                     loadUrl(currentWebView, url);
                 }
 
-                // Get a handle for the drawer layout.
-                DrawerLayout drawerLayout = findViewById(R.id.drawerlayout);
-
                 // Close the navigation drawer if it is open.
                 if (drawerLayout.isDrawerVisible(GravityCompat.START)) {
                     drawerLayout.closeDrawer(GravityCompat.START);
@@ -510,26 +552,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                     // Reapply the domain settings if the URL is not null, which can happen if an empty tab is active when returning from settings.
                     if (nestedScrollWebView.getUrl() != null) {
-                        applyDomainSettings(nestedScrollWebView, nestedScrollWebView.getUrl(), false, true);
+                        applyDomainSettings(nestedScrollWebView, nestedScrollWebView.getUrl(), false, true, false);
                     }
                 }
             }
         }
 
-        // Load the URL on restart (used when loading a bookmark).
-        if (loadUrlOnRestart) {
-            // Load the specified URL.
-            loadUrl(currentWebView, urlToLoadOnRestart);
-
-            // Reset the load on restart tracker.
-            loadUrlOnRestart = false;
-        }
-
         // Update the bookmarks drawer if returning from the Bookmarks activity.
         if (restartFromBookmarksActivity) {
-            // Get a handle for the drawer layout.
-            DrawerLayout drawerLayout = findViewById(R.id.drawerlayout);
-
             // Close the bookmarks drawer.
             drawerLayout.closeDrawer(GravityCompat.END);
 
@@ -627,6 +657,50 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         }
     }
 
+    @Override
+    public void onSaveInstanceState(@NonNull Bundle savedInstanceState) {
+        // Run the default commands.
+        super.onSaveInstanceState(savedInstanceState);
+
+        // Create the saved state array lists.
+        ArrayList<Bundle> savedStateArrayList = new ArrayList<>();
+        ArrayList<Bundle> savedNestedScrollWebViewStateArrayList = new ArrayList<>();
+
+        // Get the URLs from each tab.
+        for (int i = 0; i < webViewPagerAdapter.getCount(); i++) {
+            // Get the WebView tab fragment.
+            WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(i);
+
+            // Get the fragment view.
+            View fragmentView = webViewTabFragment.getView();
+
+            if (fragmentView != null) {
+                // Get the nested scroll WebView from the tab fragment.
+                NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
+
+                // Create saved state bundle.
+                Bundle savedStateBundle = new Bundle();
+
+                // Get the current states.
+                nestedScrollWebView.saveState(savedStateBundle);
+                Bundle savedNestedScrollWebViewStateBundle = nestedScrollWebView.saveNestedScrollWebViewState();
+
+                // Store the saved states in the array lists.
+                savedStateArrayList.add(savedStateBundle);
+                savedNestedScrollWebViewStateArrayList.add(savedNestedScrollWebViewStateBundle);
+            }
+        }
+
+        // Get the current tab position.
+        int currentTabPosition = tabLayout.getSelectedTabPosition();
+
+        // Store the saved states in the bundle.
+        savedInstanceState.putParcelableArrayList(SAVED_STATE_ARRAY_LIST, savedStateArrayList);
+        savedInstanceState.putParcelableArrayList(SAVED_NESTED_SCROLL_WEBVIEW_STATE_ARRAY_LIST, savedNestedScrollWebViewStateArrayList);
+        savedInstanceState.putInt(SAVED_TAB_POSITION, currentTabPosition);
+        savedInstanceState.putString(PROXY_MODE, proxyMode);
+    }
+
     @Override
     public void onDestroy() {
         // Unregister the orbot status broadcast receiver if it exists.
@@ -644,6 +718,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             bookmarksDatabaseHelper.close();
         }
 
+        // Stop populating the blocklists if the AsyncTask is running in the background.
+        if (populateBlocklists != null) {
+            populateBlocklists.cancel(true);
+        }
+
         // Run the default commands.
         super.onDestroy();
     }
@@ -666,6 +745,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         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 darkWebViewMenuItem = menu.findItem(R.id.dark_webview);
         MenuItem adConsentMenuItem = menu.findItem(R.id.ad_consent);
 
         // Only display third-party cookies if API >= 21
@@ -678,6 +758,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Disable the clear form data menu item if the API >= 26 so that the status of the main Clear Data is calculated correctly.
         clearFormDataMenuItem.setEnabled(Build.VERSION.SDK_INT < 26);
 
+        // Only display the dark WebView menu item if API >= 21.
+        darkWebViewMenuItem.setVisible(Build.VERSION.SDK_INT >= 21);
+
         // Only show Ad Consent if this is the free flavor.
         adConsentMenuItem.setVisible(BuildConfig.FLAVOR.contentEquals("free"));
 
@@ -709,12 +792,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
 
                 // Set the icon according to the current theme status.
-                if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
-                    // Set the dark stop icon.
-                    refreshMenuItem.setIcon(R.drawable.close_night);
-                } else {
-                    // Set the light stop icon.
+                if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
                     refreshMenuItem.setIcon(R.drawable.close_day);
+                } else {
+                    refreshMenuItem.setIcon(R.drawable.close_night);
                 }
             }
         }
@@ -749,7 +830,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         MenuItem swipeToRefreshMenuItem = menu.findItem(R.id.swipe_to_refresh);
         MenuItem wideViewportMenuItem = menu.findItem(R.id.wide_viewport);
         MenuItem displayImagesMenuItem = menu.findItem(R.id.display_images);
-        MenuItem nightModeMenuItem = menu.findItem(R.id.night_mode);
+        MenuItem darkWebViewMenuItem = menu.findItem(R.id.dark_webview);
 
         // Get a handle for the cookie manager.
         CookieManager cookieManager = CookieManager.getInstance();
@@ -786,7 +867,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             swipeToRefreshMenuItem.setChecked(currentWebView.getSwipeToRefresh());
             wideViewportMenuItem.setChecked(currentWebView.getSettings().getUseWideViewPort());
             displayImagesMenuItem.setChecked(currentWebView.getSettings().getLoadsImagesAutomatically());
-            nightModeMenuItem.setChecked(currentWebView.getNightMode());
 
             // Initialize the display names for the blocklists with the number of blocked requests.
             blocklistsMenuItem.setTitle(getString(R.string.blocklists) + " - " + currentWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS));
@@ -809,6 +889,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
             // Enable DOM Storage if JavaScript is enabled.
             domStorageMenuItem.setEnabled(currentWebView.getSettings().getJavaScriptEnabled());
+
+            // Set the checkbox status for dark WebView if the WebView supports it.
+            if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
+                darkWebViewMenuItem.setChecked(WebSettingsCompat.getForceDark(currentWebView.getSettings()) == WebSettingsCompat.FORCE_DARK_ON);
+            }
         }
 
         // Set the checked status of the first party cookies menu item.
@@ -981,1046 +1066,976 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     }
 
     @Override
-    // Remove Android Studio's warning about the dangers of using SetJavaScriptEnabled.
-    @SuppressLint("SetJavaScriptEnabled")
     public boolean onOptionsItemSelected(MenuItem menuItem) {
-        // Get the selected menu item ID.
-        int menuItemId = menuItem.getItemId();
-
         // Get a handle for the shared preferences.
         SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
 
         // Get a handle for the cookie manager.
         CookieManager cookieManager = CookieManager.getInstance();
 
+        // Get the selected menu item ID.
+        int menuItemId = menuItem.getItemId();
+
         // Run the commands that correlate to the selected menu item.
-        switch (menuItemId) {
-            case R.id.toggle_javascript:
-                // Toggle the JavaScript status.
-                currentWebView.getSettings().setJavaScriptEnabled(!currentWebView.getSettings().getJavaScriptEnabled());
-
-                // Update the privacy icon.  `true` runs `invalidateOptionsMenu` as the last step.
-                updatePrivacyIcons(true);
-
-                // Display a `Snackbar`.
-                if (currentWebView.getSettings().getJavaScriptEnabled()) {  // JavaScrip is enabled.
-                    Snackbar.make(findViewById(R.id.webviewpager), R.string.javascript_enabled, Snackbar.LENGTH_SHORT).show();
-                } else if (cookieManager.acceptCookie()) {  // JavaScript is disabled, but first-party cookies are enabled.
-                    Snackbar.make(findViewById(R.id.webviewpager), R.string.javascript_disabled, Snackbar.LENGTH_SHORT).show();
-                } else {  // Privacy mode.
-                    Snackbar.make(findViewById(R.id.webviewpager), R.string.privacy_mode, Snackbar.LENGTH_SHORT).show();
-                }
+        if (menuItemId == R.id.toggle_javascript) {  // JavaScript.
+            // Toggle the JavaScript status.
+            currentWebView.getSettings().setJavaScriptEnabled(!currentWebView.getSettings().getJavaScriptEnabled());
 
+            // Update the privacy icon.
+            updatePrivacyIcons(true);
+
+            // Display a `Snackbar`.
+            if (currentWebView.getSettings().getJavaScriptEnabled()) {  // JavaScrip is enabled.
+                Snackbar.make(webViewPager, R.string.javascript_enabled, Snackbar.LENGTH_SHORT).show();
+            } else if (cookieManager.acceptCookie()) {  // JavaScript is disabled, but first-party cookies are enabled.
+                Snackbar.make(webViewPager, R.string.javascript_disabled, Snackbar.LENGTH_SHORT).show();
+            } else {  // Privacy mode.
+                Snackbar.make(webViewPager, R.string.privacy_mode, Snackbar.LENGTH_SHORT).show();
+            }
+
+            // Reload the current WebView.
+            currentWebView.reload();
+
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.refresh) {  // Refresh.
+            // Run the command that correlates to the current status of the menu item.
+            if (menuItem.getTitle().equals(getString(R.string.refresh))) {  // The refresh button was pushed.
                 // Reload the current WebView.
                 currentWebView.reload();
+            } else {  // The stop button was pushed.
+                // Stop the loading of the WebView.
+                currentWebView.stopLoading();
+            }
 
-                // Consume the event.
-                return true;
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.bookmarks) {  // Bookmarks.
+            // Open the bookmarks drawer.
+            drawerLayout.openDrawer(GravityCompat.END);
 
-            case R.id.refresh:
-                if (menuItem.getTitle().equals(getString(R.string.refresh))) {  // The refresh button was pushed.
-                    // Reload the current WebView.
-                    currentWebView.reload();
-                } else {  // The stop button was pushed.
-                    // Stop the loading of the WebView.
-                    currentWebView.stopLoading();
-                }
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.toggle_first_party_cookies) {  // First-party cookies.
+            // Switch the first-party cookie status.
+            cookieManager.setAcceptCookie(!cookieManager.acceptCookie());
 
-                // Consume the event.
-                return true;
+            // Store the first-party cookie status.
+            currentWebView.setAcceptFirstPartyCookies(cookieManager.acceptCookie());
 
-            case R.id.bookmarks:
-                // Get a handle for the drawer layout.
-                DrawerLayout drawerLayout = findViewById(R.id.drawerlayout);
+            // Update the menu checkbox.
+            menuItem.setChecked(cookieManager.acceptCookie());
 
-                // Open the bookmarks drawer.
-                drawerLayout.openDrawer(GravityCompat.END);
+            // Update the privacy icon.
+            updatePrivacyIcons(true);
 
-                // Consume the event.
-                return true;
+            // Display a snackbar.
+            if (cookieManager.acceptCookie()) {  // First-party cookies are enabled.
+                Snackbar.make(webViewPager, R.string.first_party_cookies_enabled, Snackbar.LENGTH_SHORT).show();
+            } else if (currentWebView.getSettings().getJavaScriptEnabled()) {  // JavaScript is still enabled.
+                Snackbar.make(webViewPager, R.string.first_party_cookies_disabled, Snackbar.LENGTH_SHORT).show();
+            } else {  // Privacy mode.
+                Snackbar.make(webViewPager, R.string.privacy_mode, Snackbar.LENGTH_SHORT).show();
+            }
 
-            case R.id.toggle_first_party_cookies:
-                // Switch the first-party cookie status.
-                cookieManager.setAcceptCookie(!cookieManager.acceptCookie());
+            // Reload the current WebView.
+            currentWebView.reload();
 
-                // Store the first-party cookie status.
-                currentWebView.setAcceptFirstPartyCookies(cookieManager.acceptCookie());
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.toggle_third_party_cookies) {  // Third-party cookies.
+            // Only act if the API >= 21.  Otherwise, there are no third-party cookie controls.
+            if (Build.VERSION.SDK_INT >= 21) {
+                // Toggle the status of thirdPartyCookiesEnabled.
+                cookieManager.setAcceptThirdPartyCookies(currentWebView, !cookieManager.acceptThirdPartyCookies(currentWebView));
 
                 // Update the menu checkbox.
-                menuItem.setChecked(cookieManager.acceptCookie());
-
-                // Update the privacy icon.  `true` runs `invalidateOptionsMenu` as the last step.
-                updatePrivacyIcons(true);
+                menuItem.setChecked(cookieManager.acceptThirdPartyCookies(currentWebView));
 
                 // Display a snackbar.
-                if (cookieManager.acceptCookie()) {  // First-party cookies are enabled.
-                    Snackbar.make(findViewById(R.id.webviewpager), R.string.first_party_cookies_enabled, Snackbar.LENGTH_SHORT).show();
-                } else if (currentWebView.getSettings().getJavaScriptEnabled()) {  // JavaScript is still enabled.
-                    Snackbar.make(findViewById(R.id.webviewpager), R.string.first_party_cookies_disabled, Snackbar.LENGTH_SHORT).show();
-                } else {  // Privacy mode.
-                    Snackbar.make(findViewById(R.id.webviewpager), R.string.privacy_mode, Snackbar.LENGTH_SHORT).show();
+                if (cookieManager.acceptThirdPartyCookies(currentWebView)) {
+                    Snackbar.make(webViewPager, R.string.third_party_cookies_enabled, Snackbar.LENGTH_SHORT).show();
+                } else {
+                    Snackbar.make(webViewPager, R.string.third_party_cookies_disabled, Snackbar.LENGTH_SHORT).show();
                 }
 
                 // Reload the current WebView.
                 currentWebView.reload();
+            }
 
-                // Consume the event.
-                return true;
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.toggle_dom_storage) {  // DOM storage.
+            // Toggle the status of domStorageEnabled.
+            currentWebView.getSettings().setDomStorageEnabled(!currentWebView.getSettings().getDomStorageEnabled());
 
-            case R.id.toggle_third_party_cookies:
-                if (Build.VERSION.SDK_INT >= 21) {
-                    // Switch the status of thirdPartyCookiesEnabled.
-                    cookieManager.setAcceptThirdPartyCookies(currentWebView, !cookieManager.acceptThirdPartyCookies(currentWebView));
+            // Update the menu checkbox.
+            menuItem.setChecked(currentWebView.getSettings().getDomStorageEnabled());
 
-                    // Update the menu checkbox.
-                    menuItem.setChecked(cookieManager.acceptThirdPartyCookies(currentWebView));
+            // Update the privacy icon.
+            updatePrivacyIcons(true);
 
-                    // Display a snackbar.
-                    if (cookieManager.acceptThirdPartyCookies(currentWebView)) {
-                        Snackbar.make(findViewById(R.id.webviewpager), R.string.third_party_cookies_enabled, Snackbar.LENGTH_SHORT).show();
-                    } else {
-                        Snackbar.make(findViewById(R.id.webviewpager), R.string.third_party_cookies_disabled, Snackbar.LENGTH_SHORT).show();
-                    }
+            // Display a snackbar.
+            if (currentWebView.getSettings().getDomStorageEnabled()) {
+                Snackbar.make(webViewPager, R.string.dom_storage_enabled, Snackbar.LENGTH_SHORT).show();
+            } else {
+                Snackbar.make(webViewPager, R.string.dom_storage_disabled, Snackbar.LENGTH_SHORT).show();
+            }
 
-                    // Reload the current WebView.
-                    currentWebView.reload();
-                } // Else do nothing because SDK < 21.
+            // Reload the current WebView.
+            currentWebView.reload();
 
-                // Consume the event.
-                return true;
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.toggle_save_form_data) {  // Form data.  This can be removed once the minimum API >= 26.
+            // Switch the status of saveFormDataEnabled.
+            currentWebView.getSettings().setSaveFormData(!currentWebView.getSettings().getSaveFormData());
 
-            case R.id.toggle_dom_storage:
-                // Toggle the status of domStorageEnabled.
-                currentWebView.getSettings().setDomStorageEnabled(!currentWebView.getSettings().getDomStorageEnabled());
+            // Update the menu checkbox.
+            menuItem.setChecked(currentWebView.getSettings().getSaveFormData());
 
-                // Update the menu checkbox.
-                menuItem.setChecked(currentWebView.getSettings().getDomStorageEnabled());
+            // Display a snackbar.
+            if (currentWebView.getSettings().getSaveFormData()) {
+                Snackbar.make(webViewPager, R.string.form_data_enabled, Snackbar.LENGTH_SHORT).show();
+            } else {
+                Snackbar.make(webViewPager, R.string.form_data_disabled, Snackbar.LENGTH_SHORT).show();
+            }
 
-                // Update the privacy icon.  `true` refreshes the app bar icons.
-                updatePrivacyIcons(true);
+            // Update the privacy icon.
+            updatePrivacyIcons(true);
 
-                // Display a snackbar.
-                if (currentWebView.getSettings().getDomStorageEnabled()) {
-                    Snackbar.make(findViewById(R.id.webviewpager), R.string.dom_storage_enabled, Snackbar.LENGTH_SHORT).show();
-                } else {
-                    Snackbar.make(findViewById(R.id.webviewpager), R.string.dom_storage_disabled, Snackbar.LENGTH_SHORT).show();
-                }
+            // Reload the current WebView.
+            currentWebView.reload();
 
-                // Reload the current WebView.
-                currentWebView.reload();
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.clear_cookies) {  // Clear cookies.
+            // Create a snackbar.
+            Snackbar.make(webViewPager, R.string.cookies_deleted, Snackbar.LENGTH_LONG)
+                    .setAction(R.string.undo, v -> {
+                        // Do nothing because everything will be handled by `onDismissed()` below.
+                    })
+                    .addCallback(new Snackbar.Callback() {
+                        @Override
+                        public void onDismissed(Snackbar snackbar, int event) {
+                            if (event != Snackbar.Callback.DISMISS_EVENT_ACTION) {  // The snackbar was dismissed without the undo button being pushed.
+                                // Delete the cookies, which command varies by SDK.
+                                if (Build.VERSION.SDK_INT < 21) {
+                                    cookieManager.removeAllCookie();
+                                } else {
+                                    cookieManager.removeAllCookies(null);
+                                }
+                            }
+                        }
+                    })
+                    .show();
 
-                // Consume the event.
-                return true;
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.clear_dom_storage) {  // Clear DOM storage.
+            // Create a snackbar.
+            Snackbar.make(webViewPager, R.string.dom_storage_deleted, Snackbar.LENGTH_LONG)
+                    .setAction(R.string.undo, v -> {
+                        // Do nothing because everything will be handled by `onDismissed()` below.
+                    })
+                    .addCallback(new Snackbar.Callback() {
+                        @Override
+                        public void onDismissed(Snackbar snackbar, int event) {
+                            if (event != Snackbar.Callback.DISMISS_EVENT_ACTION) {  // The snackbar was dismissed without the undo button being pushed.
+                                // Delete the DOM Storage.
+                                WebStorage webStorage = WebStorage.getInstance();
+                                webStorage.deleteAllData();
+
+                                // Initialize a handler to manually delete the DOM storage files and directories.
+                                Handler deleteDomStorageHandler = new Handler();
+
+                                // Setup a runnable to manually delete the DOM storage files and directories.
+                                Runnable deleteDomStorageRunnable = () -> {
+                                    try {
+                                        // Get a handle for the runtime.
+                                        Runtime runtime = Runtime.getRuntime();
+
+                                        // Get the application's private data directory, which will be something like `/data/user/0/com.stoutner.privacybrowser.standard`,
+                                        // which links to `/data/data/com.stoutner.privacybrowser.standard`.
+                                        String privateDataDirectoryString = getApplicationInfo().dataDir;
+
+                                        // A string array must be used because the directory contains a space and `Runtime.exec` will otherwise not escape the string correctly.
+                                        Process deleteLocalStorageProcess = runtime.exec(new String[]{"rm", "-rf", privateDataDirectoryString + "/app_webview/Local Storage/"});
+
+                                        // Multiple commands must be used because `Runtime.exec()` does not like `*`.
+                                        Process deleteIndexProcess = runtime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/IndexedDB");
+                                        Process deleteQuotaManagerProcess = runtime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager");
+                                        Process deleteQuotaManagerJournalProcess = runtime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager-journal");
+                                        Process deleteDatabasesProcess = runtime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/databases");
+
+                                        // Wait for the processes to finish.
+                                        deleteLocalStorageProcess.waitFor();
+                                        deleteIndexProcess.waitFor();
+                                        deleteQuotaManagerProcess.waitFor();
+                                        deleteQuotaManagerJournalProcess.waitFor();
+                                        deleteDatabasesProcess.waitFor();
+                                    } catch (Exception exception) {
+                                        // Do nothing if an error is thrown.
+                                    }
+                                };
 
-            // Form data can be removed once the minimum API >= 26.
-            case R.id.toggle_save_form_data:
-                // Switch the status of saveFormDataEnabled.
-                currentWebView.getSettings().setSaveFormData(!currentWebView.getSettings().getSaveFormData());
+                                // Manually delete the DOM storage files after 200 milliseconds.
+                                deleteDomStorageHandler.postDelayed(deleteDomStorageRunnable, 200);
+                            }
+                        }
+                    })
+                    .show();
 
-                // Update the menu checkbox.
-                menuItem.setChecked(currentWebView.getSettings().getSaveFormData());
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.clear_form_data) {  // Clear form data.  This can be remove once the minimum API >= 26.
+            // Create a snackbar.
+            Snackbar.make(webViewPager, R.string.form_data_deleted, Snackbar.LENGTH_LONG)
+                    .setAction(R.string.undo, v -> {
+                        // Do nothing because everything will be handled by `onDismissed()` below.
+                    })
+                    .addCallback(new Snackbar.Callback() {
+                        @Override
+                        public void onDismissed(Snackbar snackbar, int event) {
+                            if (event != Snackbar.Callback.DISMISS_EVENT_ACTION) {  // The snackbar was dismissed without the undo button being pushed.
+                                // Get a handle for the webView database.
+                                WebViewDatabase webViewDatabase = WebViewDatabase.getInstance(getApplicationContext());
+
+                                // Delete the form data.
+                                webViewDatabase.clearFormData();
+                            }
+                        }
+                    })
+                    .show();
 
-                // Display a snackbar.
-                if (currentWebView.getSettings().getSaveFormData()) {
-                    Snackbar.make(findViewById(R.id.webviewpager), R.string.form_data_enabled, Snackbar.LENGTH_SHORT).show();
-                } else {
-                    Snackbar.make(findViewById(R.id.webviewpager), R.string.form_data_disabled, Snackbar.LENGTH_SHORT).show();
-                }
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.easylist) {  // EasyList.
+            // Toggle the EasyList status.
+            currentWebView.enableBlocklist(NestedScrollWebView.EASYLIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYLIST));
 
-                // Update the privacy icon.  `true` runs `invalidateOptionsMenu` as the last step.
-                updatePrivacyIcons(true);
+            // Update the menu checkbox.
+            menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYLIST));
 
-                // Reload the current WebView.
-                currentWebView.reload();
+            // Reload the current WebView.
+            currentWebView.reload();
 
-                // Consume the event.
-                return true;
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.easyprivacy) {  // EasyPrivacy.
+            // Toggle the EasyPrivacy status.
+            currentWebView.enableBlocklist(NestedScrollWebView.EASYPRIVACY, !currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYPRIVACY));
 
-            case R.id.clear_cookies:
-                Snackbar.make(findViewById(R.id.webviewpager), R.string.cookies_deleted, Snackbar.LENGTH_LONG)
-                        .setAction(R.string.undo, v -> {
-                            // Do nothing because everything will be handled by `onDismissed()` below.
-                        })
-                        .addCallback(new Snackbar.Callback() {
-                            @SuppressLint("SwitchIntDef")  // Ignore the lint warning about not handling the other possible events as they are covered by `default:`.
-                            @Override
-                            public void onDismissed(Snackbar snackbar, int event) {
-                                if (event != Snackbar.Callback.DISMISS_EVENT_ACTION) {  // The snackbar was dismissed without the undo button being pushed.
-                                    // Delete the cookies, which command varies by SDK.
-                                    if (Build.VERSION.SDK_INT < 21) {
-                                        cookieManager.removeAllCookie();
-                                    } else {
-                                        cookieManager.removeAllCookies(null);
-                                    }
-                                }
-                            }
-                        })
-                        .show();
+            // Update the menu checkbox.
+            menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYPRIVACY));
 
-                // Consume the event.
-                return true;
+            // Reload the current WebView.
+            currentWebView.reload();
 
-            case R.id.clear_dom_storage:
-                Snackbar.make(findViewById(R.id.webviewpager), R.string.dom_storage_deleted, Snackbar.LENGTH_LONG)
-                        .setAction(R.string.undo, v -> {
-                            // Do nothing because everything will be handled by `onDismissed()` below.
-                        })
-                        .addCallback(new Snackbar.Callback() {
-                            @SuppressLint("SwitchIntDef")  // Ignore the lint warning about not handling the other possible events as they are covered by `default:`.
-                            @Override
-                            public void onDismissed(Snackbar snackbar, int event) {
-                                if (event != Snackbar.Callback.DISMISS_EVENT_ACTION) {  // The snackbar was dismissed without the undo button being pushed.
-                                    // Delete the DOM Storage.
-                                    WebStorage webStorage = WebStorage.getInstance();
-                                    webStorage.deleteAllData();
-
-                                    // Initialize a handler to manually delete the DOM storage files and directories.
-                                    Handler deleteDomStorageHandler = new Handler();
-
-                                    // Setup a runnable to manually delete the DOM storage files and directories.
-                                    Runnable deleteDomStorageRunnable = () -> {
-                                        try {
-                                            // Get a handle for the runtime.
-                                            Runtime runtime = Runtime.getRuntime();
-
-                                            // Get the application's private data directory, which will be something like `/data/user/0/com.stoutner.privacybrowser.standard`,
-                                            // which links to `/data/data/com.stoutner.privacybrowser.standard`.
-                                            String privateDataDirectoryString = getApplicationInfo().dataDir;
-
-                                            // A string array must be used because the directory contains a space and `Runtime.exec` will otherwise not escape the string correctly.
-                                            Process deleteLocalStorageProcess = runtime.exec(new String[]{"rm", "-rf", privateDataDirectoryString + "/app_webview/Local Storage/"});
-
-                                            // Multiple commands must be used because `Runtime.exec()` does not like `*`.
-                                            Process deleteIndexProcess = runtime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/IndexedDB");
-                                            Process deleteQuotaManagerProcess = runtime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager");
-                                            Process deleteQuotaManagerJournalProcess = runtime.exec("rm -f " + privateDataDirectoryString + "/app_webview/QuotaManager-journal");
-                                            Process deleteDatabasesProcess = runtime.exec("rm -rf " + privateDataDirectoryString + "/app_webview/databases");
-
-                                            // Wait for the processes to finish.
-                                            deleteLocalStorageProcess.waitFor();
-                                            deleteIndexProcess.waitFor();
-                                            deleteQuotaManagerProcess.waitFor();
-                                            deleteQuotaManagerJournalProcess.waitFor();
-                                            deleteDatabasesProcess.waitFor();
-                                        } catch (Exception exception) {
-                                            // Do nothing if an error is thrown.
-                                        }
-                                    };
-
-                                    // Manually delete the DOM storage files after 200 milliseconds.
-                                    deleteDomStorageHandler.postDelayed(deleteDomStorageRunnable, 200);
-                                }
-                            }
-                        })
-                        .show();
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.fanboys_annoyance_list) {  // Fanboy's Annoyance List.
+            // Toggle Fanboy's Annoyance List status.
+            currentWebView.enableBlocklist(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST));
 
-                // Consume the event.
-                return true;
+            // Update the menu checkbox.
+            menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST));
 
-            // Form data can be remove once the minimum API >= 26.
-            case R.id.clear_form_data:
-                Snackbar.make(findViewById(R.id.webviewpager), R.string.form_data_deleted, Snackbar.LENGTH_LONG)
-                        .setAction(R.string.undo, v -> {
-                            // Do nothing because everything will be handled by `onDismissed()` below.
-                        })
-                        .addCallback(new Snackbar.Callback() {
-                            @SuppressLint("SwitchIntDef")  // Ignore the lint warning about not handling the other possible events as they are covered by `default:`.
-                            @Override
-                            public void onDismissed(Snackbar snackbar, int event) {
-                                if (event != Snackbar.Callback.DISMISS_EVENT_ACTION) {  // The snackbar was dismissed without the undo button being pushed.
-                                    // Delete the form data.
-                                    WebViewDatabase mainWebViewDatabase = WebViewDatabase.getInstance(getApplicationContext());
-                                    mainWebViewDatabase.clearFormData();
-                                }
-                            }
-                        })
-                        .show();
+            // Get a handle for the Fanboy's Social Block List menu item.
+            MenuItem fanboysSocialBlockingListMenuItem = optionsMenu.findItem(R.id.fanboys_social_blocking_list);
 
-                // Consume the event.
-                return true;
+            // Update the staus of Fanboy's Social Blocking List.
+            fanboysSocialBlockingListMenuItem.setEnabled(!currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST));
 
-            case R.id.easylist:
-                // Toggle the EasyList status.
-                currentWebView.enableBlocklist(NestedScrollWebView.EASYLIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYLIST));
+            // Reload the current WebView.
+            currentWebView.reload();
 
-                // Update the menu checkbox.
-                menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYLIST));
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.fanboys_social_blocking_list) {  // Fanboy's Social Blocking List.
+            // Toggle Fanboy's Social Blocking List status.
+            currentWebView.enableBlocklist(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST));
 
-                // Reload the current WebView.
-                currentWebView.reload();
+            // Update the menu checkbox.
+            menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST));
 
-                // Consume the event.
-                return true;
+            // Reload the current WebView.
+            currentWebView.reload();
 
-            case R.id.easyprivacy:
-                // Toggle the EasyPrivacy status.
-                currentWebView.enableBlocklist(NestedScrollWebView.EASYPRIVACY, !currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYPRIVACY));
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.ultralist) {  // UltraList.
+            // Toggle the UltraList status.
+            currentWebView.enableBlocklist(NestedScrollWebView.ULTRALIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRALIST));
 
-                // Update the menu checkbox.
-                menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYPRIVACY));
+            // Update the menu checkbox.
+            menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRALIST));
 
-                // Reload the current WebView.
-                currentWebView.reload();
+            // Reload the current WebView.
+            currentWebView.reload();
 
-                // Consume the event.
-                return true;
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.ultraprivacy) {  // UltraPrivacy.
+            // Toggle the UltraPrivacy status.
+            currentWebView.enableBlocklist(NestedScrollWebView.ULTRAPRIVACY, !currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRAPRIVACY));
 
-            case R.id.fanboys_annoyance_list:
-                // Toggle Fanboy's Annoyance List status.
-                currentWebView.enableBlocklist(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST));
+            // Update the menu checkbox.
+            menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRAPRIVACY));
 
-                // Update the menu checkbox.
-                menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST));
+            // Reload the current WebView.
+            currentWebView.reload();
 
-                // Update the staus of Fanboy's Social Blocking List.
-                MenuItem fanboysSocialBlockingListMenuItem = optionsMenu.findItem(R.id.fanboys_social_blocking_list);
-                fanboysSocialBlockingListMenuItem.setEnabled(!currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST));
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.block_all_third_party_requests) {  // Block all third-party requests.
+            //Toggle the third-party requests blocker status.
+            currentWebView.enableBlocklist(NestedScrollWebView.THIRD_PARTY_REQUESTS, !currentWebView.isBlocklistEnabled(NestedScrollWebView.THIRD_PARTY_REQUESTS));
 
-                // Reload the current WebView.
-                currentWebView.reload();
+            // Update the menu checkbox.
+            menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.THIRD_PARTY_REQUESTS));
 
-                // Consume the event.
-                return true;
+            // Reload the current WebView.
+            currentWebView.reload();
 
-            case R.id.fanboys_social_blocking_list:
-                // Toggle Fanboy's Social Blocking List status.
-                currentWebView.enableBlocklist(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST));
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.proxy_none) {  // Proxy - None.
+            // Update the proxy mode.
+            proxyMode = ProxyHelper.NONE;
 
-                // Update the menu checkbox.
-                menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST));
+            // Apply the proxy mode.
+            applyProxy(true);
 
-                // Reload the current WebView.
-                currentWebView.reload();
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.proxy_tor) {  // Proxy - Tor.
+            // Update the proxy mode.
+            proxyMode = ProxyHelper.TOR;
 
-                // Consume the event.
-                return true;
+            // Apply the proxy mode.
+            applyProxy(true);
 
-            case R.id.ultralist:
-                // Toggle the UltraList status.
-                currentWebView.enableBlocklist(NestedScrollWebView.ULTRALIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRALIST));
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.proxy_i2p) {  // Proxy - I2P.
+            // Update the proxy mode.
+            proxyMode = ProxyHelper.I2P;
 
-                // Update the menu checkbox.
-                menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRALIST));
+            // Apply the proxy mode.
+            applyProxy(true);
 
-                // Reload the current WebView.
-                currentWebView.reload();
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.proxy_custom) {  // Proxy - Custom.
+            // Update the proxy mode.
+            proxyMode = ProxyHelper.CUSTOM;
 
-                // Consume the event.
-                return true;
+            // Apply the proxy mode.
+            applyProxy(true);
 
-            case R.id.ultraprivacy:
-                // Toggle the UltraPrivacy status.
-                currentWebView.enableBlocklist(NestedScrollWebView.ULTRAPRIVACY, !currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRAPRIVACY));
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.user_agent_privacy_browser) {  // User Agent - Privacy Browser.
+            // Update the user agent.
+            currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[0]);
 
-                // Update the menu checkbox.
-                menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRAPRIVACY));
+            // Reload the current WebView.
+            currentWebView.reload();
 
-                // Reload the current WebView.
-                currentWebView.reload();
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.user_agent_webview_default) {  // User Agent - WebView Default.
+            // Update the user agent.
+            currentWebView.getSettings().setUserAgentString("");
 
-                // Consume the event.
-                return true;
+            // Reload the current WebView.
+            currentWebView.reload();
 
-            case R.id.block_all_third_party_requests:
-                //Toggle the third-party requests blocker status.
-                currentWebView.enableBlocklist(NestedScrollWebView.THIRD_PARTY_REQUESTS, !currentWebView.isBlocklistEnabled(NestedScrollWebView.THIRD_PARTY_REQUESTS));
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.user_agent_firefox_on_android) {  // User Agent - Firefox on Android.
+            // Update the user agent.
+            currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[2]);
 
-                // Update the menu checkbox.
-                menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.THIRD_PARTY_REQUESTS));
+            // Reload the current WebView.
+            currentWebView.reload();
 
-                // Reload the current WebView.
-                currentWebView.reload();
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.user_agent_chrome_on_android) {  // User Agent - Chrome on Android.
+            // Update the user agent.
+            currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[3]);
 
-                // Consume the event.
-                return true;
+            // Reload the current WebView.
+            currentWebView.reload();
 
-            case R.id.proxy_none:
-                // Update the proxy mode.
-                proxyMode = ProxyHelper.NONE;
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.user_agent_safari_on_ios) {  // User Agent - Safari on iOS.
+            // Update the user agent.
+            currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[4]);
 
-                // Apply the proxy mode.
-                applyProxy(true);
+            // Reload the current WebView.
+            currentWebView.reload();
 
-                // Consume the event.
-                return true;
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.user_agent_firefox_on_linux) {  // User Agent - Firefox on Linux.
+            // Update the user agent.
+            currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[5]);
 
-            case R.id.proxy_tor:
-                // Update the proxy mode.
-                proxyMode = ProxyHelper.TOR;
+            // Reload the current WebView.
+            currentWebView.reload();
 
-                // Apply the proxy mode.
-                applyProxy(true);
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.user_agent_chromium_on_linux) {  // User Agent - Chromium on Linux.
+            // Update the user agent.
+            currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[6]);
 
-                // Consume the event.
-                return true;
+            // Reload the current WebView.
+            currentWebView.reload();
 
-            case R.id.proxy_i2p:
-                // Update the proxy mode.
-                proxyMode = ProxyHelper.I2P;
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.user_agent_firefox_on_windows) {  // User Agent - Firefox on Windows.
+            // Update the user agent.
+            currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[7]);
 
-                // Apply the proxy mode.
-                applyProxy(true);
+            // Reload the current WebView.
+            currentWebView.reload();
 
-                // Consume the event.
-                return true;
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.user_agent_chrome_on_windows) {  // User Agent - Chrome on Windows.
+            // Update the user agent.
+            currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[8]);
 
-            case R.id.proxy_custom:
-                // Update the proxy mode.
-                proxyMode = ProxyHelper.CUSTOM;
+            // Reload the current WebView.
+            currentWebView.reload();
 
-                // Apply the proxy mode.
-                applyProxy(true);
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.user_agent_edge_on_windows) {  // User Agent - Edge on Windows.
+            // Update the user agent.
+            currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[9]);
 
-                // Consume the event.
-                return true;
+            // Reload the current WebView.
+            currentWebView.reload();
 
-            case R.id.user_agent_privacy_browser:
-                // Update the user agent.
-                currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[0]);
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.user_agent_internet_explorer_on_windows) {  // User Agent - Internet Explorer on Windows.
+            // Update the user agent.
+            currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[10]);
 
-                // Reload the current WebView.
-                currentWebView.reload();
+            // Reload the current WebView.
+            currentWebView.reload();
 
-                // Consume the event.
-                return true;
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.user_agent_safari_on_macos) {  // User Agent - Safari on macOS.
+            // Update the user agent.
+            currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[11]);
 
-            case R.id.user_agent_webview_default:
-                // Update the user agent.
-                currentWebView.getSettings().setUserAgentString("");
+            // Reload the current WebView.
+            currentWebView.reload();
 
-                // Reload the current WebView.
-                currentWebView.reload();
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.user_agent_custom) {  // User Agent - Custom.
+            // Update the user agent.
+            currentWebView.getSettings().setUserAgentString(sharedPreferences.getString("custom_user_agent", getString(R.string.custom_user_agent_default_value)));
 
-                // Consume the event.
-                return true;
+            // Reload the current WebView.
+            currentWebView.reload();
 
-            case R.id.user_agent_firefox_on_android:
-                // Update the user agent.
-                currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[2]);
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.font_size) {  // Font size.
+            // Instantiate the font size dialog.
+            DialogFragment fontSizeDialogFragment = FontSizeDialog.displayDialog(currentWebView.getSettings().getTextZoom());
 
-                // Reload the current WebView.
-                currentWebView.reload();
+            // Show the font size dialog.
+            fontSizeDialogFragment.show(getSupportFragmentManager(), getString(R.string.font_size));
 
-                // Consume the event.
-                return true;
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.swipe_to_refresh) {  // Swipe to refresh.
+            // Toggle the stored status of swipe to refresh.
+            currentWebView.setSwipeToRefresh(!currentWebView.getSwipeToRefresh());
 
-            case R.id.user_agent_chrome_on_android:
-                // Update the user agent.
-                currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[3]);
+            // Get a handle for the swipe refresh layout.
+            SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout);
 
-                // Reload the current WebView.
-                currentWebView.reload();
-
-                // Consume the event.
-                return true;
-
-            case R.id.user_agent_safari_on_ios:
-                // Update the user agent.
-                currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[4]);
-
-                // Reload the current WebView.
-                currentWebView.reload();
-
-                // Consume the event.
-                return true;
-
-            case R.id.user_agent_firefox_on_linux:
-                // Update the user agent.
-                currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[5]);
-
-                // Reload the current WebView.
-                currentWebView.reload();
-
-                // Consume the event.
-                return true;
-
-            case R.id.user_agent_chromium_on_linux:
-                // Update the user agent.
-                currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[6]);
-
-                // Reload the current WebView.
-                currentWebView.reload();
-
-                // Consume the event.
-                return true;
-
-            case R.id.user_agent_firefox_on_windows:
-                // Update the user agent.
-                currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[7]);
-
-                // Reload the current WebView.
-                currentWebView.reload();
-
-                // Consume the event.
-                return true;
-
-            case R.id.user_agent_chrome_on_windows:
-                // Update the user agent.
-                currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[8]);
-
-                // Reload the current WebView.
-                currentWebView.reload();
-
-                // Consume the event.
-                return true;
-
-            case R.id.user_agent_edge_on_windows:
-                // Update the user agent.
-                currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[9]);
-
-                // Reload the current WebView.
-                currentWebView.reload();
-
-                // Consume the event.
-                return true;
-
-            case R.id.user_agent_internet_explorer_on_windows:
-                // Update the user agent.
-                currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[10]);
-
-                // Reload the current WebView.
-                currentWebView.reload();
-
-                // Consume the event.
-                return true;
-
-            case R.id.user_agent_safari_on_macos:
-                // Update the user agent.
-                currentWebView.getSettings().setUserAgentString(getResources().getStringArray(R.array.user_agent_data)[11]);
-
-                // Reload the current WebView.
-                currentWebView.reload();
+            // Update the swipe refresh layout.
+            if (currentWebView.getSwipeToRefresh()) {  // Swipe to refresh is enabled.
+                // Only enable the swipe refresh layout if the WebView is scrolled to the top.  It is updated every time the scroll changes.
+                swipeRefreshLayout.setEnabled(currentWebView.getY() == 0);
+            } else {  // Swipe to refresh is disabled.
+                // Disable the swipe refresh layout.
+                swipeRefreshLayout.setEnabled(false);
+            }
 
-                // Consume the event.
-                return true;
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.wide_viewport) {  // Wide viewport.
+            // Toggle the viewport.
+            currentWebView.getSettings().setUseWideViewPort(!currentWebView.getSettings().getUseWideViewPort());
 
-            case R.id.user_agent_custom:
-                // Update the user agent.
-                currentWebView.getSettings().setUserAgentString(sharedPreferences.getString("custom_user_agent", getString(R.string.custom_user_agent_default_value)));
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.display_images) {  // Display images.
+            // Toggle the displaying of images.
+            if (currentWebView.getSettings().getLoadsImagesAutomatically()) {  // Images are currently loaded automatically.
+                // Disable loading of images.
+                currentWebView.getSettings().setLoadsImagesAutomatically(false);
 
-                // Reload the current WebView.
+                // Reload the website to remove existing images.
                 currentWebView.reload();
+            } else {  // Images are not currently loaded automatically.
+                // Enable loading of images.  Missing images will be loaded without the need for a reload.
+                currentWebView.getSettings().setLoadsImagesAutomatically(true);
+            }
 
-                // Consume the event.
-                return true;
-
-            case R.id.font_size:
-                // Instantiate the font size dialog.
-                DialogFragment fontSizeDialogFragment = FontSizeDialog.displayDialog(currentWebView.getSettings().getTextZoom());
-
-                // Show the font size dialog.
-                fontSizeDialogFragment.show(getSupportFragmentManager(), getString(R.string.font_size));
-
-                // Consume the event.
-                return true;
-
-            case R.id.swipe_to_refresh:
-                // Toggle the stored status of swipe to refresh.
-                currentWebView.setSwipeToRefresh(!currentWebView.getSwipeToRefresh());
-
-                // Get a handle for the swipe refresh layout.
-                SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout);
-
-                // Update the swipe refresh layout.
-                if (currentWebView.getSwipeToRefresh()) {  // Swipe to refresh is enabled.
-                    // Only enable the swipe refresh layout if the WebView is scrolled to the top.  It is updated every time the scroll changes.
-                    swipeRefreshLayout.setEnabled(currentWebView.getY() == 0);
-                } else {  // Swipe to refresh is disabled.
-                    // Disable the swipe refresh layout.
-                    swipeRefreshLayout.setEnabled(false);
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.dark_webview) {  // Dark WebView.
+            // Check to see if dark WebView is supported by this WebView.
+            if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
+                // Toggle the dark WebView setting.
+                if (WebSettingsCompat.getForceDark(currentWebView.getSettings()) == WebSettingsCompat.FORCE_DARK_ON) {  // Dark WebView is currently enabled.
+                    // Turn off dark WebView.
+                    WebSettingsCompat.setForceDark(currentWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
+                } else {  // Dark WebView is currently disabled.
+                    // Turn on dark WebView.
+                    WebSettingsCompat.setForceDark(currentWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
                 }
+            }
 
-                // Consume the event.
-                return true;
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.find_on_page) {  // Find on page.
+            // Get a handle for the views.
+            Toolbar toolbar = findViewById(R.id.toolbar);
+            LinearLayout findOnPageLinearLayout = findViewById(R.id.find_on_page_linearlayout);
+            EditText findOnPageEditText = findViewById(R.id.find_on_page_edittext);
 
-            case R.id.wide_viewport:
-                // Toggle the viewport.
-                currentWebView.getSettings().setUseWideViewPort(!currentWebView.getSettings().getUseWideViewPort());
+            // Set the minimum height of the find on page linear layout to match the toolbar.
+            findOnPageLinearLayout.setMinimumHeight(toolbar.getHeight());
 
-                // Consume the event.
-                return true;
-
-            case R.id.display_images:
-                if (currentWebView.getSettings().getLoadsImagesAutomatically()) {  // Images are currently loaded automatically.
-                    // Disable loading of images.
-                    currentWebView.getSettings().setLoadsImagesAutomatically(false);
+            // Hide the toolbar.
+            toolbar.setVisibility(View.GONE);
 
-                    // Reload the website to remove existing images.
-                    currentWebView.reload();
-                } else {  // Images are not currently loaded automatically.
-                    // Enable loading of images.  Missing images will be loaded without the need for a reload.
-                    currentWebView.getSettings().setLoadsImagesAutomatically(true);
-                }
+            // Show the find on page linear layout.
+            findOnPageLinearLayout.setVisibility(View.VISIBLE);
 
-                // Consume the event.
-                return true;
+            // Display the keyboard.  The app must wait 200 ms before running the command to work around a bug in Android.
+            // http://stackoverflow.com/questions/5520085/android-show-softkeyboard-with-showsoftinput-is-not-working
+            findOnPageEditText.postDelayed(() -> {
+                // Set the focus on the find on page edit text.
+                findOnPageEditText.requestFocus();
 
-            case R.id.night_mode:
-                // Toggle night mode.
-                currentWebView.setNightMode(!currentWebView.getNightMode());
-
-                // Enable or disable JavaScript according to night mode, the global preference, and any domain settings.
-                if (currentWebView.getNightMode()) {  // Night mode is enabled, which requires JavaScript.
-                    // Enable JavaScript.
-                    currentWebView.getSettings().setJavaScriptEnabled(true);
-                } else if (currentWebView.getDomainSettingsApplied()) {  // Night mode is disabled and domain settings are applied.  Set JavaScript according to the domain settings.
-                    // Apply the JavaScript preference that was stored the last time domain settings were loaded.
-                    currentWebView.getSettings().setJavaScriptEnabled(currentWebView.getDomainSettingsJavaScriptEnabled());
-                } else {  // Night mode is disabled and domain settings are not applied.  Set JavaScript according to the global preference.
-                    // Apply the JavaScript preference.
-                    currentWebView.getSettings().setJavaScriptEnabled(sharedPreferences.getBoolean("javascript", false));
-                }
+                // Get a handle for the input method manager.
+                InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
 
-                // Update the privacy icons.
-                updatePrivacyIcons(false);
+                // Remove the lint warning below that the input method manager might be null.
+                assert inputMethodManager != null;
 
-                // Reload the website.
-                currentWebView.reload();
+                // Display the keyboard.  `0` sets no input flags.
+                inputMethodManager.showSoftInput(findOnPageEditText, 0);
+            }, 200);
 
-                // Consume the event.
-                return true;
-
-            case R.id.find_on_page:
-                // Get a handle for the views.
-                Toolbar toolbar = findViewById(R.id.toolbar);
-                LinearLayout findOnPageLinearLayout = findViewById(R.id.find_on_page_linearlayout);
-                EditText findOnPageEditText = findViewById(R.id.find_on_page_edittext);
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.print) {  // Print.
+            // Get a print manager instance.
+            PrintManager printManager = (PrintManager) getSystemService(Context.PRINT_SERVICE);
 
-                // Set the minimum height of the find on page linear layout to match the toolbar.
-                findOnPageLinearLayout.setMinimumHeight(toolbar.getHeight());
+            // Remove the lint error below that print manager might be null.
+            assert printManager != null;
 
-                // Hide the toolbar.
-                toolbar.setVisibility(View.GONE);
+            // Create a print document adapter from the current WebView.
+            PrintDocumentAdapter printDocumentAdapter = currentWebView.createPrintDocumentAdapter();
 
-                // Show the find on page linear layout.
-                findOnPageLinearLayout.setVisibility(View.VISIBLE);
+            // Print the document.
+            printManager.print(getString(R.string.privacy_browser_web_page), printDocumentAdapter, null);
 
-                // Display the keyboard.  The app must wait 200 ms before running the command to work around a bug in Android.
-                // http://stackoverflow.com/questions/5520085/android-show-softkeyboard-with-showsoftinput-is-not-working
-                findOnPageEditText.postDelayed(() -> {
-                    // Set the focus on `findOnPageEditText`.
-                    findOnPageEditText.requestFocus();
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.save_url) {  // Save URL.
+            // Prepare the save dialog.  The dialog will be displayed once the file size and the content disposition have been acquired.
+            new PrepareSaveDialog(this, this, getSupportFragmentManager(), StoragePermissionDialog.SAVE_URL, currentWebView.getSettings().getUserAgentString(),
+                    currentWebView.getAcceptFirstPartyCookies()).execute(currentWebView.getCurrentUrl());
 
-                    // Get a handle for the input method manager.
-                    InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.save_archive) {  // Save archive.
+            // Instantiate the save dialog.
+            DialogFragment saveArchiveFragment = SaveWebpageDialog.saveWebpage(StoragePermissionDialog.SAVE_ARCHIVE, null, null, getString(R.string.webpage_mht), null,
+                    false);
 
-                    // Remove the lint warning below that the input method manager might be null.
-                    assert inputMethodManager != null;
+            // Show the save dialog.  It must be named `save_dialog` so that the file picker can update the file name.
+            saveArchiveFragment.show(getSupportFragmentManager(), getString(R.string.save_dialog));
 
-                    // Display the keyboard.  `0` sets no input flags.
-                    inputMethodManager.showSoftInput(findOnPageEditText, 0);
-                }, 200);
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.save_image) {  // Save image.
+            // Instantiate the save dialog.
+            DialogFragment saveImageFragment = SaveWebpageDialog.saveWebpage(StoragePermissionDialog.SAVE_IMAGE, null, null, getString(R.string.webpage_png), null,
+                    false);
 
-                // Consume the event.
-                return true;
+            // Show the save dialog.  It must be named `save_dialog` so that the file picker can update the file name.
+            saveImageFragment.show(getSupportFragmentManager(), getString(R.string.save_dialog));
 
-            case R.id.print:
-                // Get a print manager instance.
-                PrintManager printManager = (PrintManager) getSystemService(Context.PRINT_SERVICE);
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.add_to_homescreen) {  // Add to homescreen.
+            // Instantiate the create home screen shortcut dialog.
+            DialogFragment createHomeScreenShortcutDialogFragment = CreateHomeScreenShortcutDialog.createDialog(currentWebView.getTitle(), currentWebView.getUrl(),
+                    currentWebView.getFavoriteOrDefaultIcon());
 
-                // Remove the lint error below that print manager might be null.
-                assert printManager != null;
+            // Show the create home screen shortcut dialog.
+            createHomeScreenShortcutDialogFragment.show(getSupportFragmentManager(), getString(R.string.create_shortcut));
 
-                // Create a print document adapter from the current WebView.
-                PrintDocumentAdapter printDocumentAdapter = currentWebView.createPrintDocumentAdapter();
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.view_source) {  // View source.
+            // Create an intent to launch the view source activity.
+            Intent viewSourceIntent = new Intent(this, ViewSourceActivity.class);
 
-                // Print the document.
-                printManager.print(getString(R.string.privacy_browser_web_page), printDocumentAdapter, null);
+            // Add the variables to the intent.
+            viewSourceIntent.putExtra("user_agent", currentWebView.getSettings().getUserAgentString());
+            viewSourceIntent.putExtra("current_url", currentWebView.getUrl());
 
-                // Consume the event.
-                return true;
+            // Make it so.
+            startActivity(viewSourceIntent);
 
-            case R.id.save_url:
-                // Prepare the save dialog.  The dialog will be displayed once the file size and the content disposition have been acquired.
-                new PrepareSaveDialog(this, this, getSupportFragmentManager(), StoragePermissionDialog.SAVE_URL, currentWebView.getSettings().getUserAgentString(),
-                        currentWebView.getAcceptFirstPartyCookies()).execute(currentWebView.getCurrentUrl());
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.share_url) {  // Share URL.
+            // Setup the share string.
+            String shareString = currentWebView.getTitle() + " â€“ " + currentWebView.getUrl();
 
-                // Consume the event.
-                return true;
+            // Create the share intent.
+            Intent shareIntent = new Intent(Intent.ACTION_SEND);
 
-            case R.id.save_as_archive:
-                // Prepare the save dialog.  The dialog will be displayed once the file size and the content disposition have been acquired.
-                new PrepareSaveDialog(this, this, getSupportFragmentManager(), StoragePermissionDialog.SAVE_AS_ARCHIVE, currentWebView.getSettings().getUserAgentString(),
-                        currentWebView.getAcceptFirstPartyCookies()).execute(currentWebView.getCurrentUrl());
+            // Add the share string to the intent.
+            shareIntent.putExtra(Intent.EXTRA_TEXT, shareString);
 
-                // Consume the event.
-                return true;
+            // Set the MIME type.
+            shareIntent.setType("text/plain");
 
-            case R.id.save_as_image:
-                // Prepare the save dialog.  The dialog will be displayed once the file size adn the content disposition have been acquired.
-                new PrepareSaveDialog(this, this, getSupportFragmentManager(), StoragePermissionDialog.SAVE_AS_IMAGE, currentWebView.getSettings().getUserAgentString(),
-                        currentWebView.getAcceptFirstPartyCookies()).execute(currentWebView.getCurrentUrl());
+            // Set the intent to open in a new task.
+            shareIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 
-                // Consume the event.
-                return true;
+            // Make it so.
+            startActivity(Intent.createChooser(shareIntent, getString(R.string.share_url)));
 
-            case R.id.add_to_homescreen:
-                // Instantiate the create home screen shortcut dialog.
-                DialogFragment createHomeScreenShortcutDialogFragment = CreateHomeScreenShortcutDialog.createDialog(currentWebView.getTitle(), currentWebView.getUrl(),
-                        currentWebView.getFavoriteOrDefaultIcon());
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.open_with_app) {  // Open with app.
+            // Open the URL with an outside app.
+            openWithApp(currentWebView.getUrl());
 
-                // Show the create home screen shortcut dialog.
-                createHomeScreenShortcutDialogFragment.show(getSupportFragmentManager(), getString(R.string.create_shortcut));
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.open_with_browser) {  // Open with browser.
+            // Open the URL with an outside browser.
+            openWithBrowser(currentWebView.getUrl());
 
-                // Consume the event.
-                return true;
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.add_or_edit_domain) {  // Add or edit domain.
+            // Check if domain settings currently exist.
+            if (currentWebView.getDomainSettingsApplied()) {  // Edit the current domain settings.
+                // Reapply the domain settings on returning to `MainWebViewActivity`.
+                reapplyDomainSettingsOnRestart = true;
 
-            case R.id.view_source:
-                // Create an intent to launch the view source activity.
-                Intent viewSourceIntent = new Intent(this, ViewSourceActivity.class);
+                // Create an intent to launch the domains activity.
+                Intent domainsIntent = new Intent(this, DomainsActivity.class);
 
-                // Add the variables to the intent.
-                viewSourceIntent.putExtra("user_agent", currentWebView.getSettings().getUserAgentString());
-                viewSourceIntent.putExtra("current_url", currentWebView.getUrl());
+                // Add the extra information to the intent.
+                domainsIntent.putExtra("load_domain", currentWebView.getDomainSettingsDatabaseId());
+                domainsIntent.putExtra("close_on_back", true);
+                domainsIntent.putExtra("current_url", currentWebView.getUrl());
 
-                // Make it so.
-                startActivity(viewSourceIntent);
+                // Get the current certificate.
+                SslCertificate sslCertificate = currentWebView.getCertificate();
 
-                // Consume the event.
-                return true;
+                // Check to see if the SSL certificate is populated.
+                if (sslCertificate != null) {
+                    // Extract the certificate to strings.
+                    String issuedToCName = sslCertificate.getIssuedTo().getCName();
+                    String issuedToOName = sslCertificate.getIssuedTo().getOName();
+                    String issuedToUName = sslCertificate.getIssuedTo().getUName();
+                    String issuedByCName = sslCertificate.getIssuedBy().getCName();
+                    String issuedByOName = sslCertificate.getIssuedBy().getOName();
+                    String issuedByUName = sslCertificate.getIssuedBy().getUName();
+                    long startDateLong = sslCertificate.getValidNotBeforeDate().getTime();
+                    long endDateLong = sslCertificate.getValidNotAfterDate().getTime();
 
-            case R.id.share_url:
-                // Setup the share string.
-                String shareString = currentWebView.getTitle() + " â€“ " + currentWebView.getUrl();
+                    // Add the certificate to the intent.
+                    domainsIntent.putExtra("ssl_issued_to_cname", issuedToCName);
+                    domainsIntent.putExtra("ssl_issued_to_oname", issuedToOName);
+                    domainsIntent.putExtra("ssl_issued_to_uname", issuedToUName);
+                    domainsIntent.putExtra("ssl_issued_by_cname", issuedByCName);
+                    domainsIntent.putExtra("ssl_issued_by_oname", issuedByOName);
+                    domainsIntent.putExtra("ssl_issued_by_uname", issuedByUName);
+                    domainsIntent.putExtra("ssl_start_date", startDateLong);
+                    domainsIntent.putExtra("ssl_end_date", endDateLong);
+                }
 
-                // Create the share intent.
-                Intent shareIntent = new Intent(Intent.ACTION_SEND);
-                shareIntent.putExtra(Intent.EXTRA_TEXT, shareString);
-                shareIntent.setType("text/plain");
+                // Check to see if the current IP addresses have been received.
+                if (currentWebView.hasCurrentIpAddresses()) {
+                    // Add the current IP addresses to the intent.
+                    domainsIntent.putExtra("current_ip_addresses", currentWebView.getCurrentIpAddresses());
+                }
 
                 // Make it so.
-                startActivity(Intent.createChooser(shareIntent, getString(R.string.share_url)));
-
-                // Consume the event.
-                return true;
-
-            case R.id.open_with_app:
-                // Open the URL with an outside app.
-                openWithApp(currentWebView.getUrl());
+                startActivity(domainsIntent);
+            } else {  // Add a new domain.
+                // Apply the new domain settings on returning to `MainWebViewActivity`.
+                reapplyDomainSettingsOnRestart = true;
 
-                // Consume the event.
-                return true;
+                // Get the current domain
+                Uri currentUri = Uri.parse(currentWebView.getUrl());
+                String currentDomain = currentUri.getHost();
 
-            case R.id.open_with_browser:
-                // Open the URL with an outside browser.
-                openWithBrowser(currentWebView.getUrl());
+                // Initialize the database handler.  The `0` specifies the database version, but that is ignored and set instead using a constant in `DomainsDatabaseHelper`.
+                DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(this, null, null, 0);
 
-                // Consume the event.
-                return true;
+                // Create the domain and store the database ID.
+                int newDomainDatabaseId = domainsDatabaseHelper.addDomain(currentDomain);
 
-            case R.id.add_or_edit_domain:
-                if (currentWebView.getDomainSettingsApplied()) {  // Edit the current domain settings.
-                    // Reapply the domain settings on returning to `MainWebViewActivity`.
-                    reapplyDomainSettingsOnRestart = true;
-
-                    // Create an intent to launch the domains activity.
-                    Intent domainsIntent = new Intent(this, DomainsActivity.class);
-
-                    // Add the extra information to the intent.
-                    domainsIntent.putExtra("load_domain", currentWebView.getDomainSettingsDatabaseId());
-                    domainsIntent.putExtra("close_on_back", true);
-                    domainsIntent.putExtra("current_url", currentWebView.getUrl());
-
-                    // Get the current certificate.
-                    SslCertificate sslCertificate = currentWebView.getCertificate();
-
-                    // Check to see if the SSL certificate is populated.
-                    if (sslCertificate != null) {
-                        // Extract the certificate to strings.
-                        String issuedToCName = sslCertificate.getIssuedTo().getCName();
-                        String issuedToOName = sslCertificate.getIssuedTo().getOName();
-                        String issuedToUName = sslCertificate.getIssuedTo().getUName();
-                        String issuedByCName = sslCertificate.getIssuedBy().getCName();
-                        String issuedByOName = sslCertificate.getIssuedBy().getOName();
-                        String issuedByUName = sslCertificate.getIssuedBy().getUName();
-                        long startDateLong = sslCertificate.getValidNotBeforeDate().getTime();
-                        long endDateLong = sslCertificate.getValidNotAfterDate().getTime();
-
-                        // Add the certificate to the intent.
-                        domainsIntent.putExtra("ssl_issued_to_cname", issuedToCName);
-                        domainsIntent.putExtra("ssl_issued_to_oname", issuedToOName);
-                        domainsIntent.putExtra("ssl_issued_to_uname", issuedToUName);
-                        domainsIntent.putExtra("ssl_issued_by_cname", issuedByCName);
-                        domainsIntent.putExtra("ssl_issued_by_oname", issuedByOName);
-                        domainsIntent.putExtra("ssl_issued_by_uname", issuedByUName);
-                        domainsIntent.putExtra("ssl_start_date", startDateLong);
-                        domainsIntent.putExtra("ssl_end_date", endDateLong);
-                    }
+                // Create an intent to launch the domains activity.
+                Intent domainsIntent = new Intent(this, DomainsActivity.class);
 
-                    // Check to see if the current IP addresses have been received.
-                    if (currentWebView.hasCurrentIpAddresses()) {
-                        // Add the current IP addresses to the intent.
-                        domainsIntent.putExtra("current_ip_addresses", currentWebView.getCurrentIpAddresses());
-                    }
+                // Add the extra information to the intent.
+                domainsIntent.putExtra("load_domain", newDomainDatabaseId);
+                domainsIntent.putExtra("close_on_back", true);
+                domainsIntent.putExtra("current_url", currentWebView.getUrl());
 
-                    // Make it so.
-                    startActivity(domainsIntent);
-                } else {  // Add a new domain.
-                    // Apply the new domain settings on returning to `MainWebViewActivity`.
-                    reapplyDomainSettingsOnRestart = true;
-
-                    // Get the current domain
-                    Uri currentUri = Uri.parse(currentWebView.getUrl());
-                    String currentDomain = currentUri.getHost();
-
-                    // Initialize the database handler.  The `0` specifies the database version, but that is ignored and set instead using a constant in `DomainsDatabaseHelper`.
-                    DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(this, null, null, 0);
-
-                    // Create the domain and store the database ID.
-                    int newDomainDatabaseId = domainsDatabaseHelper.addDomain(currentDomain);
-
-                    // Create an intent to launch the domains activity.
-                    Intent domainsIntent = new Intent(this, DomainsActivity.class);
-
-                    // Add the extra information to the intent.
-                    domainsIntent.putExtra("load_domain", newDomainDatabaseId);
-                    domainsIntent.putExtra("close_on_back", true);
-                    domainsIntent.putExtra("current_url", currentWebView.getUrl());
-
-                    // Get the current certificate.
-                    SslCertificate sslCertificate = currentWebView.getCertificate();
-
-                    // Check to see if the SSL certificate is populated.
-                    if (sslCertificate != null) {
-                        // Extract the certificate to strings.
-                        String issuedToCName = sslCertificate.getIssuedTo().getCName();
-                        String issuedToOName = sslCertificate.getIssuedTo().getOName();
-                        String issuedToUName = sslCertificate.getIssuedTo().getUName();
-                        String issuedByCName = sslCertificate.getIssuedBy().getCName();
-                        String issuedByOName = sslCertificate.getIssuedBy().getOName();
-                        String issuedByUName = sslCertificate.getIssuedBy().getUName();
-                        long startDateLong = sslCertificate.getValidNotBeforeDate().getTime();
-                        long endDateLong = sslCertificate.getValidNotAfterDate().getTime();
-
-                        // Add the certificate to the intent.
-                        domainsIntent.putExtra("ssl_issued_to_cname", issuedToCName);
-                        domainsIntent.putExtra("ssl_issued_to_oname", issuedToOName);
-                        domainsIntent.putExtra("ssl_issued_to_uname", issuedToUName);
-                        domainsIntent.putExtra("ssl_issued_by_cname", issuedByCName);
-                        domainsIntent.putExtra("ssl_issued_by_oname", issuedByOName);
-                        domainsIntent.putExtra("ssl_issued_by_uname", issuedByUName);
-                        domainsIntent.putExtra("ssl_start_date", startDateLong);
-                        domainsIntent.putExtra("ssl_end_date", endDateLong);
-                    }
+                // Get the current certificate.
+                SslCertificate sslCertificate = currentWebView.getCertificate();
 
-                    // Check to see if the current IP addresses have been received.
-                    if (currentWebView.hasCurrentIpAddresses()) {
-                        // Add the current IP addresses to the intent.
-                        domainsIntent.putExtra("current_ip_addresses", currentWebView.getCurrentIpAddresses());
-                    }
+                // Check to see if the SSL certificate is populated.
+                if (sslCertificate != null) {
+                    // Extract the certificate to strings.
+                    String issuedToCName = sslCertificate.getIssuedTo().getCName();
+                    String issuedToOName = sslCertificate.getIssuedTo().getOName();
+                    String issuedToUName = sslCertificate.getIssuedTo().getUName();
+                    String issuedByCName = sslCertificate.getIssuedBy().getCName();
+                    String issuedByOName = sslCertificate.getIssuedBy().getOName();
+                    String issuedByUName = sslCertificate.getIssuedBy().getUName();
+                    long startDateLong = sslCertificate.getValidNotBeforeDate().getTime();
+                    long endDateLong = sslCertificate.getValidNotAfterDate().getTime();
 
-                    // Make it so.
-                    startActivity(domainsIntent);
+                    // Add the certificate to the intent.
+                    domainsIntent.putExtra("ssl_issued_to_cname", issuedToCName);
+                    domainsIntent.putExtra("ssl_issued_to_oname", issuedToOName);
+                    domainsIntent.putExtra("ssl_issued_to_uname", issuedToUName);
+                    domainsIntent.putExtra("ssl_issued_by_cname", issuedByCName);
+                    domainsIntent.putExtra("ssl_issued_by_oname", issuedByOName);
+                    domainsIntent.putExtra("ssl_issued_by_uname", issuedByUName);
+                    domainsIntent.putExtra("ssl_start_date", startDateLong);
+                    domainsIntent.putExtra("ssl_end_date", endDateLong);
                 }
 
-                // Consume the event.
-                return true;
+                // Check to see if the current IP addresses have been received.
+                if (currentWebView.hasCurrentIpAddresses()) {
+                    // Add the current IP addresses to the intent.
+                    domainsIntent.putExtra("current_ip_addresses", currentWebView.getCurrentIpAddresses());
+                }
 
-            case R.id.ad_consent:
-                // Instantiate the ad consent dialog.
-                DialogFragment adConsentDialogFragment = new AdConsentDialog();
+                // Make it so.
+                startActivity(domainsIntent);
+            }
 
-                // Display the ad consent dialog.
-                adConsentDialogFragment.show(getSupportFragmentManager(), getString(R.string.ad_consent));
+            // Consume the event.
+            return true;
+        } else if (menuItemId == R.id.ad_consent) {  // Ad consent.
+            // Instantiate the ad consent dialog.
+            DialogFragment adConsentDialogFragment = new AdConsentDialog();
 
-                // Consume the event.
-                return true;
+            // Display the ad consent dialog.
+            adConsentDialogFragment.show(getSupportFragmentManager(), getString(R.string.ad_consent));
 
-            default:
-                // Don't consume the event.
-                return super.onOptionsItemSelected(menuItem);
+            // Consume the event.
+            return true;
+        } else {  // There is no match with the options menu.  Pass the event up to the parent method.
+            // Don't consume the event.
+            return super.onOptionsItemSelected(menuItem);
         }
     }
 
     // removeAllCookies is deprecated, but it is required for API < 21.
     @Override
     public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) {
-        // Get the menu item ID.
-        int menuItemId = menuItem.getItemId();
-
         // Get a handle for the shared preferences.
         SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
 
-        // Run the commands that correspond to the selected menu item.
-        switch (menuItemId) {
-            case R.id.clear_and_exit:
-                // Clear and exit Privacy Browser.
-                clearAndExit();
-                break;
-
-            case R.id.home:
-                // Load the homepage.
-                loadUrl(currentWebView, sharedPreferences.getString("homepage", getString(R.string.homepage_default_value)));
-                break;
-
-            case R.id.back:
-                if (currentWebView.canGoBack()) {
-                    // Get the current web back forward list.
-                    WebBackForwardList webBackForwardList = currentWebView.copyBackForwardList();
-
-                    // Get the previous entry URL.
-                    String previousUrl = webBackForwardList.getItemAtIndex(webBackForwardList.getCurrentIndex() - 1).getUrl();
-
-                    // Apply the domain settings.
-                    applyDomainSettings(currentWebView, previousUrl, false, false);
-
-                    // Load the previous website in the history.
-                    currentWebView.goBack();
-                }
-                break;
-
-            case R.id.forward:
-                if (currentWebView.canGoForward()) {
-                    // Get the current web back forward list.
-                    WebBackForwardList webBackForwardList = currentWebView.copyBackForwardList();
-
-                    // Get the next entry URL.
-                    String nextUrl = webBackForwardList.getItemAtIndex(webBackForwardList.getCurrentIndex() + 1).getUrl();
-
-                    // Apply the domain settings.
-                    applyDomainSettings(currentWebView, nextUrl, false, false);
-
-                    // Load the next website in the history.
-                    currentWebView.goForward();
-                }
-                break;
-
-            case R.id.history:
-                // Instantiate the URL history dialog.
-                DialogFragment urlHistoryDialogFragment = UrlHistoryDialog.loadBackForwardList(currentWebView.getWebViewFragmentId());
-
-                // Show the URL history dialog.
-                urlHistoryDialogFragment.show(getSupportFragmentManager(), getString(R.string.history));
-                break;
-
-            case R.id.open:
-                // Instantiate the open file dialog.
-                DialogFragment openDialogFragment = new OpenDialog();
-
-                // Show the open file dialog.
-                openDialogFragment.show(getSupportFragmentManager(), getString(R.string.open));
-                break;
-
-            case R.id.requests:
-                // Populate the resource requests.
-                RequestsActivity.resourceRequests = currentWebView.getResourceRequests();
+        // Get the menu item ID.
+        int menuItemId = menuItem.getItemId();
 
-                // Create an intent to launch the Requests activity.
-                Intent requestsIntent = new Intent(this, RequestsActivity.class);
+        // Run the commands that correspond to the selected menu item.
+        if (menuItemId == R.id.clear_and_exit) {  // Clear and exit.
+            // Clear and exit Privacy Browser.
+            clearAndExit();
+        } else if (menuItemId == R.id.home) {  // Home.
+            // Load the homepage.
+            loadUrl(currentWebView, sharedPreferences.getString("homepage", getString(R.string.homepage_default_value)));
+        } else if (menuItemId == R.id.back) {  // Back.
+            // Check if the WebView can go back.
+            if (currentWebView.canGoBack()) {
+                // Get the current web back forward list.
+                WebBackForwardList webBackForwardList = currentWebView.copyBackForwardList();
+
+                // Get the previous entry URL.
+                String previousUrl = webBackForwardList.getItemAtIndex(webBackForwardList.getCurrentIndex() - 1).getUrl();
 
-                // Add the block third-party requests status to the intent.
-                requestsIntent.putExtra("block_all_third_party_requests", currentWebView.isBlocklistEnabled(NestedScrollWebView.THIRD_PARTY_REQUESTS));
+                // Apply the domain settings.
+                applyDomainSettings(currentWebView, previousUrl, false, false, false);
 
-                // Make it so.
-                startActivity(requestsIntent);
-                break;
+                // Load the previous website in the history.
+                currentWebView.goBack();
+            }
+        } else if (menuItemId == R.id.forward) {  // Forward.
+            // Check if the WebView can go forward.
+            if (currentWebView.canGoForward()) {
+                // Get the current web back forward list.
+                WebBackForwardList webBackForwardList = currentWebView.copyBackForwardList();
 
-            case R.id.downloads:
-                // Launch the system Download Manager.
-                Intent downloadManagerIntent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS);
+                // Get the next entry URL.
+                String nextUrl = webBackForwardList.getItemAtIndex(webBackForwardList.getCurrentIndex() + 1).getUrl();
 
-                // Launch as a new task so that Download Manager and Privacy Browser show as separate windows in the recent tasks list.
-                downloadManagerIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                // Apply the domain settings.
+                applyDomainSettings(currentWebView, nextUrl, false, false, false);
 
-                // Make it so.
-                startActivity(downloadManagerIntent);
-                break;
+                // Load the next website in the history.
+                currentWebView.goForward();
+            }
+        } else if (menuItemId == R.id.history) {  // History.
+            // Instantiate the URL history dialog.
+            DialogFragment urlHistoryDialogFragment = UrlHistoryDialog.loadBackForwardList(currentWebView.getWebViewFragmentId());
 
-            case R.id.domains:
-                // Set the flag to reapply the domain settings on restart when returning from Domain Settings.
-                reapplyDomainSettingsOnRestart = true;
+            // Show the URL history dialog.
+            urlHistoryDialogFragment.show(getSupportFragmentManager(), getString(R.string.history));
+        } else if (menuItemId == R.id.open) {  // Open.
+            // Instantiate the open file dialog.
+            DialogFragment openDialogFragment = new OpenDialog();
 
-                // Launch the domains activity.
-                Intent domainsIntent = new Intent(this, DomainsActivity.class);
+            // Show the open file dialog.
+            openDialogFragment.show(getSupportFragmentManager(), getString(R.string.open));
+        } else if (menuItemId == R.id.requests) {  // Requests.
+            // Populate the resource requests.
+            RequestsActivity.resourceRequests = currentWebView.getResourceRequests();
 
-                // Add the extra information to the intent.
-                domainsIntent.putExtra("current_url", currentWebView.getUrl());
+            // Create an intent to launch the Requests activity.
+            Intent requestsIntent = new Intent(this, RequestsActivity.class);
 
-                // Get the current certificate.
-                SslCertificate sslCertificate = currentWebView.getCertificate();
+            // Add the block third-party requests status to the intent.
+            requestsIntent.putExtra("block_all_third_party_requests", currentWebView.isBlocklistEnabled(NestedScrollWebView.THIRD_PARTY_REQUESTS));
 
-                // Check to see if the SSL certificate is populated.
-                if (sslCertificate != null) {
-                    // Extract the certificate to strings.
-                    String issuedToCName = sslCertificate.getIssuedTo().getCName();
-                    String issuedToOName = sslCertificate.getIssuedTo().getOName();
-                    String issuedToUName = sslCertificate.getIssuedTo().getUName();
-                    String issuedByCName = sslCertificate.getIssuedBy().getCName();
-                    String issuedByOName = sslCertificate.getIssuedBy().getOName();
-                    String issuedByUName = sslCertificate.getIssuedBy().getUName();
-                    long startDateLong = sslCertificate.getValidNotBeforeDate().getTime();
-                    long endDateLong = sslCertificate.getValidNotAfterDate().getTime();
+            // Make it so.
+            startActivity(requestsIntent);
+        } else if (menuItemId == R.id.downloads) {  // Downloads.
+            // Launch the system Download Manager.
+            Intent downloadManagerIntent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS);
 
-                    // Add the certificate to the intent.
-                    domainsIntent.putExtra("ssl_issued_to_cname", issuedToCName);
-                    domainsIntent.putExtra("ssl_issued_to_oname", issuedToOName);
-                    domainsIntent.putExtra("ssl_issued_to_uname", issuedToUName);
-                    domainsIntent.putExtra("ssl_issued_by_cname", issuedByCName);
-                    domainsIntent.putExtra("ssl_issued_by_oname", issuedByOName);
-                    domainsIntent.putExtra("ssl_issued_by_uname", issuedByUName);
-                    domainsIntent.putExtra("ssl_start_date", startDateLong);
-                    domainsIntent.putExtra("ssl_end_date", endDateLong);
-                }
+            // Launch as a new task so that Download Manager and Privacy Browser show as separate windows in the recent tasks list.
+            downloadManagerIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 
-                // Check to see if the current IP addresses have been received.
-                if (currentWebView.hasCurrentIpAddresses()) {
-                    // Add the current IP addresses to the intent.
-                    domainsIntent.putExtra("current_ip_addresses", currentWebView.getCurrentIpAddresses());
-                }
+            // Make it so.
+            startActivity(downloadManagerIntent);
+        } else if (menuItemId == R.id.domains) {  // Domains.
+            // Set the flag to reapply the domain settings on restart when returning from Domain Settings.
+            reapplyDomainSettingsOnRestart = true;
 
-                // Make it so.
-                startActivity(domainsIntent);
-                break;
+            // Launch the domains activity.
+            Intent domainsIntent = new Intent(this, DomainsActivity.class);
 
-            case R.id.settings:
-                // Set the flag to reapply app settings on restart when returning from Settings.
-                reapplyAppSettingsOnRestart = true;
+            // Add the extra information to the intent.
+            domainsIntent.putExtra("current_url", currentWebView.getUrl());
+
+            // Get the current certificate.
+            SslCertificate sslCertificate = currentWebView.getCertificate();
+
+            // Check to see if the SSL certificate is populated.
+            if (sslCertificate != null) {
+                // Extract the certificate to strings.
+                String issuedToCName = sslCertificate.getIssuedTo().getCName();
+                String issuedToOName = sslCertificate.getIssuedTo().getOName();
+                String issuedToUName = sslCertificate.getIssuedTo().getUName();
+                String issuedByCName = sslCertificate.getIssuedBy().getCName();
+                String issuedByOName = sslCertificate.getIssuedBy().getOName();
+                String issuedByUName = sslCertificate.getIssuedBy().getUName();
+                long startDateLong = sslCertificate.getValidNotBeforeDate().getTime();
+                long endDateLong = sslCertificate.getValidNotAfterDate().getTime();
+
+                // Add the certificate to the intent.
+                domainsIntent.putExtra("ssl_issued_to_cname", issuedToCName);
+                domainsIntent.putExtra("ssl_issued_to_oname", issuedToOName);
+                domainsIntent.putExtra("ssl_issued_to_uname", issuedToUName);
+                domainsIntent.putExtra("ssl_issued_by_cname", issuedByCName);
+                domainsIntent.putExtra("ssl_issued_by_oname", issuedByOName);
+                domainsIntent.putExtra("ssl_issued_by_uname", issuedByUName);
+                domainsIntent.putExtra("ssl_start_date", startDateLong);
+                domainsIntent.putExtra("ssl_end_date", endDateLong);
+            }
 
-                // Set the flag to reapply the domain settings on restart when returning from Settings.
-                reapplyDomainSettingsOnRestart = true;
+            // Check to see if the current IP addresses have been received.
+            if (currentWebView.hasCurrentIpAddresses()) {
+                // Add the current IP addresses to the intent.
+                domainsIntent.putExtra("current_ip_addresses", currentWebView.getCurrentIpAddresses());
+            }
 
-                // Launch the settings activity.
-                Intent settingsIntent = new Intent(this, SettingsActivity.class);
-                startActivity(settingsIntent);
-                break;
+            // Make it so.
+            startActivity(domainsIntent);
+        } else if (menuItemId == R.id.settings) {  // Settings.
+            // Set the flag to reapply app settings on restart when returning from Settings.
+            reapplyAppSettingsOnRestart = true;
 
-            case R.id.import_export:
-                // Launch the import/export activity.
-                Intent importExportIntent = new Intent (this, ImportExportActivity.class);
-                startActivity(importExportIntent);
-                break;
+            // Set the flag to reapply the domain settings on restart when returning from Settings.
+            reapplyDomainSettingsOnRestart = true;
 
-            case R.id.logcat:
-                // Launch the logcat activity.
-                Intent logcatIntent = new Intent(this, LogcatActivity.class);
-                startActivity(logcatIntent);
-                break;
+            // Launch the settings activity.
+            Intent settingsIntent = new Intent(this, SettingsActivity.class);
+            startActivity(settingsIntent);
+        } else if (menuItemId == R.id.import_export) { // Import/Export.
+            // Create an intent to launch the import/export activity.
+            Intent importExportIntent = new Intent(this, ImportExportActivity.class);
 
-            case R.id.guide:
-                // Launch `GuideActivity`.
-                Intent guideIntent = new Intent(this, GuideActivity.class);
-                startActivity(guideIntent);
-                break;
+            // Make it so.
+            startActivity(importExportIntent);
+        } else if (menuItemId == R.id.logcat) {  // Logcat.
+            // Create an intent to launch the logcat activity.
+            Intent logcatIntent = new Intent(this, LogcatActivity.class);
+
+            // Make it so.
+            startActivity(logcatIntent);
+        } else if (menuItemId == R.id.guide) {  // Guide.
+            // Create an intent to launch the guide activity.
+            Intent guideIntent = new Intent(this, GuideActivity.class);
 
-            case R.id.about:
-                // Create an intent to launch the about activity.
-                Intent aboutIntent = new Intent(this, AboutActivity.class);
+            // Make it so.
+            startActivity(guideIntent);
+        } else if (menuItemId == R.id.about) {  // About
+            // Create an intent to launch the about activity.
+            Intent aboutIntent = new Intent(this, AboutActivity.class);
 
-                // Create a string array for the blocklist versions.
-                String[] blocklistVersions = new String[] {easyList.get(0).get(0)[0], easyPrivacy.get(0).get(0)[0], fanboysAnnoyanceList.get(0).get(0)[0], fanboysSocialList.get(0).get(0)[0],
-                        ultraList.get(0).get(0)[0], ultraPrivacy.get(0).get(0)[0]};
+            // Create a string array for the blocklist versions.
+            String[] blocklistVersions = new String[]{easyList.get(0).get(0)[0], easyPrivacy.get(0).get(0)[0], fanboysAnnoyanceList.get(0).get(0)[0], fanboysSocialList.get(0).get(0)[0],
+                    ultraList.get(0).get(0)[0], ultraPrivacy.get(0).get(0)[0]};
 
-                // Add the blocklist versions to the intent.
-                aboutIntent.putExtra("blocklist_versions", blocklistVersions);
+            // Add the blocklist versions to the intent.
+            aboutIntent.putExtra("blocklist_versions", blocklistVersions);
 
-                // Make it so.
-                startActivity(aboutIntent);
-                break;
+            // Make it so.
+            startActivity(aboutIntent);
         }
 
-        // Get a handle for the drawer layout.
-        DrawerLayout drawerLayout = findViewById(R.id.drawerlayout);
-
         // Close the navigation drawer.
         drawerLayout.closeDrawer(GravityCompat.START);
         return true;
@@ -2053,7 +2068,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
     @Override
     public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
-        // Store the hit test result.
+        // Get the hit test result.
         final WebView.HitTestResult hitTestResult = currentWebView.getHitTestResult();
 
         // Define the URL strings.
@@ -2141,8 +2156,17 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 // Get the image URL.
                 imageUrl = hitTestResult.getExtra();
 
-                // Set the image URL as the title of the context menu.
-                menu.setHeaderTitle(imageUrl);
+                // Remove the incorrect lint warning below that the image URL might be null.
+                assert imageUrl != null;
+
+                // Set the context menu title.
+                if (imageUrl.startsWith("data:")) {  // The image data is contained in within the URL, making it exceedingly long.
+                    // Truncate the image URL before making it the title.
+                    menu.setHeaderTitle(imageUrl.substring(0, 100));
+                } else {  // The image URL does not contain the full image data.
+                    // Set the image URL as the title of the context menu.
+                    menu.setHeaderTitle(imageUrl);
+                }
 
                 // Add an Open in New Tab entry.
                 menu.add(R.string.open_image_in_new_tab).setOnMenuItemClickListener((MenuItem item) -> {
@@ -2334,8 +2358,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                     // `FLAG_ACTIVITY_NEW_TASK` opens the email program in a new task instead as part of Privacy Browser.
                     emailIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 
-                    // Make it so.
-                    startActivity(emailIntent);
+                    try {
+                        // Make it so.
+                        startActivity(emailIntent);
+                    } catch (ActivityNotFoundException exception) {
+                        // Display a snackbar.
+                        Snackbar.make(currentWebView, getString(R.string.error) + "  " + exception, Snackbar.LENGTH_INDEFINITE).show();
+                    }
 
                     // Consume the event.
                     return true;
@@ -2470,13 +2499,16 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
     @Override
     public void onSaveBookmarkFolder(DialogFragment dialogFragment, int selectedFolderDatabaseId, Bitmap favoriteIconBitmap) {
+        // Remove the incorrect lint warning below that the dialog fragment might be null.
+        assert dialogFragment != null;
+
         // Get the dialog.
         Dialog dialog = dialogFragment.getDialog();
 
         // Remove the incorrect lint warning below that the dialog might be null.
         assert dialog != null;
 
-        // Get handles for the views from `dialogFragment`.
+        // Get handles for the views from the dialog.
         EditText editFolderNameEditText = dialog.findViewById(R.id.edit_folder_name_edittext);
         RadioButton currentFolderIconRadioButton = dialog.findViewById(R.id.edit_folder_current_icon_radiobutton);
         RadioButton defaultFolderIconRadioButton = dialog.findViewById(R.id.edit_folder_default_icon_radiobutton);
@@ -2556,27 +2588,16 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         bookmarksCursorAdapter.changeCursor(bookmarksCursor);
     }
 
-    // Override `onBackPressed` to handle the navigation drawer and and the WebViews.
+    // Override `onBackPressed()` to handle the navigation drawer and and the WebViews.
     @Override
     public void onBackPressed() {
-        // Get a handle for the drawer layout and the tab layout.
-        DrawerLayout drawerLayout = findViewById(R.id.drawerlayout);
-        TabLayout tabLayout = findViewById(R.id.tablayout);
-
+        // Check the different options for processing `back`.
         if (drawerLayout.isDrawerVisible(GravityCompat.START)) {  // The navigation drawer is open.
             // Close the navigation drawer.
             drawerLayout.closeDrawer(GravityCompat.START);
         } else if (drawerLayout.isDrawerVisible(GravityCompat.END)){  // The bookmarks drawer is open.
-            if (currentBookmarksFolder.isEmpty()) {  // The home folder is displayed.
-                // close the bookmarks drawer.
-                drawerLayout.closeDrawer(GravityCompat.END);
-            } else {  // A subfolder is displayed.
-                // Place the former parent folder in `currentFolder`.
-                currentBookmarksFolder = bookmarksDatabaseHelper.getParentFolderName(currentBookmarksFolder);
-
-                // Load the new folder.
-                loadBookmarksFolder();
-            }
+            // close the bookmarks drawer.
+            drawerLayout.closeDrawer(GravityCompat.END);
         } else if (displayingFullScreenVideo) {  // A full screen video is shown.
             // Get a handle for the layouts.
             FrameLayout rootFrameLayout = findViewById(R.id.root_framelayout);
@@ -2640,7 +2661,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             String previousUrl = webBackForwardList.getItemAtIndex(webBackForwardList.getCurrentIndex() - 1).getUrl();
 
             // Apply the domain settings.
-            applyDomainSettings(currentWebView, previousUrl, false, false);
+            applyDomainSettings(currentWebView, previousUrl, false, false, false);
 
             // Go back.
             currentWebView.goBack();
@@ -2826,11 +2847,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Sanitize the URL.
         url = sanitizeUrl(url);
 
-        // Apply the domain settings.
-        applyDomainSettings(nestedScrollWebView, url, true, false);
-
-        // Load the URL.
-        nestedScrollWebView.loadUrl(url, customHeaders);
+        // Apply the domain settings and load the URL.
+        applyDomainSettings(nestedScrollWebView, url, true, false, true);
     }
 
     public void findPreviousOnPage(View view) {
@@ -2875,6 +2893,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
     @Override
     public void onApplyNewFontSize(DialogFragment dialogFragment) {
+        // Remove the incorrect lint warning below that the dialog fragment might be null.
+        assert dialogFragment != null;
+
         // Get the dialog.
         Dialog dialog = dialogFragment.getDialog();
 
@@ -2912,6 +2933,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Get the file path string.
         openFilePath = fileNameEditText.getText().toString();
 
+        // Apply the domain settings.  This resets the favorite icon and removes any domain settings.
+        applyDomainSettings(currentWebView, "file://" + openFilePath, true, false, false);
+
         // Check to see if the storage permission is needed.
         if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {  // The storage permission has been granted.
             // Open the file.
@@ -2940,14 +2964,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                     storagePermissionDialogFragment.show(getSupportFragmentManager(), getString(R.string.storage_permission));
                 } else {  // Show the permission request directly.
                     // Request the write external storage permission.  The file will be opened when it finishes.
-                    ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_OPEN_REQUEST_CODE);
+                    ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, StoragePermissionDialog.OPEN);
                 }
             }
         }
     }
 
     @Override
-    public void onSaveWebpage(int saveType, DialogFragment dialogFragment) {
+    public void onSaveWebpage(int saveType, String originalUrlString, DialogFragment dialogFragment) {
         // Get the dialog.
         Dialog dialog = dialogFragment.getDialog();
 
@@ -2958,8 +2982,16 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         EditText urlEditText = dialog.findViewById(R.id.url_edittext);
         EditText fileNameEditText = dialog.findViewById(R.id.file_name_edittext);
 
-        // Get the strings from the edit texts.
-        saveWebpageUrl = urlEditText.getText().toString();
+        // Store the URL.
+        if ((originalUrlString != null) && originalUrlString.startsWith("data:")) {
+            // Save the original URL.
+            saveWebpageUrl = originalUrlString;
+        } else {
+            // Get the URL from the edit text, which may have been modified.
+            saveWebpageUrl = urlEditText.getText().toString();
+        }
+
+        // Get the file path from the edit text.
         saveWebpageFilePath = fileNameEditText.getText().toString();
 
         // Check to see if the storage permission is needed.
@@ -2971,16 +3003,20 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                     new SaveUrl(this, this, saveWebpageFilePath, currentWebView.getSettings().getUserAgentString(), currentWebView.getAcceptFirstPartyCookies()).execute(saveWebpageUrl);
                     break;
 
-                case StoragePermissionDialog.SAVE_AS_ARCHIVE:
+                case StoragePermissionDialog.SAVE_ARCHIVE:
                     // Save the webpage archive.
-                    currentWebView.saveWebArchive(saveWebpageFilePath);
+                    saveWebpageArchive(saveWebpageFilePath);
                     break;
 
-                case StoragePermissionDialog.SAVE_AS_IMAGE:
+                case StoragePermissionDialog.SAVE_IMAGE:
                     // Save the webpage image.
-                    new SaveWebpageImage(this, currentWebView).execute(saveWebpageFilePath);
+                    new SaveWebpageImage(this, this, saveWebpageFilePath, currentWebView).execute();
                     break;
             }
+
+            // Reset the strings.
+            saveWebpageUrl = "";
+            saveWebpageFilePath = "";
         } else {  // The storage permission has not been granted.
             // Get the external private directory file.
             File externalPrivateDirectoryFile = getExternalFilesDir(null);
@@ -3000,286 +3036,97 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                         new SaveUrl(this, this, saveWebpageFilePath, currentWebView.getSettings().getUserAgentString(), currentWebView.getAcceptFirstPartyCookies()).execute(saveWebpageUrl);
                         break;
 
-                    case StoragePermissionDialog.SAVE_AS_ARCHIVE:
-                        // Save the webpage archive.
-                        currentWebView.saveWebArchive(saveWebpageFilePath);
-                        break;
-
-                    case StoragePermissionDialog.SAVE_AS_IMAGE:
-                        // Save the webpage image.
-                        new SaveWebpageImage(this, currentWebView).execute(saveWebpageFilePath);
-                        break;
-                }
-            } else {  // The file path is in a public directory.
-                // Check if the user has previously denied the storage permission.
-                if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {  // Show a dialog explaining the request first.
-                    // Instantiate the storage permission alert dialog.
-                    DialogFragment storagePermissionDialogFragment = StoragePermissionDialog.displayDialog(saveType);
-
-                    // Show the storage permission alert dialog.  The permission will be requested when the dialog is closed.
-                    storagePermissionDialogFragment.show(getSupportFragmentManager(), getString(R.string.storage_permission));
-                } else {  // Show the permission request directly.
-                    switch (saveType) {
-                        case StoragePermissionDialog.SAVE_URL:
-                            // Request the write external storage permission.  The URL will be saved when it finishes.
-                            ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_SAVE_URL_REQUEST_CODE);
-
-                        case StoragePermissionDialog.SAVE_AS_ARCHIVE:
-                            // Request the write external storage permission.  The webpage archive will be saved when it finishes.
-                            ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_SAVE_AS_ARCHIVE_REQUEST_CODE);
-                            break;
-
-                        case StoragePermissionDialog.SAVE_AS_IMAGE:
-                            // Request the write external storage permission.  The webpage image will be saved when it finishes.
-                            ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_SAVE_AS_IMAGE_REQUEST_CODE);
-                            break;
-                    }
-                }
-            }
-        }
-    }
-
-    @Override
-    public void onCloseStoragePermissionDialog(int requestType) {
-        switch (requestType) {
-            case StoragePermissionDialog.OPEN:
-                // Request the write external storage permission.  The file will be opened when it finishes.
-                ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_OPEN_REQUEST_CODE);
-                break;
-
-            case StoragePermissionDialog.SAVE_URL:
-                // Request the write external storage permission.  The URL will be saved when it finishes.
-                ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_SAVE_URL_REQUEST_CODE);
-                break;
-
-            case StoragePermissionDialog.SAVE_AS_ARCHIVE:
-                // Request the write external storage permission.  The webpage archive will be saved when it finishes.
-                ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_SAVE_AS_ARCHIVE_REQUEST_CODE);
-                break;
-
-            case StoragePermissionDialog.SAVE_AS_IMAGE:
-                // Request the write external storage permission.  The webpage image will be saved when it finishes.
-                ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_SAVE_AS_IMAGE_REQUEST_CODE);
-                break;
-        }
-    }
-
-    @Override
-    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
-        //Only process the results if they exist (this method is triggered when a dialog is presented the first time for an app, but no grant results are included).
-        if (grantResults.length > 0) {
-            switch (requestCode) {
-                case PERMISSION_OPEN_REQUEST_CODE:
-                    // Check to see if the storage permission was granted.  If the dialog was canceled the grant results will be empty.
-                    if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {  // The storage permission was granted.
-                        // Load the file.
-                        currentWebView.loadUrl("file://" + openFilePath);
-                    } else {  // The storage permission was not granted.
-                        // Display an error snackbar.
-                        Snackbar.make(currentWebView, getString(R.string.cannot_use_location), Snackbar.LENGTH_LONG).show();
-                    }
-
-                    // Reset the open file path.
-                    openFilePath = "";
-                    break;
-
-                case PERMISSION_SAVE_URL_REQUEST_CODE:
-                    // Check to see if the storage permission was granted.  If the dialog was canceled the grant results will be empty.
-                    if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {  // The storage permission was granted.
-                        // Save the raw URL.
-                        new SaveUrl(this, this, saveWebpageFilePath, currentWebView.getSettings().getUserAgentString(), currentWebView.getAcceptFirstPartyCookies()).execute(saveWebpageUrl);
-                    } else {  // The storage permission was not granted.
-                        // Display an error snackbar.
-                        Snackbar.make(currentWebView, getString(R.string.cannot_use_location), Snackbar.LENGTH_LONG).show();
-                    }
-
-                    // Reset the save strings.
-                    saveWebpageUrl = "";
-                    saveWebpageFilePath = "";
-                    break;
-
-                case PERMISSION_SAVE_AS_ARCHIVE_REQUEST_CODE:
-                    // Check to see if the storage permission was granted.  If the dialog was canceled the grant results will be empty.
-                    if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {  // The storage permission was granted.
-                        // Save the webpage archive.
-                        currentWebView.saveWebArchive(saveWebpageFilePath);
-                    } else {  // The storage permission was not granted.
-                        // Display an error snackbar.
-                        Snackbar.make(currentWebView, getString(R.string.cannot_use_location), Snackbar.LENGTH_LONG).show();
-                    }
-
-                    // Reset the save webpage file path.
-                    saveWebpageFilePath = "";
-                    break;
-
-                case PERMISSION_SAVE_AS_IMAGE_REQUEST_CODE:
-                    // Check to see if the storage permission was granted.  If the dialog was canceled the grant results will be empty.
-                    if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {  // The storage permission was granted.
-                        // Save the webpage image.
-                        new SaveWebpageImage(this, currentWebView).execute(saveWebpageFilePath);
-                    } else {  // The storage permission was not granted.
-                        // Display an error snackbar.
-                        Snackbar.make(currentWebView, getString(R.string.cannot_use_location), Snackbar.LENGTH_LONG).show();
-                    }
-
-                    // Reset the save webpage file path.
-                    saveWebpageFilePath = "";
-                    break;
-            }
-        }
-    }
-
-    private void applyAppSettings() {
-        // Initialize the app if this is the first run.  This is done here instead of in `onCreate()` to shorten the time that an unthemed background is displayed on app startup.
-        if (webViewDefaultUserAgent == null) {
-            initializeApp();
-        }
-
-        // Get a handle for the shared preferences.
-        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
-
-        // Store the values from the shared preferences in variables.
-        incognitoModeEnabled = sharedPreferences.getBoolean("incognito_mode", false);
-        boolean doNotTrackEnabled = sharedPreferences.getBoolean("do_not_track", false);
-        sanitizeGoogleAnalytics = sharedPreferences.getBoolean("google_analytics", true);
-        sanitizeFacebookClickIds = sharedPreferences.getBoolean("facebook_click_ids", true);
-        sanitizeTwitterAmpRedirects = sharedPreferences.getBoolean("twitter_amp_redirects", true);
-        proxyMode = sharedPreferences.getString("proxy", getString(R.string.proxy_default_value));
-        fullScreenBrowsingModeEnabled = sharedPreferences.getBoolean("full_screen_browsing_mode", false);
-        hideAppBar = sharedPreferences.getBoolean("hide_app_bar", true);
-        scrollAppBar = sharedPreferences.getBoolean("scroll_app_bar", true);
-
-        // Get the search string.
-        String searchString = sharedPreferences.getString("search", getString(R.string.search_default_value));
-
-        // Set the search string.
-        if (searchString.equals("Custom URL")) {  // A custom search string is used.
-            searchURL = sharedPreferences.getString("search_custom_url", getString(R.string.search_custom_url_default_value));
-        } else {  // A custom search string is not used.
-            searchURL = searchString;
-        }
-
-        // Get a handle for the app compat delegate.
-        AppCompatDelegate appCompatDelegate = getDelegate();
-
-        // Get handles for the views that need to be modified.
-        FrameLayout rootFrameLayout = findViewById(R.id.root_framelayout);
-        AppBarLayout appBarLayout = findViewById(R.id.appbar_layout);
-        ActionBar actionBar = appCompatDelegate.getSupportActionBar();
-        Toolbar toolbar = findViewById(R.id.toolbar);
-        LinearLayout findOnPageLinearLayout = findViewById(R.id.find_on_page_linearlayout);
-        LinearLayout tabsLinearLayout = findViewById(R.id.tabs_linearlayout);
-        SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout);
-
-        // Remove the incorrect lint warning below that the action bar might be null.
-        assert actionBar != null;
-
-        // Apply the proxy.
-        applyProxy(false);
-
-        // Set Do Not Track status.
-        if (doNotTrackEnabled) {
-            customHeaders.put("DNT", "1");
-        } else {
-            customHeaders.remove("DNT");
-        }
-
-        // Get the current layout parameters.  Using coordinator layout parameters allows the `setBehavior()` command and using app bar layout parameters allows the `setScrollFlags()` command.
-        CoordinatorLayout.LayoutParams swipeRefreshLayoutParams = (CoordinatorLayout.LayoutParams) swipeRefreshLayout.getLayoutParams();
-        AppBarLayout.LayoutParams toolbarLayoutParams = (AppBarLayout.LayoutParams) toolbar.getLayoutParams();
-        AppBarLayout.LayoutParams findOnPageLayoutParams = (AppBarLayout.LayoutParams) findOnPageLinearLayout.getLayoutParams();
-        AppBarLayout.LayoutParams tabsLayoutParams = (AppBarLayout.LayoutParams) tabsLinearLayout.getLayoutParams();
-
-        // Add the scrolling behavior to the layout parameters.
-        if (scrollAppBar) {
-            // Enable scrolling of the app bar.
-            swipeRefreshLayoutParams.setBehavior(new AppBarLayout.ScrollingViewBehavior());
-            toolbarLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP);
-            findOnPageLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP);
-            tabsLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP);
-        } else {
-            // Disable scrolling of the app bar.
-            swipeRefreshLayoutParams.setBehavior(null);
-            toolbarLayoutParams.setScrollFlags(0);
-            findOnPageLayoutParams.setScrollFlags(0);
-            tabsLayoutParams.setScrollFlags(0);
-
-            // Expand the app bar if it is currently collapsed.
-            appBarLayout.setExpanded(true);
-        }
-
-        // Apply the modified layout parameters.
-        swipeRefreshLayout.setLayoutParams(swipeRefreshLayoutParams);
-        toolbar.setLayoutParams(toolbarLayoutParams);
-        findOnPageLinearLayout.setLayoutParams(findOnPageLayoutParams);
-        tabsLinearLayout.setLayoutParams(tabsLayoutParams);
-
-        // Set the app bar scrolling for each WebView.
-        for (int i = 0; i < webViewPagerAdapter.getCount(); i++) {
-            // Get the WebView tab fragment.
-            WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(i);
-
-            // Get the fragment view.
-            View fragmentView = webViewTabFragment.getView();
-
-            // Only modify the WebViews if they exist.
-            if (fragmentView != null) {
-                // Get the nested scroll WebView from the tab fragment.
-                NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
-
-                // Set the app bar scrolling.
-                nestedScrollWebView.setNestedScrollingEnabled(scrollAppBar);
-            }
-        }
+                    case StoragePermissionDialog.SAVE_ARCHIVE:
+                        // Save the webpage archive.
+                        saveWebpageArchive(saveWebpageFilePath);
+                        break;
 
-        // Update the full screen browsing mode settings.
-        if (fullScreenBrowsingModeEnabled && inFullScreenBrowsingMode) {  // Privacy Browser is currently in full screen browsing mode.
-            // Update the visibility of the app bar, which might have changed in the settings.
-            if (hideAppBar) {
-                // Hide the tab linear layout.
-                tabsLinearLayout.setVisibility(View.GONE);
+                    case StoragePermissionDialog.SAVE_IMAGE:
+                        // Save the webpage image.
+                        new SaveWebpageImage(this, this, saveWebpageFilePath, currentWebView).execute();
+                        break;
+                }
 
-                // Hide the action bar.
-                actionBar.hide();
-            } else {
-                // Show the tab linear layout.
-                tabsLinearLayout.setVisibility(View.VISIBLE);
+                // Reset the strings.
+                saveWebpageUrl = "";
+                saveWebpageFilePath = "";
+            } else {  // The file path is in a public directory.
+                // Check if the user has previously denied the storage permission.
+                if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {  // Show a dialog explaining the request first.
+                    // Instantiate the storage permission alert dialog.
+                    DialogFragment storagePermissionDialogFragment = StoragePermissionDialog.displayDialog(saveType);
 
-                // Show the action bar.
-                actionBar.show();
+                    // Show the storage permission alert dialog.  The permission will be requested when the dialog is closed.
+                    storagePermissionDialogFragment.show(getSupportFragmentManager(), getString(R.string.storage_permission));
+                } else {  // Show the permission request directly.
+                    // Request the write external storage permission according to the save type.  The URL will be saved when it finishes.
+                    ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, saveType);
+                }
             }
+        }
+    }
 
-            // Hide the banner ad in the free flavor.
-            if (BuildConfig.FLAVOR.contentEquals("free")) {
-                AdHelper.hideAd(findViewById(R.id.adview));
-            }
+    @Override
+    public void onCloseStoragePermissionDialog(int requestType) {
+        // Request the write external storage permission according to the request type.  The file will be opened when it finishes.
+        ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, requestType);
 
-            /* Hide the system bars.
-             * SYSTEM_UI_FLAG_FULLSCREEN hides the status bar at the top of the screen.
-             * SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN makes the root frame layout fill the area that is normally reserved for the status bar.
-             * SYSTEM_UI_FLAG_HIDE_NAVIGATION hides the navigation bar on the bottom or right of the screen.
-             * SYSTEM_UI_FLAG_IMMERSIVE_STICKY makes the status and navigation bars translucent and automatically re-hides them after they are shown.
-             */
-            rootFrameLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
-                    View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
-        } else {  // Privacy Browser is not in full screen browsing mode.
-            // Reset the full screen tracker, which could be true if Privacy Browser was in full screen mode before entering settings and full screen browsing was disabled.
-            inFullScreenBrowsingMode = false;
+    }
 
-            // Show the tab linear layout.
-            tabsLinearLayout.setVisibility(View.VISIBLE);
+    @Override
+    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+        //Only process the results if they exist (this method is triggered when a dialog is presented the first time for an app, but no grant results are included).
+        if (grantResults.length > 0) {
+            switch (requestCode) {
+                case StoragePermissionDialog.OPEN:
+                    // Check to see if the storage permission was granted.  If the dialog was canceled the grant results will be empty.
+                    if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {  // The storage permission was granted.
+                        // Load the file.
+                        currentWebView.loadUrl("file://" + openFilePath);
+                    } else {  // The storage permission was not granted.
+                        // Display an error snackbar.
+                        Snackbar.make(currentWebView, getString(R.string.cannot_use_location), Snackbar.LENGTH_LONG).show();
+                    }
+                    break;
 
-            // Show the action bar.
-            actionBar.show();
+                case StoragePermissionDialog.SAVE_URL:
+                    // Check to see if the storage permission was granted.  If the dialog was canceled the grant results will be empty.
+                    if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {  // The storage permission was granted.
+                        // Save the raw URL.
+                        new SaveUrl(this, this, saveWebpageFilePath, currentWebView.getSettings().getUserAgentString(), currentWebView.getAcceptFirstPartyCookies()).execute(saveWebpageUrl);
+                    } else {  // The storage permission was not granted.
+                        // Display an error snackbar.
+                        Snackbar.make(currentWebView, getString(R.string.cannot_use_location), Snackbar.LENGTH_LONG).show();
+                    }
+                    break;
 
-            // Show the banner ad in the free flavor.
-            if (BuildConfig.FLAVOR.contentEquals("free")) {
-                // Initialize the ads.  If this isn't the first run, `loadAd()` will be automatically called instead.
-                AdHelper.initializeAds(findViewById(R.id.adview), getApplicationContext(), getSupportFragmentManager(), getString(R.string.google_app_id), getString(R.string.ad_unit_id));
+                case StoragePermissionDialog.SAVE_ARCHIVE:
+                    // Check to see if the storage permission was granted.  If the dialog was canceled the grant results will be empty.
+                    if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {  // The storage permission was granted.
+                        // Save the webpage archive.
+                        saveWebpageArchive(saveWebpageFilePath);
+                    } else {  // The storage permission was not granted.
+                        // Display an error snackbar.
+                        Snackbar.make(currentWebView, getString(R.string.cannot_use_location), Snackbar.LENGTH_LONG).show();
+                    }
+                    break;
+
+                case StoragePermissionDialog.SAVE_IMAGE:
+                    // Check to see if the storage permission was granted.  If the dialog was canceled the grant results will be empty.
+                    if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {  // The storage permission was granted.
+                        // Save the webpage image.
+                        new SaveWebpageImage(this, this, saveWebpageFilePath, currentWebView).execute();
+                    } else {  // The storage permission was not granted.
+                        // Display an error snackbar.
+                        Snackbar.make(currentWebView, getString(R.string.cannot_use_location), Snackbar.LENGTH_LONG).show();
+                    }
+                    break;
             }
 
-            // Remove the `SYSTEM_UI` flags from the root frame layout.
-            rootFrameLayout.setSystemUiVisibility(0);
+            // Reset the strings.
+            openFilePath = "";
+            saveWebpageUrl = "";
+            saveWebpageFilePath = "";
         }
     }
 
@@ -3290,11 +3137,20 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Remove the lint warning below that the input method manager might be null.
         assert inputMethodManager != null;
 
-        // Initialize the foreground color spans for highlighting the URLs.  We have to use the deprecated `getColor()` until API >= 23.
-        redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_a700));
+        // Initialize the gray foreground color spans for highlighting the URLs.  The deprecated `getResources()` must be used until API >= 23.
         initialGrayColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.gray_500));
         finalGrayColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.gray_500));
 
+        // Get the current theme status.
+        int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
+
+        // Set the red color span according to the theme.
+        if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
+            redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_a700));
+        } else {
+            redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_900));
+        }
+
         // Get handles for the URL views.
         EditText urlEditText = findViewById(R.id.url_edittext);
 
@@ -3386,11 +3242,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         this.registerReceiver(orbotStatusBroadcastReceiver, new IntentFilter("org.torproject.android.intent.action.STATUS"));
 
         // Get handles for views that need to be modified.
-        DrawerLayout drawerLayout = findViewById(R.id.drawerlayout);
         NavigationView navigationView = findViewById(R.id.navigationview);
-        TabLayout tabLayout = findViewById(R.id.tablayout);
         SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout);
-        ViewPager webViewPager = findViewById(R.id.webviewpager);
         ListView bookmarksListView = findViewById(R.id.bookmarks_drawer_listview);
         FloatingActionButton launchBookmarksActivityFab = findViewById(R.id.launch_bookmarks_activity_fab);
         FloatingActionButton createBookmarkFolderFab = findViewById(R.id.create_bookmark_folder_fab);
@@ -3400,12 +3253,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Listen for touches on the navigation menu.
         navigationView.setNavigationItemSelectedListener(this);
 
-        // Get handles for the navigation menu and the back and forward menu items.  The menu is 0 based.
+        // Get handles for the navigation menu and the back and forward menu items.
         Menu navigationMenu = navigationView.getMenu();
-        MenuItem navigationBackMenuItem = navigationMenu.getItem(2);
-        MenuItem navigationForwardMenuItem = navigationMenu.getItem(3);
-        MenuItem navigationHistoryMenuItem = navigationMenu.getItem(4);
-        MenuItem navigationRequestsMenuItem = navigationMenu.getItem(6);
+        MenuItem navigationBackMenuItem = navigationMenu.findItem(R.id.back);
+        MenuItem navigationForwardMenuItem = navigationMenu.findItem(R.id.forward);
+        MenuItem navigationHistoryMenuItem = navigationMenu.findItem(R.id.history);
+        MenuItem navigationRequestsMenuItem = navigationMenu.findItem(R.id.requests);
 
         // Update the web view pager every time a tab is modified.
         webViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@@ -3422,13 +3275,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 // Set the current WebView.
                 setCurrentWebView(position);
 
-                // Select the corresponding tab if it does not match the currently selected page.  This will happen if the page was scrolled via swiping in the view pager or by creating a new tab.
+                // Select the corresponding tab if it does not match the currently selected page.  This will happen if the page was scrolled by creating a new tab.
                 if (tabLayout.getSelectedTabPosition() != position) {
-                    // Create a handler to select the tab.
-                    Handler selectTabHandler = new Handler();
-
-                    // Create a runnable to select the tab.
-                    Runnable selectTabRunnable = () -> {
+                    // Wait until the new tab has been created.
+                    tabLayout.post(() -> {
                         // Get a handle for the tab.
                         TabLayout.Tab tab = tabLayout.getTabAt(position);
 
@@ -3437,10 +3287,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                         // Select the tab.
                         tab.select();
-                    };
-
-                    // Select the tab layout after 150 milliseconds, which leaves enough time for a new tab to be inflated.
-                    selectTabHandler.postDelayed(selectTabRunnable, 150);
+                    });
                 }
             }
 
@@ -3560,14 +3407,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         defaultProgressViewStartOffset = swipeRefreshLayout.getProgressViewStartOffset();
         defaultProgressViewEndOffset = swipeRefreshLayout.getProgressViewEndOffset();
 
-        // Get the current theme status.
-        int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
-
         // Set the refresh color scheme according to the theme.
-        if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
-            swipeRefreshLayout.setColorSchemeResources(R.color.blue_500);
-        } else {
+        if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
             swipeRefreshLayout.setColorSchemeResources(R.color.blue_700);
+        } else {
+            swipeRefreshLayout.setColorSchemeResources(R.color.violet_500);
         }
 
         // Initialize a color background typed value.
@@ -3696,20 +3540,173 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Inflate a bare WebView to get the default user agent.  It is not used to render content on the screen.
         @SuppressLint("InflateParams") View webViewLayout = getLayoutInflater().inflate(R.layout.bare_webview, null, false);
 
-        // Get a handle for the WebView.
-        WebView bareWebView = webViewLayout.findViewById(R.id.bare_webview);
+        // Get a handle for the WebView.
+        WebView bareWebView = webViewLayout.findViewById(R.id.bare_webview);
+
+        // Store the default user agent.
+        webViewDefaultUserAgent = bareWebView.getSettings().getUserAgentString();
+
+        // Destroy the bare WebView.
+        bareWebView.destroy();
+    }
+
+    private void applyAppSettings() {
+        // Get a handle for the shared preferences.
+        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+
+        // Store the values from the shared preferences in variables.
+        incognitoModeEnabled = sharedPreferences.getBoolean("incognito_mode", false);
+        boolean doNotTrackEnabled = sharedPreferences.getBoolean("do_not_track", false);
+        sanitizeGoogleAnalytics = sharedPreferences.getBoolean("google_analytics", true);
+        sanitizeFacebookClickIds = sharedPreferences.getBoolean("facebook_click_ids", true);
+        sanitizeTwitterAmpRedirects = sharedPreferences.getBoolean("twitter_amp_redirects", true);
+        proxyMode = sharedPreferences.getString("proxy", getString(R.string.proxy_default_value));
+        fullScreenBrowsingModeEnabled = sharedPreferences.getBoolean("full_screen_browsing_mode", false);
+        hideAppBar = sharedPreferences.getBoolean("hide_app_bar", true);
+        scrollAppBar = sharedPreferences.getBoolean("scroll_app_bar", true);
+
+        // Apply the saved proxy mode if the app has been restarted.
+        if (savedProxyMode != null) {
+            // Apply the saved proxy mode.
+            proxyMode = savedProxyMode;
+
+            // Reset the saved proxy mode.
+            savedProxyMode = null;
+        }
+
+        // Get the search string.
+        String searchString = sharedPreferences.getString("search", getString(R.string.search_default_value));
+
+        // Set the search string.
+        if (searchString.equals("Custom URL")) {  // A custom search string is used.
+            searchURL = sharedPreferences.getString("search_custom_url", getString(R.string.search_custom_url_default_value));
+        } else {  // A custom search string is not used.
+            searchURL = searchString;
+        }
+
+        // Get a handle for the app compat delegate.
+        AppCompatDelegate appCompatDelegate = getDelegate();
+
+        // Get handles for the views that need to be modified.
+        FrameLayout rootFrameLayout = findViewById(R.id.root_framelayout);
+        ActionBar actionBar = appCompatDelegate.getSupportActionBar();
+
+        // Remove the incorrect lint warning below that the action bar might be null.
+        assert actionBar != null;
+
+        // Apply the proxy.
+        applyProxy(false);
+
+        // Set Do Not Track status.
+        if (doNotTrackEnabled) {
+            customHeaders.put("DNT", "1");
+        } else {
+            customHeaders.remove("DNT");
+        }
+
+        // Get the current layout parameters.  Using coordinator layout parameters allows the `setBehavior()` command and using app bar layout parameters allows the `setScrollFlags()` command.
+        CoordinatorLayout.LayoutParams swipeRefreshLayoutParams = (CoordinatorLayout.LayoutParams) swipeRefreshLayout.getLayoutParams();
+        AppBarLayout.LayoutParams toolbarLayoutParams = (AppBarLayout.LayoutParams) toolbar.getLayoutParams();
+        AppBarLayout.LayoutParams findOnPageLayoutParams = (AppBarLayout.LayoutParams) findOnPageLinearLayout.getLayoutParams();
+        AppBarLayout.LayoutParams tabsLayoutParams = (AppBarLayout.LayoutParams) tabsLinearLayout.getLayoutParams();
+
+        // Add the scrolling behavior to the layout parameters.
+        if (scrollAppBar) {
+            // Enable scrolling of the app bar.
+            swipeRefreshLayoutParams.setBehavior(new AppBarLayout.ScrollingViewBehavior());
+            toolbarLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP);
+            findOnPageLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP);
+            tabsLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP);
+        } else {
+            // Disable scrolling of the app bar.
+            swipeRefreshLayoutParams.setBehavior(null);
+            toolbarLayoutParams.setScrollFlags(0);
+            findOnPageLayoutParams.setScrollFlags(0);
+            tabsLayoutParams.setScrollFlags(0);
+
+            // Expand the app bar if it is currently collapsed.
+            appBarLayout.setExpanded(true);
+        }
+
+        // Apply the modified layout parameters.
+        swipeRefreshLayout.setLayoutParams(swipeRefreshLayoutParams);
+        toolbar.setLayoutParams(toolbarLayoutParams);
+        findOnPageLinearLayout.setLayoutParams(findOnPageLayoutParams);
+        tabsLinearLayout.setLayoutParams(tabsLayoutParams);
+
+        // Set the app bar scrolling for each WebView.
+        for (int i = 0; i < webViewPagerAdapter.getCount(); i++) {
+            // Get the WebView tab fragment.
+            WebViewTabFragment webViewTabFragment = webViewPagerAdapter.getPageFragment(i);
+
+            // Get the fragment view.
+            View fragmentView = webViewTabFragment.getView();
+
+            // Only modify the WebViews if they exist.
+            if (fragmentView != null) {
+                // Get the nested scroll WebView from the tab fragment.
+                NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
+
+                // Set the app bar scrolling.
+                nestedScrollWebView.setNestedScrollingEnabled(scrollAppBar);
+            }
+        }
+
+        // Update the full screen browsing mode settings.
+        if (fullScreenBrowsingModeEnabled && inFullScreenBrowsingMode) {  // Privacy Browser is currently in full screen browsing mode.
+            // Update the visibility of the app bar, which might have changed in the settings.
+            if (hideAppBar) {
+                // Hide the tab linear layout.
+                tabsLinearLayout.setVisibility(View.GONE);
+
+                // Hide the action bar.
+                actionBar.hide();
+            } else {
+                // Show the tab linear layout.
+                tabsLinearLayout.setVisibility(View.VISIBLE);
+
+                // Show the action bar.
+                actionBar.show();
+            }
+
+            // Hide the banner ad in the free flavor.
+            if (BuildConfig.FLAVOR.contentEquals("free")) {
+                AdHelper.hideAd(findViewById(R.id.adview));
+            }
+
+            /* Hide the system bars.
+             * SYSTEM_UI_FLAG_FULLSCREEN hides the status bar at the top of the screen.
+             * SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN makes the root frame layout fill the area that is normally reserved for the status bar.
+             * SYSTEM_UI_FLAG_HIDE_NAVIGATION hides the navigation bar on the bottom or right of the screen.
+             * SYSTEM_UI_FLAG_IMMERSIVE_STICKY makes the status and navigation bars translucent and automatically re-hides them after they are shown.
+             */
+            rootFrameLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
+                    View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
+        } else {  // Privacy Browser is not in full screen browsing mode.
+            // Reset the full screen tracker, which could be true if Privacy Browser was in full screen mode before entering settings and full screen browsing was disabled.
+            inFullScreenBrowsingMode = false;
+
+            // Show the tab linear layout.
+            tabsLinearLayout.setVisibility(View.VISIBLE);
 
-        // Store the default user agent.
-        webViewDefaultUserAgent = bareWebView.getSettings().getUserAgentString();
+            // Show the action bar.
+            actionBar.show();
 
-        // Destroy the bare WebView.
-        bareWebView.destroy();
+            // Show the banner ad in the free flavor.
+            if (BuildConfig.FLAVOR.contentEquals("free")) {
+                // Initialize the ads.  If this isn't the first run, `loadAd()` will be automatically called instead.
+                AdHelper.initializeAds(findViewById(R.id.adview), getApplicationContext(), getSupportFragmentManager(), getString(R.string.google_app_id), getString(R.string.ad_unit_id));
+            }
+
+            // Remove the `SYSTEM_UI` flags from the root frame layout.
+            rootFrameLayout.setSystemUiVisibility(0);
+        }
     }
 
     @Override
     public void navigateHistory(String url, int steps) {
         // Apply the domain settings.
-        applyDomainSettings(currentWebView, url, false, false);
+        applyDomainSettings(currentWebView, url, false, false, false);
 
         // Load the history entry.
         currentWebView.goBackOrForward(steps);
@@ -3724,7 +3721,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         String previousUrl = webBackForwardList.getItemAtIndex(webBackForwardList.getCurrentIndex() - 1).getUrl();
 
         // Apply the domain settings.
-        applyDomainSettings(currentWebView, previousUrl, false, false);
+        applyDomainSettings(currentWebView, previousUrl, false, false, false);
 
         // Go back.
         currentWebView.goBack();
@@ -3732,10 +3729,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
     // `reloadWebsite` is used if returning from the Domains activity.  Otherwise JavaScript might not function correctly if it is newly enabled.
     @SuppressLint("SetJavaScriptEnabled")
-    private boolean applyDomainSettings(NestedScrollWebView nestedScrollWebView, String url, boolean resetTab, boolean reloadWebsite) {
-        // Store a copy of the current user agent to track changes for the return boolean.
-        String initialUserAgent = nestedScrollWebView.getSettings().getUserAgentString();
-
+    private void applyDomainSettings(NestedScrollWebView nestedScrollWebView, String url, boolean resetTab, boolean reloadWebsite, boolean loadUrl) {
         // Store the current URL.
         nestedScrollWebView.setCurrentUrl(url);
 
@@ -3770,9 +3764,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 // Get the current page position.
                 int currentPagePosition = webViewPagerAdapter.getPositionForId(nestedScrollWebView.getWebViewFragmentId());
 
-                // Get a handle for the tab layout.
-                TabLayout tabLayout = findViewById(R.id.tablayout);
-
                 // Get the corresponding tab.
                 TabLayout.Tab tab = tabLayout.getTabAt(currentPagePosition);
 
@@ -3857,9 +3848,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             String defaultFontSizeString = sharedPreferences.getString("font_size", getString(R.string.font_size_default_value));
             String defaultUserAgentName = sharedPreferences.getString("user_agent", getString(R.string.user_agent_default_value));
             boolean defaultSwipeToRefresh = sharedPreferences.getBoolean("swipe_to_refresh", true);
+            String webViewTheme = sharedPreferences.getString("webview_theme", getString(R.string.webview_theme_default_value));
             boolean wideViewport = sharedPreferences.getBoolean("wide_viewport", true);
             boolean displayWebpageImages = sharedPreferences.getBoolean("display_webpage_images", true);
 
+            // Get the WebView theme entry values string array.
+            String[] webViewThemeEntryValuesStringArray = getResources().getStringArray(R.array.webview_theme_entry_values);
+
             // Get a handle for the cookie manager.
             CookieManager cookieManager = CookieManager.getInstance();
 
@@ -3878,7 +3873,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                 // Get the settings from the cursor.
                 nestedScrollWebView.setDomainSettingsDatabaseId(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper._ID)));
-                nestedScrollWebView.setDomainSettingsJavaScriptEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_JAVASCRIPT)) == 1);
+                nestedScrollWebView.getSettings().setJavaScriptEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_JAVASCRIPT)) == 1);
                 nestedScrollWebView.setAcceptFirstPartyCookies(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FIRST_PARTY_COOKIES)) == 1);
                 boolean domainThirdPartyCookiesEnabled = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_THIRD_PARTY_COOKIES)) == 1);
                 nestedScrollWebView.getSettings().setDomStorageEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_DOM_STORAGE)) == 1);
@@ -3900,7 +3895,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 String userAgentName = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.USER_AGENT));
                 int fontSize = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.FONT_SIZE));
                 int swipeToRefreshInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SWIPE_TO_REFRESH));
-                int nightModeInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.NIGHT_MODE));
+                int webViewThemeInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.WEBVIEW_THEME));
                 int wideViewportInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.WIDE_VIEWPORT));
                 int displayWebpageImagesInt = currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.DISPLAY_IMAGES));
                 boolean pinnedSslCertificate = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE)) == 1);
@@ -3913,24 +3908,31 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 boolean pinnedIpAddresses = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.PINNED_IP_ADDRESSES)) == 1);
                 String pinnedHostIpAddresses = currentDomainSettingsCursor.getString(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.IP_ADDRESSES));
 
-                // Create the pinned SSL date variables.
+                // Get the pinned SSL date longs.
+                long pinnedSslStartDateLong = currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE));
+                long pinnedSslEndDateLong = currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE));
+
+                // Define the pinned SSL date variables.
                 Date pinnedSslStartDate;
                 Date pinnedSslEndDate;
 
-                // Set the pinned SSL certificate start date to `null` if the saved date `long` is 0 because creating a new Date results in an error if the input is 0.
-                if (currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE)) == 0) {
+                // Set the pinned SSL certificate start date to `null` if the saved date long is 0 because creating a new date results in an error if the input is 0.
+                if (pinnedSslStartDateLong == 0) {
                     pinnedSslStartDate = null;
                 } else {
-                    pinnedSslStartDate = new Date(currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE)));
+                    pinnedSslStartDate = new Date(pinnedSslStartDateLong);
                 }
 
-                // Set the pinned SSL certificate end date to `null` if the saved date `long` is 0 because creating a new Date results in an error if the input is 0.
-                if (currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE)) == 0) {
+                // Set the pinned SSL certificate end date to `null` if the saved date long is 0 because creating a new date results in an error if the input is 0.
+                if (pinnedSslEndDateLong == 0) {
                     pinnedSslEndDate = null;
                 } else {
-                    pinnedSslEndDate = new Date(currentDomainSettingsCursor.getLong(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE)));
+                    pinnedSslEndDate = new Date(pinnedSslEndDateLong);
                 }
 
+                // Close the current host domain settings cursor.
+                currentDomainSettingsCursor.close();
+
                 // If there is a pinned SSL certificate, store it in the WebView.
                 if (pinnedSslCertificate) {
                     nestedScrollWebView.setPinnedSslCertificate(pinnedSslIssuedToCName, pinnedSslIssuedToOName, pinnedSslIssuedToUName, pinnedSslIssuedByCName, pinnedSslIssuedByOName, pinnedSslIssuedByUName,
@@ -3942,37 +3944,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                     nestedScrollWebView.setPinnedIpAddresses(pinnedHostIpAddresses);
                 }
 
-                // Set night mode according to the night mode int.
-                switch (nightModeInt) {
-                    case DomainsDatabaseHelper.SYSTEM_DEFAULT:
-                        // Set night mode according to the current default.
-                        nestedScrollWebView.setNightMode(sharedPreferences.getBoolean("night_mode", false));
-                        break;
-
-                    case DomainsDatabaseHelper.ENABLED:
-                        // Enable night mode.
-                        nestedScrollWebView.setNightMode(true);
-                        break;
-
-                    case DomainsDatabaseHelper.DISABLED:
-                        // Disable night mode.
-                        nestedScrollWebView.setNightMode(false);
-                        break;
-                }
-
-                // Enable JavaScript if night mode is enabled.
-                if (nestedScrollWebView.getNightMode()) {
-                    // Enable JavaScript.
-                    nestedScrollWebView.getSettings().setJavaScriptEnabled(true);
-                } else {
-                    // Set JavaScript according to the domain settings.
-                    nestedScrollWebView.getSettings().setJavaScriptEnabled(nestedScrollWebView.getDomainSettingsJavaScriptEnabled());
-                }
-
-                // Close the current host domain settings cursor.
-                currentDomainSettingsCursor.close();
-
-                // Apply the domain settings.
+                // Apply the cookie domain settings.
                 cookieManager.setAcceptCookie(nestedScrollWebView.getAcceptFirstPartyCookies());
 
                 // Set third-party cookies status if API >= 21.
@@ -4076,6 +4048,45 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                         swipeRefreshLayout.setEnabled(false);
                 }
 
+                // Check to see if WebView themes are supported.
+                if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
+                    // Set the WebView theme.
+                    switch (webViewThemeInt) {
+                        case DomainsDatabaseHelper.SYSTEM_DEFAULT:
+                            // Set the WebView theme.  A switch statement cannot be used because the WebView theme entry values string array is not a compile time constant.
+                            if (webViewTheme.equals(webViewThemeEntryValuesStringArray[1])) {  // The light theme is selected.
+                                // Turn off the WebView dark mode.
+                                WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
+                            } else if (webViewTheme.equals(webViewThemeEntryValuesStringArray[2])) {  // The dark theme is selected.
+                                // Turn on the WebView dark mode.
+                                WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
+                            } else {  // The system default theme is selected.
+                                // Get the current system theme status.
+                                int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
+
+                                // Set the WebView theme according to the current system theme status.
+                                if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {  // The system is in day mode.
+                                    // Turn off the WebView dark mode.
+                                    WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
+                                } else {  // The system is in night mode.
+                                    // Turn on the WebView dark mode.
+                                    WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
+                                }
+                            }
+                            break;
+
+                        case DomainsDatabaseHelper.LIGHT_THEME:
+                            // Turn off the WebView dark mode.
+                            WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
+                            break;
+
+                        case DomainsDatabaseHelper.DARK_THEME:
+                            // Turn on the WebView dark mode.
+                            WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
+                            break;
+                    }
+                }
+
                 // Set the viewport.
                 switch (wideViewportInt) {
                     case DomainsDatabaseHelper.SYSTEM_DEFAULT:
@@ -4109,15 +4120,15 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 // Get the current theme status.
                 int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
 
-                // Set a background on the URL relative layout to indicate that custom domain settings are being used. The deprecated `.getDrawable()` must be used until the minimum API >= 21.
-                if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
-                    urlRelativeLayout.setBackground(getResources().getDrawable(R.drawable.url_bar_background_dark_blue));
+                // Set a background on the URL relative layout to indicate that custom domain settings are being used.
+                if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
+                    urlRelativeLayout.setBackground(ResourcesCompat.getDrawable(getResources(), R.drawable.url_bar_background_light_green, null));
                 } else {
-                    urlRelativeLayout.setBackground(getResources().getDrawable(R.drawable.url_bar_background_light_green));
+                    urlRelativeLayout.setBackground(ResourcesCompat.getDrawable(getResources(), R.drawable.url_bar_background_dark_blue, null));
                 }
             } else {  // The new URL does not have custom domain settings.  Load the defaults.
                 // Store the values from the shared preferences.
-                boolean defaultJavaScriptEnabled = sharedPreferences.getBoolean("javascript", false);
+                nestedScrollWebView.getSettings().setJavaScriptEnabled(sharedPreferences.getBoolean("javascript", false));
                 nestedScrollWebView.setAcceptFirstPartyCookies(sharedPreferences.getBoolean("first_party_cookies", false));
                 boolean defaultThirdPartyCookiesEnabled = sharedPreferences.getBoolean("third_party_cookies", false);
                 nestedScrollWebView.getSettings().setDomStorageEnabled(sharedPreferences.getBoolean("dom_storage", false));
@@ -4129,16 +4140,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 nestedScrollWebView.enableBlocklist(NestedScrollWebView.ULTRALIST, sharedPreferences.getBoolean("ultralist", true));
                 nestedScrollWebView.enableBlocklist(NestedScrollWebView.ULTRAPRIVACY, sharedPreferences.getBoolean("ultraprivacy", true));
                 nestedScrollWebView.enableBlocklist(NestedScrollWebView.THIRD_PARTY_REQUESTS, sharedPreferences.getBoolean("block_all_third_party_requests", false));
-                nestedScrollWebView.setNightMode(sharedPreferences.getBoolean("night_mode", false));
-
-                // Enable JavaScript if night mode is enabled.
-                if (nestedScrollWebView.getNightMode()) {
-                    // Enable JavaScript.
-                    nestedScrollWebView.getSettings().setJavaScriptEnabled(true);
-                } else {
-                    // Set JavaScript according to the domain settings.
-                    nestedScrollWebView.getSettings().setJavaScriptEnabled(defaultJavaScriptEnabled);
-                }
 
                 // Apply the default first-party cookie setting.
                 cookieManager.setAcceptCookie(nestedScrollWebView.getAcceptFirstPartyCookies());
@@ -4202,14 +4203,38 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                         nestedScrollWebView.getSettings().setUserAgentString(userAgentDataArray[userAgentArrayPosition]);
                 }
 
+                // Apply the WebView theme if supported by the installed WebView.
+                if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
+                    // Set the WebView theme.  A switch statement cannot be used because the WebView theme entry values string array is not a compile time constant.
+                    if (webViewTheme.equals(webViewThemeEntryValuesStringArray[1])) {  // The light theme is selected.
+                        // Turn off the WebView dark mode.
+                        WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
+                    } else if (webViewTheme.equals(webViewThemeEntryValuesStringArray[2])) {  // The dark theme is selected.
+                        // Turn on the WebView dark mode.
+                        WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
+                    } else {  // The system default theme is selected.
+                        // Get the current system theme status.
+                        int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
+
+                        // Set the WebView theme according to the current system theme status.
+                        if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {  // The system is in day mode.
+                            // Turn off the WebView dark mode.
+                            WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
+                        } else {  // The system is in night mode.
+                            // Turn on the WebView dark mode.
+                            WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
+                        }
+                    }
+                }
+
                 // Set the viewport.
                 nestedScrollWebView.getSettings().setUseWideViewPort(wideViewport);
 
                 // Set the loading of webpage images.
                 nestedScrollWebView.getSettings().setLoadsImagesAutomatically(displayWebpageImages);
 
-                // Set a transparent background on URL edit text.  The deprecated `getResources().getDrawable()` must be used until the minimum API >= 21.
-                urlRelativeLayout.setBackground(getResources().getDrawable(R.color.transparent));
+                // Set a transparent background on URL edit text.
+                urlRelativeLayout.setBackground(ResourcesCompat.getDrawable(getResources(), R.color.transparent, null));
             }
 
             // Close the domains database helper.
@@ -4224,14 +4249,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             nestedScrollWebView.reload();
         }
 
-        // Return the user agent changed status.
-        return !nestedScrollWebView.getSettings().getUserAgentString().equals(initialUserAgent);
+        // Load the URL if directed.  This makes sure that the domain settings are properly loaded before the URL.  By using `loadUrl()`, instead of `loadUrlFromBase()`, the Referer header will never be sent.
+        if (loadUrl) {
+            nestedScrollWebView.loadUrl(url, customHeaders);
+        }
     }
 
     private void applyProxy(boolean reloadWebViews) {
-        // Get a handle for the app bar layout.
-        AppBarLayout appBarLayout = findViewById(R.id.appbar_layout);
-
         // Set the proxy according to the mode.  `this` refers to the current activity where an alert dialog might be displayed.
         ProxyHelper.setProxy(getApplicationContext(), appBarLayout, proxyMode);
 
@@ -4259,10 +4283,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
             case ProxyHelper.TOR:
                 // Set the app bar background to indicate proxying through Orbot is enabled.
-                if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
-                    appBarLayout.setBackgroundResource(R.color.dark_blue_30);
-                } else {
+                if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
                     appBarLayout.setBackgroundResource(R.color.blue_50);
+                } else {
+                    appBarLayout.setBackgroundResource(R.color.dark_blue_30);
                 }
 
                 // Check to see if Orbot is installed.
@@ -4301,10 +4325,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
             case ProxyHelper.I2P:
                 // Set the app bar background to indicate proxying through Orbot is enabled.
-                if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
-                    appBarLayout.setBackgroundResource(R.color.dark_blue_30);
-                } else {
+                if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
                     appBarLayout.setBackgroundResource(R.color.blue_50);
+                } else {
+                    appBarLayout.setBackgroundResource(R.color.dark_blue_30);
                 }
 
                 // Check to see if I2P is installed.
@@ -4328,10 +4352,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
             case ProxyHelper.CUSTOM:
                 // Set the app bar background to indicate proxying through Orbot is enabled.
-                if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
-                    appBarLayout.setBackgroundResource(R.color.dark_blue_30);
-                } else {
+                if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
                     appBarLayout.setBackgroundResource(R.color.blue_50);
+                } else {
+                    appBarLayout.setBackgroundResource(R.color.dark_blue_30);
                 }
                 break;
         }
@@ -4383,10 +4407,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             if (currentWebView.getAcceptFirstPartyCookies()) {  // First-party cookies are enabled.
                 firstPartyCookiesMenuItem.setIcon(R.drawable.cookies_enabled);
             } else {  // First-party cookies are disabled.
-                if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
-                    firstPartyCookiesMenuItem.setIcon(R.drawable.cookies_disabled_night);
-                } else {
+                if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
                     firstPartyCookiesMenuItem.setIcon(R.drawable.cookies_disabled_day);
+                } else {
+                    firstPartyCookiesMenuItem.setIcon(R.drawable.cookies_disabled_night);
                 }
             }
 
@@ -4394,24 +4418,24 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             if (currentWebView.getSettings().getJavaScriptEnabled() && currentWebView.getSettings().getDomStorageEnabled()) {  // Both JavaScript and DOM storage are enabled.
                 domStorageMenuItem.setIcon(R.drawable.dom_storage_enabled);
             } else if (currentWebView.getSettings().getJavaScriptEnabled()) {  // JavaScript is enabled but DOM storage is disabled.
-                if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
-                    domStorageMenuItem.setIcon(R.drawable.dom_storage_disabled_night);
-                } else {
+                if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
                     domStorageMenuItem.setIcon(R.drawable.dom_storage_disabled_day);
+                } else {
+                    domStorageMenuItem.setIcon(R.drawable.dom_storage_disabled_night);
                 }
             } else {  // JavaScript is disabled, so DOM storage is ghosted.
-                if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
-                    domStorageMenuItem.setIcon(R.drawable.dom_storage_ghosted_night);
-                } else {
+                if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
                     domStorageMenuItem.setIcon(R.drawable.dom_storage_ghosted_day);
+                } else {
+                    domStorageMenuItem.setIcon(R.drawable.dom_storage_ghosted_night);
                 }
             }
 
             // Update the refresh icon.
-            if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
-                refreshMenuItem.setIcon(R.drawable.refresh_enabled_night);
-            } else {
+            if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
                 refreshMenuItem.setIcon(R.drawable.refresh_enabled_day);
+            } else {
+                refreshMenuItem.setIcon(R.drawable.refresh_enabled_night);
             }
 
             // `invalidateOptionsMenu()` calls `onPrepareOptionsMenu()` and redraws the icons in the app bar.
@@ -4637,8 +4661,95 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         ultraList = combinedBlocklists.get(4);
         ultraPrivacy = combinedBlocklists.get(5);
 
-        // Add the first tab.
-        addNewTab("", true);
+        // Check to see if the activity has been restarted.
+        if ((savedStateArrayList == null) || (savedStateArrayList.size() == 0)) {  // The activity has not been restarted or it was restarted on start to force the night theme.
+            // Add the first tab.
+            addNewTab("", true);
+        } else {  // The activity has been restarted.
+            // Restore each tab.  Once the minimum API >= 24, a `forEach()` command can be used.
+            for (int i = 0; i < savedStateArrayList.size(); i++) {
+                // Add a new tab.
+                tabLayout.addTab(tabLayout.newTab());
+
+                // Get the new tab.
+                TabLayout.Tab newTab = tabLayout.getTabAt(i);
+
+                // Remove the lint warning below that the current tab might be null.
+                assert newTab != null;
+
+                // Set a custom view on the new tab.
+                newTab.setCustomView(R.layout.tab_custom_view);
+
+                // Add the new page.
+                webViewPagerAdapter.restorePage(savedStateArrayList.get(i), savedNestedScrollWebViewStateArrayList.get(i));
+            }
+
+            // Reset the saved state variables.
+            savedStateArrayList = null;
+            savedNestedScrollWebViewStateArrayList = null;
+
+            // Restore the selected tab position.
+            if (savedTabPosition == 0) {  // The first tab is selected.
+                // Set the first page as the current WebView.
+                setCurrentWebView(0);
+            } else {  // the first tab is not selected.
+                // Move to the selected tab.
+                webViewPager.setCurrentItem(savedTabPosition);
+            }
+
+            // Get the intent that started the app.
+            Intent intent = getIntent();
+
+            // Reset the intent.  This prevents a duplicate tab from being created on restart.
+            setIntent(new Intent());
+
+            // Get the information from the intent.
+            String intentAction = intent.getAction();
+            Uri intentUriData = intent.getData();
+
+            // Determine if this is a web search.
+            boolean isWebSearch = ((intentAction != null) && intentAction.equals(Intent.ACTION_WEB_SEARCH));
+
+            // Only process the URI if it contains data or it is a web search.  If the user pressed the desktop icon after the app was already running the URI will be null.
+            if (intentUriData != null || isWebSearch) {
+                // Get the shared preferences.
+                SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+
+                // Create a URL string.
+                String url;
+
+                // If the intent action is a web search, perform the search.
+                if (isWebSearch) {  // The intent is a web search.
+                    // Create an encoded URL string.
+                    String encodedUrlString;
+
+                    // Sanitize the search input and convert it to a search.
+                    try {
+                        encodedUrlString = URLEncoder.encode(intent.getStringExtra(SearchManager.QUERY), "UTF-8");
+                    } catch (UnsupportedEncodingException exception) {
+                        encodedUrlString = "";
+                    }
+
+                    // Add the base search URL.
+                    url = searchURL + encodedUrlString;
+                } else {  // The intent should contain a URL.
+                    // Set the intent data as the url.
+                    url = intentUriData.toString();
+                }
+
+                // Add a new tab if specified in the preferences.
+                if (sharedPreferences.getBoolean("open_intents_in_new_tab", true)) {  // Load the URL in a new tab.
+                    // Set the loading new intent flag.
+                    loadingNewIntent = true;
+
+                    // Add a new tab.
+                    addNewTab(url, true);
+                } else {  // Load the URL in the current tab.
+                    // Make it so.
+                    loadUrl(currentWebView, url);
+                }
+            }
+        }
     }
 
     public void addTab(View view) {
@@ -4647,13 +4758,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     }
 
     private void addNewTab(String url, boolean moveToTab) {
-        // Sanitize the URL.
-        url = sanitizeUrl(url);
-
-        // Get a handle for the tab layout and the view pager.
-        TabLayout tabLayout = findViewById(R.id.tablayout);
-        ViewPager webViewPager = findViewById(R.id.webviewpager);
-
         // Get the new page number.  The page numbers are 0 indexed, so the new page number will match the current count.
         int newTabNumber = tabLayout.getTabCount();
 
@@ -4674,9 +4778,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     }
 
     public void closeTab(View view) {
-        // Get a handle for the tab layout.
-        TabLayout tabLayout = findViewById(R.id.tablayout);
-
         // Run the command according to the number of tabs.
         if (tabLayout.getTabCount() > 1) {  // There is more than one tab open.
             // Close the current tab.
@@ -4687,10 +4788,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     }
 
     private void closeCurrentTab() {
-        // Get handles for the views.
-        AppBarLayout appBarLayout = findViewById(R.id.appbar_layout);
-        TabLayout tabLayout = findViewById(R.id.tablayout);
-        ViewPager webViewPager = findViewById(R.id.webviewpager);
+        // Pause the current WebView.
+        currentWebView.onPause();
+
+        // Pause the current WebView JavaScript timers.
+        currentWebView.pauseTimers();
 
         // Get the current tab number.
         int currentTabNumber = tabLayout.getSelectedTabPosition();
@@ -4707,6 +4809,48 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         appBarLayout.setExpanded(true);
     }
 
+    private void saveWebpageArchive(String filePath) {
+        // Save the webpage archive.
+        currentWebView.saveWebArchive(filePath);
+
+        // Display a snackbar.
+        Snackbar saveWebpageArchiveSnackbar = Snackbar.make(currentWebView, getString(R.string.file_saved) + "  " + filePath, Snackbar.LENGTH_SHORT);
+
+        // Add an open option to the snackbar.
+        saveWebpageArchiveSnackbar.setAction(R.string.open, (View view) -> {
+            // Get a file for the file name string.
+            File file = new File(filePath);
+
+            // Declare a file URI variable.
+            Uri fileUri;
+
+            // Get the URI for the file according to the Android version.
+            if (Build.VERSION.SDK_INT >= 24) {  // Use a file provider.
+                fileUri = FileProvider.getUriForFile(this, getString(R.string.file_provider), file);
+            } else {  // Get the raw file path URI.
+                fileUri = Uri.fromFile(file);
+            }
+
+            // Get a handle for the content resolver.
+            ContentResolver contentResolver = getContentResolver();
+
+            // Create an open intent with `ACTION_VIEW`.
+            Intent openIntent = new Intent(Intent.ACTION_VIEW);
+
+            // Set the URI and the MIME type.
+            openIntent.setDataAndType(fileUri, contentResolver.getType(fileUri));
+
+            // Allow the app to read the file URI.
+            openIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+            // Show the chooser.
+            startActivity(Intent.createChooser(openIntent, getString(R.string.open)));
+        });
+
+        // Show the snackbar.
+        saveWebpageArchiveSnackbar.show();
+    }
+
     private void clearAndExit() {
         // Get a handle for the shared preferences.
         SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
@@ -4795,6 +4939,19 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             }
         }
 
+        // Clear the logcat.
+        if (clearEverything || sharedPreferences.getBoolean(getString(R.string.clear_logcat_key), true)) {
+            try {
+                // Clear the logcat.  `-c` clears the logcat.  `-b all` clears all the buffers (instead of just crash, main, and system).
+                Process process = Runtime.getRuntime().exec("logcat -b all -c");
+
+                // Wait for the process to finish.
+                process.waitFor();
+            } catch (IOException|InterruptedException exception) {
+                // Do nothing.
+            }
+        }
+
         // Clear the cache.
         if (clearEverything || sharedPreferences.getBoolean("clear_cache", true)) {
             // Clear the cache from each WebView.
@@ -4851,7 +5008,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 // Clear the back/forward history for this WebView.
                 nestedScrollWebView.clearHistory();
 
-                // Destroy the internal state of `mainWebView`.
+                // Destroy the internal state of the WebView.
                 nestedScrollWebView.destroy();
             }
         }
@@ -4884,6 +5041,19 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         System.exit(0);
     }
 
+    public void bookmarksBack(View view) {
+        if (currentBookmarksFolder.isEmpty()) {  // The home folder is displayed.
+            // close the bookmarks drawer.
+            drawerLayout.closeDrawer(GravityCompat.END);
+        } else {  // A subfolder is displayed.
+            // Place the former parent folder in `currentFolder`.
+            currentBookmarksFolder = bookmarksDatabaseHelper.getParentFolderName(currentBookmarksFolder);
+
+            // Load the new folder.
+            loadBookmarksFolder();
+        }
+    }
+
     private void setCurrentWebView(int pageNumber) {
         // Get handles for the URL views.
         RelativeLayout urlRelativeLayout = findViewById(R.id.url_relativelayout);
@@ -4965,14 +5135,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 // Get the current theme status.
                 int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
 
-                // Set a green background on the URL relative layout to indicate that custom domain settings are being used. The deprecated `.getDrawable()` must be used until the minimum API >= 21.
-                if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
-                    urlRelativeLayout.setBackground(getResources().getDrawable(R.drawable.url_bar_background_dark_blue));
+                // Set a green background on the URL relative layout to indicate that custom domain settings are being used.
+                if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
+                    urlRelativeLayout.setBackground(ResourcesCompat.getDrawable(getResources(), R.drawable.url_bar_background_light_green, null));
                 } else {
-                    urlRelativeLayout.setBackground(getResources().getDrawable(R.drawable.url_bar_background_light_green));
+                    urlRelativeLayout.setBackground(ResourcesCompat.getDrawable(getResources(), R.drawable.url_bar_background_dark_blue, null));
                 }
             } else {
-                urlRelativeLayout.setBackground(getResources().getDrawable(R.color.transparent));
+                urlRelativeLayout.setBackground(ResourcesCompat.getDrawable(getResources(), R.color.transparent, null));
             }
         } else {  // The fragment has not been populated.  Try again in 100 milliseconds.
             // Create a handler to set the current WebView.
@@ -4990,18 +5160,57 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     }
 
     @Override
-    public void initializeWebView(NestedScrollWebView nestedScrollWebView, int pageNumber, ProgressBar progressBar, String url) {
+    public void initializeWebView(NestedScrollWebView nestedScrollWebView, int pageNumber, ProgressBar progressBar, String url, Boolean restoringState) {
+        // Get a handle for the shared preferences.
+        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+
+        // Get the WebView theme.
+        String webViewTheme = sharedPreferences.getString("webview_theme", getString(R.string.webview_theme_default_value));
+
+        // Get the WebView theme entry values string array.
+        String[] webViewThemeEntryValuesStringArray = getResources().getStringArray(R.array.webview_theme_entry_values);
+
+        // Apply the WebView theme if supported by the installed WebView.
+        if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
+            // Set the WebView theme.  A switch statement cannot be used because the WebView theme entry values string array is not a compile time constant.
+            if (webViewTheme.equals(webViewThemeEntryValuesStringArray[1])) {  // The light theme is selected.
+                // Turn off the WebView dark mode.
+                WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
+
+                // Make the WebView visible. The WebView was created invisible in `webview_framelayout` to prevent a white background splash in night mode.
+                // If the system is currently in night mode, showing the WebView will be handled in `onProgressChanged()`.
+                nestedScrollWebView.setVisibility(View.VISIBLE);
+            } else if (webViewTheme.equals(webViewThemeEntryValuesStringArray[2])) {  // The dark theme is selected.
+                // Turn on the WebView dark mode.
+                WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
+            } else {  // The system default theme is selected.
+                // Get the current system theme status.
+                int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
+
+                // Set the WebView theme according to the current system theme status.
+                if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {  // The system is in day mode.
+                    // Turn off the WebView dark mode.
+                    WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_OFF);
+
+                    // Make the WebView visible. The WebView was created invisible in `webview_framelayout` to prevent a white background splash in night mode.
+                    // If the system is currently in night mode, showing the WebView will be handled in `onProgressChanged()`.
+                    nestedScrollWebView.setVisibility(View.VISIBLE);
+                } else {  // The system is in night mode.
+                    // Turn on the WebView dark mode.
+                    WebSettingsCompat.setForceDark(nestedScrollWebView.getSettings(), WebSettingsCompat.FORCE_DARK_ON);
+                }
+            }
+        }
+
         // Get a handle for the app compat delegate.
         AppCompatDelegate appCompatDelegate = getDelegate();
 
         // Get handles for the activity views.
         FrameLayout rootFrameLayout = findViewById(R.id.root_framelayout);
-        DrawerLayout drawerLayout = findViewById(R.id.drawerlayout);
         RelativeLayout mainContentRelativeLayout = findViewById(R.id.main_content_relativelayout);
         ActionBar actionBar = appCompatDelegate.getSupportActionBar();
         LinearLayout tabsLinearLayout = findViewById(R.id.tabs_linearlayout);
         EditText urlEditText = findViewById(R.id.url_edittext);
-        TabLayout tabLayout = findViewById(R.id.tablayout);
         SwipeRefreshLayout swipeRefreshLayout = findViewById(R.id.swiperefreshlayout);
 
         // Remove the incorrect lint warning below that the action bar might be null.
@@ -5019,9 +5228,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Remove the lint warning below that the input method manager might be null.
         assert inputMethodManager != null;
 
-        // Get a handle for the shared preferences.
-        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
-
         // Initialize the favorite icon.
         nestedScrollWebView.initializeFavoriteIcon();
 
@@ -5067,8 +5273,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                             // Hide the action bar.
                             actionBar.hide();
 
-                            // If the app bar is not being scrolled, the swipe refresh layout needs to be adjusted.
-                            if (!scrollAppBar) {
+                            // Check to see if the app bar is normally scrolled.
+                            if (scrollAppBar) {  // The app bar is scrolled when it is displayed.
+                                // Get the swipe refresh layout parameters.
+                                CoordinatorLayout.LayoutParams swipeRefreshLayoutParams = (CoordinatorLayout.LayoutParams) swipeRefreshLayout.getLayoutParams();
+
+                                // Remove the off-screen scrolling layout.
+                                swipeRefreshLayoutParams.setBehavior(null);
+                            } else {  // The app bar is not scrolled when it is displayed.
                                 // Remove the padding from the top of the swipe refresh layout.
                                 swipeRefreshLayout.setPadding(0, 0, 0, 0);
 
@@ -5099,8 +5311,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                             // Show the action bar.
                             actionBar.show();
 
-                            // If the app bar is not being scrolled, the swipe refresh layout needs to be adjusted.
-                            if (!scrollAppBar) {
+                            // Check to see if the app bar is normally scrolled.
+                            if (scrollAppBar) {  // The app bar is scrolled when it is displayed.
+                                // Get the swipe refresh layout parameters.
+                                CoordinatorLayout.LayoutParams swipeRefreshLayoutParams = (CoordinatorLayout.LayoutParams) swipeRefreshLayout.getLayoutParams();
+
+                                // Add the off-screen scrolling layout.
+                                swipeRefreshLayoutParams.setBehavior(new AppBarLayout.ScrollingViewBehavior());
+                            } else {  // The app bar is not scrolled when it is displayed.
                                 // The swipe refresh layout must be manually moved below the app bar layout.
                                 swipeRefreshLayout.setPadding(0, appBarHeight, 0, 0);
 
@@ -5154,10 +5372,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             }
 
             // Get the file name from the content disposition.
-            String fileNameString = PrepareSaveDialog.getFileNameFromContentDisposition(this, contentDisposition, downloadUrl);
+            String fileNameString = PrepareSaveDialog.getFileNameFromHeaders(this, contentDisposition, mimetype, downloadUrl);
 
             // Instantiate the save dialog.
-            DialogFragment saveDialogFragment = SaveDialog.saveUrl(StoragePermissionDialog.SAVE_URL, downloadUrl, formattedFileSizeString, fileNameString, userAgent,
+            DialogFragment saveDialogFragment = SaveWebpageDialog.saveWebpage(StoragePermissionDialog.SAVE_URL, downloadUrl, formattedFileSizeString, fileNameString, userAgent,
                     nestedScrollWebView.getAcceptFirstPartyCookies());
 
             // Show the save dialog.  It must be named `save_dialog` so that the file picker can update the file name.
@@ -5243,36 +5461,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             // Update the progress bar when a page is loading.
             @Override
             public void onProgressChanged(WebView view, int progress) {
-                // Inject the night mode CSS if night mode is enabled.
-                if (nestedScrollWebView.getNightMode()) {  // Night mode is enabled.
-                    // `background-color: #212121` sets the background to be dark gray.  `color: #BDBDBD` sets the text color to be light gray.  `box-shadow: none` removes a lower underline on links
-                    // used by WordPress.  `text-decoration: none` removes all text underlines.  `text-shadow: none` removes text shadows, which usually have a hard coded color.
-                    // `border: none` removes all borders, which can also be used to underline text.  `a {color: #1565C0}` sets links to be a dark blue.
-                    // `::selection {background: #0D47A1}' sets the text selection highlight color to be a dark blue. `!important` takes precedent over any existing sub-settings.
-                    nestedScrollWebView.evaluateJavascript("(function() {var parent = document.getElementsByTagName('head').item(0); var style = document.createElement('style'); style.type = 'text/css'; " +
-                            "style.innerHTML = '* {background-color: #212121 !important; color: #BDBDBD !important; box-shadow: none !important; text-decoration: none !important;" +
-                            "text-shadow: none !important; border: none !important;} a {color: #1565C0 !important;} ::selection {background: #0D47A1 !important;}'; parent.appendChild(style)})()", value -> {
-                        // Initialize a handler to display `mainWebView`.
-                        Handler displayWebViewHandler = new Handler();
-
-                        // Setup a runnable to display `mainWebView` after a delay to allow the CSS to be applied.
-                        Runnable displayWebViewRunnable = () -> {
-                            // Only display `mainWebView` if the progress bar is gone.  This prevents the display of the `WebView` while it is still loading.
-                            if (progressBar.getVisibility() == View.GONE) {
-                                nestedScrollWebView.setVisibility(View.VISIBLE);
-                            }
-                        };
-
-                        // Display the WebView after 500 milliseconds.
-                        displayWebViewHandler.postDelayed(displayWebViewRunnable, 500);
-                    });
-                } else {  // Night mode is disabled.
-                    // Display the nested scroll WebView if night mode is disabled.
-                    // Because of a race condition between `applyDomainSettings` and `onPageStarted`,
-                    // when night mode is set by domain settings the WebView may be hidden even if night mode is not currently enabled.
-                    nestedScrollWebView.setVisibility(View.VISIBLE);
-                }
-
                 // Update the progress bar.
                 progressBar.setProgress(progress);
 
@@ -5286,6 +5474,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                     //Stop the swipe to refresh indicator if it is running
                     swipeRefreshLayout.setRefreshing(false);
+
+                    // Make the current WebView visible.  If this is a new tab, the current WebView would have been created invisible in `webview_framelayout` to prevent a white background splash in night mode.
+                    nestedScrollWebView.setVisibility(View.VISIBLE);
                 }
             }
 
@@ -5334,19 +5525,19 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                     // Get the custom view from the tab.
                     View tabView = tab.getCustomView();
 
-                    // Remove the incorrect warning below that the current tab view might be null.
-                    assert tabView != null;
+                    // Only populate the title text view if the tab view has been fully populated.
+                    if (tabView != null) {
+                        // Get the title text view from the tab.
+                        TextView tabTitleTextView = tabView.findViewById(R.id.title_textview);
 
-                    // Get the title text view from the tab.
-                    TextView tabTitleTextView = tabView.findViewById(R.id.title_textview);
-
-                    // Set the title according to the URL.
-                    if (title.equals("about:blank")) {
-                        // Set the title to indicate a new tab.
-                        tabTitleTextView.setText(R.string.new_tab);
-                    } else {
-                        // Set the title as the tab text.
-                        tabTitleTextView.setText(title);
+                        // Set the title according to the URL.
+                        if (title.equals("about:blank")) {
+                            // Set the title to indicate a new tab.
+                            tabTitleTextView.setText(R.string.new_tab);
+                        } else {
+                            // Set the title as the tab text.
+                            tabTitleTextView.setText(title);
+                        }
                     }
                 }
             }
@@ -5491,28 +5682,21 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         });
 
         nestedScrollWebView.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.
+            // `shouldOverrideUrlLoading` makes this WebView the default handler for URLs inside the app, so that links are not kicked out to other apps.
             // The deprecated `shouldOverrideUrlLoading` must be used until API >= 24.
             @Override
             public boolean shouldOverrideUrlLoading(WebView view, String url) {
                 // Sanitize the url.
                 url = sanitizeUrl(url);
 
+                // Handle the URL according to the type.
                 if (url.startsWith("http")) {  // Load the URL in Privacy Browser.
-                    // Apply the domain settings for the new URL.  This doesn't do anything if the domain has not changed.
-                    boolean userAgentChanged = applyDomainSettings(nestedScrollWebView, url, true, false);
-
-                    // Check if the user agent has changed.
-                    if (userAgentChanged) {
-                        // Manually load the URL.  The changing of the user agent will cause WebView to reload the previous URL.
-                        nestedScrollWebView.loadUrl(url, customHeaders);
-
-                        // Returning true indicates that Privacy Browser is manually handling the loading of the URL.
-                        return true;
-                    } else {
-                        // Returning false causes the current WebView to handle the URL and prevents it from adding redirects to the history list.
-                        return false;
-                    }
+                    // Load the URL.  By using `loadUrl()`, instead of `loadUrlFromBase()`, the Referer header will never be sent.
+                    loadUrl(nestedScrollWebView, url);
+
+                    // Returning true indicates that Privacy Browser is manually handling the loading of the URL.
+                    // Custom headers cannot be added if false is returned and the WebView handles the loading of the URL.
+                    return true;
                 } 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.
                     Intent emailIntent = new Intent(Intent.ACTION_SENDTO);
@@ -5523,8 +5707,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                     // Open the email program in a new task instead of as part of Privacy Browser.
                     emailIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 
-                    // Make it so.
-                    startActivity(emailIntent);
+                    try {
+                        // Make it so.
+                        startActivity(emailIntent);
+                    } catch (ActivityNotFoundException exception) {
+                        // Display a snackbar.
+                        Snackbar.make(currentWebView, getString(R.string.error) + "  " + exception, Snackbar.LENGTH_INDEFINITE).show();
+                    }
+
 
                     // Returning true indicates Privacy Browser is handling the URL by creating an intent.
                     return true;
@@ -5538,8 +5728,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                     // Open the dialer in a new task instead of as part of Privacy Browser.
                     dialIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 
-                    // Make it so.
-                    startActivity(dialIntent);
+                    try {
+                        // Make it so.
+                        startActivity(dialIntent);
+                    } catch (ActivityNotFoundException exception) {
+                        // Display a snackbar.
+                        Snackbar.make(currentWebView, getString(R.string.error) + "  " + exception, Snackbar.LENGTH_INDEFINITE).show();
+                    }
 
                     // Returning true indicates Privacy Browser is handling the URL by creating an intent.
                     return true;
@@ -5599,8 +5794,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 // Get a handle for the navigation menu.
                 Menu navigationMenu = navigationView.getMenu();
 
-                // Get a handle for the navigation requests menu item.  The menu is 0 based.
-                MenuItem navigationRequestsMenuItem = navigationMenu.getItem(6);
+                // Get a handle for the navigation requests menu item.
+                MenuItem navigationRequestsMenuItem = navigationMenu.findItem(R.id.requests);
 
                 // 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()));
@@ -5942,9 +6137,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 // Get the preferences.
                 boolean scrollAppBar = sharedPreferences.getBoolean("scroll_app_bar", true);
 
-                // Get a handler for the app bar layout.
-                AppBarLayout appBarLayout = findViewById(R.id.appbar_layout);
-
                 // Set the top padding of the swipe refresh layout according to the app bar scrolling preference.  This can't be done in `appAppSettings()` because the app bar is not yet populated there.
                 if (scrollAppBar || (inFullScreenBrowsingMode && hideAppBar)) {
                     // No padding is needed because it will automatically be placed below the app bar layout due to the scrolling layout behavior.
@@ -5969,30 +6161,19 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 // Reset the requests counters.
                 nestedScrollWebView.resetRequestsCounters();
 
-                // TODO.  Make the background of a new tab match the theme.
-                // If night mode is enabled, hide `mainWebView` until after the night mode CSS is applied.
-                if (nestedScrollWebView.getNightMode()) {
-                    nestedScrollWebView.setVisibility(View.INVISIBLE);
-                } else {
-                    nestedScrollWebView.setVisibility(View.VISIBLE);
-                }
-
-                // Hide the keyboard.
-                inputMethodManager.hideSoftInputFromWindow(nestedScrollWebView.getWindowToken(), 0);
-
                 // Get the current page position.
                 int currentPagePosition = webViewPagerAdapter.getPositionForId(nestedScrollWebView.getWebViewFragmentId());
 
-                // Update the URL text bar if the page is currently selected.
-                if (tabLayout.getSelectedTabPosition() == currentPagePosition) {
-                    // Clear the focus from the URL edit text.
-                    urlEditText.clearFocus();
-
+                // Update the URL text bar if the page is currently selected and the URL edit text is not currently being edited.
+                if ((tabLayout.getSelectedTabPosition() == currentPagePosition) && !urlEditText.hasFocus()) {
                     // Display the formatted URL text.
                     urlEditText.setText(url);
 
                     // Apply text highlighting to `urlTextBox`.
                     highlightUrlText();
+
+                    // Hide the keyboard.
+                    inputMethodManager.hideSoftInputFromWindow(nestedScrollWebView.getWindowToken(), 0);
                 }
 
                 // Reset the list of host IP addresses.
@@ -6021,10 +6202,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                         int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
 
                         // Set the stop icon according to the theme.
-                        if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
-                            refreshMenuItem.setIcon(R.drawable.close_night);
-                        } else {
+                        if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
                             refreshMenuItem.setIcon(R.drawable.close_day);
+                        } else {
+                            refreshMenuItem.setIcon(R.drawable.close_night);
                         }
                     }
                 }
@@ -6054,15 +6235,15 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                         int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
 
                         // Set the icon according to the theme.
-                        if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
-                            refreshMenuItem.setIcon(R.drawable.refresh_enabled_night);
-                        } else {
+                        if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
                             refreshMenuItem.setIcon(R.drawable.refresh_enabled_day);
+                        } else {
+                            refreshMenuItem.setIcon(R.drawable.refresh_enabled_night);
                         }
                     }
                 }
 
-                // Clear the cache and history if Incognito Mode is enabled.
+                // Clear the cache, history, and logcat if Incognito Mode is enabled.
                 if (incognitoModeEnabled) {
                     // Clear the cache.  `true` includes disk files.
                     nestedScrollWebView.clearCache(true);
@@ -6082,20 +6263,22 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                         // Delete the secondary `Service Worker` cache directory.
                         // A `String[]` must be used because the directory contains a space and `Runtime.exec` will not escape the string correctly otherwise.
                         Runtime.getRuntime().exec(new String[]{"rm", "-rf", privateDataDirectoryString + "/app_webview/Service Worker/"});
-                    } catch (IOException e) {
+                    } catch (IOException exception) {
                         // Do nothing if an error is thrown.
                     }
+
+                    // Clear the logcat.
+                    try {
+                        // Clear the logcat.  `-c` clears the logcat.  `-b all` clears all the buffers (instead of just crash, main, and system).
+                        Runtime.getRuntime().exec("logcat -b all -c");
+                    } catch (IOException exception) {
+                        // Do nothing.
+                    }
                 }
 
                 // Get the current page position.
                 int currentPagePosition = webViewPagerAdapter.getPositionForId(nestedScrollWebView.getWebViewFragmentId());
 
-                // Check the current website information against any pinned domain information if the current IP addresses have been loaded.
-                if ((nestedScrollWebView.hasPinnedSslCertificate() || nestedScrollWebView.hasPinnedIpAddresses()) && nestedScrollWebView.hasCurrentIpAddresses() &&
-                        !nestedScrollWebView.ignorePinnedDomainInformation()) {
-                    CheckPinnedMismatchHelper.checkPinnedMismatch(getSupportFragmentManager(), nestedScrollWebView);
-                }
-
                 // Get the current URL from the nested scroll WebView.  This is more accurate than using the URL passed into the method, which is sometimes not the final one.
                 String currentUrl = nestedScrollWebView.getUrl();
 
@@ -6118,7 +6301,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                         inputMethodManager.showSoftInput(urlEditText, 0);
 
                         // Apply the domain settings.  This clears any settings from the previous domain.
-                        applyDomainSettings(nestedScrollWebView, "", true, false);
+                        applyDomainSettings(nestedScrollWebView, "", true, false, false);
 
                         // Only populate the title text view if the tab has been fully created.
                         if (tab != null) {
@@ -6212,20 +6395,23 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             }
         });
 
-        // Check to see if this is the first page.
-        if (pageNumber == 0) {
+        // Check to see if the state is being restored.
+        if (restoringState) {  // The state is being restored.
+            // Resume the nested scroll WebView JavaScript timers.
+            nestedScrollWebView.resumeTimers();
+        } else if (pageNumber == 0) {  // The first page is being loaded.
             // Set this nested scroll WebView as the current WebView.
             currentWebView = nestedScrollWebView;
 
-            // Apply the app settings from the shared preferences.
-            applyAppSettings();
-
             // Initialize the URL to load string.
             String urlToLoadString;
 
             // Get the intent that started the app.
             Intent launchingIntent = getIntent();
 
+            // Reset the intent.  This prevents a duplicate tab from being created on restart.
+            setIntent(new Intent());
+
             // Get the information from the intent.
             String launchingIntentAction = launchingIntent.getAction();
             Uri launchingIntentUriData = launchingIntent.getData();
@@ -6247,6 +6433,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             } else if (launchingIntentUriData != null){  // The intent contains a URL.
                 // Store the URL.
                 urlToLoadString = launchingIntentUriData.toString();
+
+                // Reset the intent.  This prevents a duplicate tab from being created on a subsequent restart if loading an link from a new intent on restart.
+                // For example, this prevents a duplicate tab if a link is loaded from the Guide after changing the theme in the guide and then changing the theme again in the main activity.
+                setIntent(new Intent());
+            } else if (!url.equals("")) {  // The activity has been restarted.
+                // Load the saved URL.
+                urlToLoadString = url;
             } else {  // The is no URL in the intent.
                 // Store the homepage to be loaded.
                 urlToLoadString = sharedPreferences.getString("homepage", getString(R.string.homepage_default_value));
@@ -6259,11 +6452,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 loadUrl(nestedScrollWebView, urlToLoadString);
             }
         } else {  // This is not the first tab.
-            // Apply the domain settings.
-            applyDomainSettings(nestedScrollWebView, url, false, false);
-
             // Load the URL.
-            nestedScrollWebView.loadUrl(url, customHeaders);
+            loadUrl(nestedScrollWebView, url);
 
             // Set the focus and display the keyboard if the URL is blank.
             if (url.equals("")) {