]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/blobdiff - app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java
Allow specifying any font size. https://redmine.stoutner.com/issues/504
[PrivacyBrowserAndroid.git] / app / src / main / java / com / stoutner / privacybrowser / activities / MainWebViewActivity.java
index af6f05c4ca57691d395edc18471db3a9e220ec71..252609212a83251a57f041d7bd437fff1680d09b 100644 (file)
@@ -73,6 +73,7 @@ import android.webkit.CookieManager;
 import android.webkit.HttpAuthHandler;
 import android.webkit.SslErrorHandler;
 import android.webkit.ValueCallback;
+import android.webkit.WebBackForwardList;
 import android.webkit.WebChromeClient;
 import android.webkit.WebResourceResponse;
 import android.webkit.WebSettings;
@@ -128,7 +129,9 @@ import com.stoutner.privacybrowser.dialogs.DownloadImageDialog;
 import com.stoutner.privacybrowser.dialogs.DownloadLocationPermissionDialog;
 import com.stoutner.privacybrowser.dialogs.EditBookmarkDialog;
 import com.stoutner.privacybrowser.dialogs.EditBookmarkFolderDialog;
+import com.stoutner.privacybrowser.dialogs.FontSizeDialog;
 import com.stoutner.privacybrowser.dialogs.HttpAuthenticationDialog;
+import com.stoutner.privacybrowser.dialogs.PinnedMismatchDialog;
 import com.stoutner.privacybrowser.dialogs.SaveWebpageImageDialog;
 import com.stoutner.privacybrowser.dialogs.SslCertificateErrorDialog;
 import com.stoutner.privacybrowser.dialogs.StoragePermissionDialog;
@@ -159,13 +162,14 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 
 // AppCompatActivity from android.support.v7.app.AppCompatActivity must be used to have access to the SupportActionBar until the minimum API is >= 21.
 public class MainWebViewActivity extends AppCompatActivity implements CreateBookmarkDialog.CreateBookmarkListener, CreateBookmarkFolderDialog.CreateBookmarkFolderListener,
         DownloadFileDialog.DownloadFileListener, DownloadImageDialog.DownloadImageListener, DownloadLocationPermissionDialog.DownloadLocationPermissionDialogListener, EditBookmarkDialog.EditBookmarkListener,
-        EditBookmarkFolderDialog.EditBookmarkFolderListener, NavigationView.OnNavigationItemSelectedListener, PopulateBlocklists.PopulateBlocklistsListener, SaveWebpageImageDialog.SaveWebpageImageListener,
-        StoragePermissionDialog.StoragePermissionDialogListener, WebViewTabFragment.NewTabListener {
+        EditBookmarkFolderDialog.EditBookmarkFolderListener, FontSizeDialog.UpdateFontSizeListener, NavigationView.OnNavigationItemSelectedListener, PinnedMismatchDialog.PinnedMismatchListener, PopulateBlocklists.PopulateBlocklistsListener, SaveWebpageImageDialog.SaveWebpageImageListener,
+        StoragePermissionDialog.StoragePermissionDialogListener, UrlHistoryDialog.NavigateHistoryListener, WebViewTabFragment.NewTabListener {
 
     // `orbotStatus` is public static so it can be accessed from `OrbotProxyHelper`.  It is also used in `onCreate()`, `onResume()`, and `applyProxyThroughOrbot()`.
     public static String orbotStatus;
@@ -215,6 +219,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     private ArrayList<List<String[]>> easyPrivacy;
     private ArrayList<List<String[]>> fanboysAnnoyanceList;
     private ArrayList<List<String[]>> fanboysSocialList;
+    private ArrayList<List<String[]>> ultraList;
     private ArrayList<List<String[]>> ultraPrivacy;
 
     // `webViewDefaultUserAgent` is used in `onCreate()` and `onPrepareOptionsMenu()`.
@@ -387,6 +392,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
     @Override
     protected void onNewIntent(Intent intent) {
+        // Run the default commands.
+        super.onNewIntent(intent);
+
         // Replace the intent that started the app with this one.
         setIntent(intent);
 
@@ -432,7 +440,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                     loadingNewIntent = true;
 
                     // Add a new tab.
-                    addNewTab(url);
+                    addNewTab(url, true);
                 } else {  // Load the URL in the current tab.
                     // Make it so.
                     loadUrl(url);
@@ -725,6 +733,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         MenuItem easyPrivacyMenuItem = menu.findItem(R.id.easyprivacy);
         MenuItem fanboysAnnoyanceListMenuItem = menu.findItem(R.id.fanboys_annoyance_list);
         MenuItem fanboysSocialBlockingListMenuItem = menu.findItem(R.id.fanboys_social_blocking_list);
+        MenuItem ultraListMenuItem = menu.findItem(R.id.ultralist);
         MenuItem ultraPrivacyMenuItem = menu.findItem(R.id.ultraprivacy);
         MenuItem blockAllThirdPartyRequestsMenuItem = menu.findItem(R.id.block_all_third_party_requests);
         MenuItem fontSizeMenuItem = menu.findItem(R.id.font_size);
@@ -759,11 +768,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             // Set the status of the menu item checkboxes.
             domStorageMenuItem.setChecked(currentWebView.getSettings().getDomStorageEnabled());
             saveFormDataMenuItem.setChecked(currentWebView.getSettings().getSaveFormData());  // Form data can be removed once the minimum API >= 26.
-            easyListMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.EASY_LIST));
-            easyPrivacyMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.EASY_PRIVACY));
+            easyListMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYLIST));
+            easyPrivacyMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYPRIVACY));
             fanboysAnnoyanceListMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST));
             fanboysSocialBlockingListMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST));
-            ultraPrivacyMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRA_PRIVACY));
+            ultraListMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRALIST));
+            ultraPrivacyMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRAPRIVACY));
             blockAllThirdPartyRequestsMenuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.THIRD_PARTY_REQUESTS));
             swipeToRefreshMenuItem.setChecked(currentWebView.getSwipeToRefresh());
             wideViewportMenuItem.setChecked(currentWebView.getSettings().getUseWideViewPort());
@@ -772,11 +782,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
             // Initialize the display names for the blocklists with the number of blocked requests.
             blocklistsMenuItem.setTitle(getString(R.string.blocklists) + " - " + currentWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS));
-            easyListMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.EASY_LIST) + " - " + getString(R.string.easylist));
-            easyPrivacyMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.EASY_PRIVACY) + " - " + getString(R.string.easyprivacy));
+            easyListMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.EASYLIST) + " - " + getString(R.string.easylist));
+            easyPrivacyMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.EASYPRIVACY) + " - " + getString(R.string.easyprivacy));
             fanboysAnnoyanceListMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST) + " - " + getString(R.string.fanboys_annoyance_list));
             fanboysSocialBlockingListMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST) + " - " + getString(R.string.fanboys_social_blocking_list));
-            ultraPrivacyMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.ULTRA_PRIVACY) + " - " + getString(R.string.ultraprivacy));
+            ultraListMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.ULTRALIST) + " - " + getString(R.string.ultralist));
+            ultraPrivacyMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.ULTRAPRIVACY) + " - " + getString(R.string.ultraprivacy));
             blockAllThirdPartyRequestsMenuItem.setTitle(currentWebView.getRequestsCount(NestedScrollWebView.THIRD_PARTY_REQUESTS) + " - " + getString(R.string.block_all_third_party_requests));
 
             // Only modify third-party cookies if the API >= 21.
@@ -806,14 +817,16 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         File localStorageDirectory = new File (privateDataDirectoryString + "/app_webview/Local Storage/");
         int localStorageDirectoryNumberOfFiles = 0;
         if (localStorageDirectory.exists()) {
-            localStorageDirectoryNumberOfFiles = localStorageDirectory.list().length;
+            // `Objects.requireNonNull` removes a lint warning that `localStorageDirectory.list` might produce a null pointed exception if it is dereferenced.
+            localStorageDirectoryNumberOfFiles = Objects.requireNonNull(localStorageDirectory.list()).length;
         }
 
         // Get a count of the number of files in the IndexedDB directory.
         File indexedDBDirectory = new File (privateDataDirectoryString + "/app_webview/IndexedDB");
         int indexedDBDirectoryNumberOfFiles = 0;
         if (indexedDBDirectory.exists()) {
-            indexedDBDirectoryNumberOfFiles = indexedDBDirectory.list().length;
+            // `Objects.requireNonNull` removes a lint warning that `indexedDBDirectory.list` might produce a null pointed exception if it is dereferenced.
+            indexedDBDirectoryNumberOfFiles = Objects.requireNonNull(indexedDBDirectory.list()).length;
         }
 
         // Enable Clear DOM Storage if there is any.
@@ -863,61 +876,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             menu.findItem(R.id.user_agent_custom).setChecked(true);
         }
 
-        // Instantiate the font size title and the selected font size menu item.
-        String fontSizeTitle;
-        MenuItem selectedFontSizeMenuItem;
-
-        // Prepare the font size title and current size menu item.
-        switch (fontSize) {
-            case 25:
-                fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.twenty_five_percent);
-                selectedFontSizeMenuItem = menu.findItem(R.id.font_size_twenty_five_percent);
-                break;
-
-            case 50:
-                fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.fifty_percent);
-                selectedFontSizeMenuItem = menu.findItem(R.id.font_size_fifty_percent);
-                break;
-
-            case 75:
-                fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.seventy_five_percent);
-                selectedFontSizeMenuItem = menu.findItem(R.id.font_size_seventy_five_percent);
-                break;
-
-            case 100:
-                fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.one_hundred_percent);
-                selectedFontSizeMenuItem = menu.findItem(R.id.font_size_one_hundred_percent);
-                break;
-
-            case 125:
-                fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.one_hundred_twenty_five_percent);
-                selectedFontSizeMenuItem = menu.findItem(R.id.font_size_one_hundred_twenty_five_percent);
-                break;
-
-            case 150:
-                fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.one_hundred_fifty_percent);
-                selectedFontSizeMenuItem = menu.findItem(R.id.font_size_one_hundred_fifty_percent);
-                break;
-
-            case 175:
-                fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.one_hundred_seventy_five_percent);
-                selectedFontSizeMenuItem = menu.findItem(R.id.font_size_one_hundred_seventy_five_percent);
-                break;
-
-            case 200:
-                fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.two_hundred_percent);
-                selectedFontSizeMenuItem = menu.findItem(R.id.font_size_two_hundred_percent);
-                break;
-
-            default:
-                fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.one_hundred_percent);
-                selectedFontSizeMenuItem = menu.findItem(R.id.font_size_one_hundred_percent);
-                break;
-        }
-
-        // Set the font size title and select the current size menu item.
-        fontSizeMenuItem.setTitle(fontSizeTitle);
-        selectedFontSizeMenuItem.setChecked(true);
+        // Set the font size title.
+        fontSizeMenuItem.setTitle(getString(R.string.font_size) + " - " + fontSize + "%");
 
         // Run all the other default commands.
         super.onPrepareOptionsMenu(menu);
@@ -930,23 +890,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     // Remove Android Studio's warning about the dangers of using SetJavaScriptEnabled.
     @SuppressLint("SetJavaScriptEnabled")
     public boolean onOptionsItemSelected(MenuItem menuItem) {
-        // Reenter full screen browsing mode if it was interrupted by the options menu.  <https://redmine.stoutner.com/issues/389>
-        if (inFullScreenBrowsingMode) {
-            // Remove the translucent status flag.  This is necessary so the root frame layout can fill the entire screen.
-            getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
-
-            FrameLayout rootFrameLayout = findViewById(R.id.root_framelayout);
-
-            /* 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);
-        }
-
         // Get the selected menu item ID.
         int menuItemId = menuItem.getItemId();
 
@@ -1290,10 +1233,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
             case R.id.easylist:
                 // Toggle the EasyList status.
-                currentWebView.enableBlocklist(NestedScrollWebView.EASY_LIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.EASY_LIST));
+                currentWebView.enableBlocklist(NestedScrollWebView.EASYLIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYLIST));
 
                 // Update the menu checkbox.
-                menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.EASY_LIST));
+                menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYLIST));
 
                 // Reload the current WebView.
                 currentWebView.reload();
@@ -1303,10 +1246,10 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
             case R.id.easyprivacy:
                 // Toggle the EasyPrivacy status.
-                currentWebView.enableBlocklist(NestedScrollWebView.EASY_PRIVACY, !currentWebView.isBlocklistEnabled(NestedScrollWebView.EASY_PRIVACY));
+                currentWebView.enableBlocklist(NestedScrollWebView.EASYPRIVACY, !currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYPRIVACY));
 
                 // Update the menu checkbox.
-                menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.EASY_PRIVACY));
+                menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.EASYPRIVACY));
 
                 // Reload the current WebView.
                 currentWebView.reload();
@@ -1344,12 +1287,25 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 // Consume the event.
                 return true;
 
+            case R.id.ultralist:
+                // Toggle the UltraList status.
+                currentWebView.enableBlocklist(NestedScrollWebView.ULTRALIST, !currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRALIST));
+
+                // Update the menu checkbox.
+                menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRALIST));
+
+                // Reload the current WebView.
+                currentWebView.reload();
+
+                // Consume the event.
+                return true;
+
             case R.id.ultraprivacy:
                 // Toggle the UltraPrivacy status.
-                currentWebView.enableBlocklist(NestedScrollWebView.ULTRA_PRIVACY, !currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRA_PRIVACY));
+                currentWebView.enableBlocklist(NestedScrollWebView.ULTRAPRIVACY, !currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRAPRIVACY));
 
                 // Update the menu checkbox.
-                menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRA_PRIVACY));
+                menuItem.setChecked(currentWebView.isBlocklistEnabled(NestedScrollWebView.ULTRAPRIVACY));
 
                 // Reload the current WebView.
                 currentWebView.reload();
@@ -1500,58 +1456,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 // Consume the event.
                 return true;
 
-            case R.id.font_size_twenty_five_percent:
-                // Set the font size.
-                currentWebView.getSettings().setTextZoom(25);
-
-                // Consume the event.
-                return true;
-
-            case R.id.font_size_fifty_percent:
-                // Set the font size.
-                currentWebView.getSettings().setTextZoom(50);
-
-                // Consume the event.
-                return true;
-
-            case R.id.font_size_seventy_five_percent:
-                // Set the font size.
-                currentWebView.getSettings().setTextZoom(75);
-
-                // Consume the event.
-                return true;
-
-            case R.id.font_size_one_hundred_percent:
-                // Set the font size.
-                currentWebView.getSettings().setTextZoom(100);
+            case R.id.font_size:
+                // Instantiate the font size dialog.
+                DialogFragment fontSizeDialogFragment = FontSizeDialog.displayDialog(currentWebView.getSettings().getTextZoom());
 
-                // Consume the event.
-                return true;
-
-            case R.id.font_size_one_hundred_twenty_five_percent:
-                // Set the font size.
-                currentWebView.getSettings().setTextZoom(125);
-
-                // Consume the event.
-                return true;
-
-            case R.id.font_size_one_hundred_fifty_percent:
-                // Set the font size.
-                currentWebView.getSettings().setTextZoom(150);
-
-                // Consume the event.
-                return true;
-
-            case R.id.font_size_one_hundred_seventy_five_percent:
-                // Set the font size.
-                currentWebView.getSettings().setTextZoom(175);
-
-                // Consume the event.
-                return true;
-
-            case R.id.font_size_two_hundred_percent:
-                // Set the font size.
-                currentWebView.getSettings().setTextZoom(200);
+                // Show the font size dialog.
+                fontSizeDialogFragment.show(getSupportFragmentManager(), getString(R.string.font_size));
 
                 // Consume the event.
                 return true;
@@ -1803,11 +1713,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
             case R.id.back:
                 if (currentWebView.canGoBack()) {
-                    // Reset the current domain name so that navigation works if third-party requests are blocked.
-                    currentWebView.resetCurrentDomainName();
+                    // Get the current web back forward list.
+                    WebBackForwardList webBackForwardList = currentWebView.copyBackForwardList();
 
-                    // Set navigating history so that the domain settings are applied when the new URL is loaded.
-                    currentWebView.setNavigatingHistory(true);
+                    // 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();
@@ -1816,11 +1729,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
             case R.id.forward:
                 if (currentWebView.canGoForward()) {
-                    // Reset the current domain name so that navigation works if third-party requests are blocked.
-                    currentWebView.resetCurrentDomainName();
+                    // Get the current web back forward list.
+                    WebBackForwardList webBackForwardList = currentWebView.copyBackForwardList();
+
+                    // Get the next entry URL.
+                    String nextUrl = webBackForwardList.getItemAtIndex(webBackForwardList.getCurrentIndex() + 1).getUrl();
 
-                    // Set navigating history so that the domain settings are applied when the new URL is loaded.
-                    currentWebView.setNavigatingHistory(true);
+                    // Apply the domain settings.
+                    applyDomainSettings(currentWebView, nextUrl, false, false);
 
                     // Load the next website in the history.
                     currentWebView.goForward();
@@ -1941,7 +1857,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                 // 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],
-                        ultraPrivacy.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);
@@ -1969,7 +1885,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     }
 
     @Override
-    public void onConfigurationChanged(Configuration newConfig) {
+    public void onConfigurationChanged(@NonNull Configuration newConfig) {
         // Run the default commands.
         super.onConfigurationChanged(newConfig);
 
@@ -2025,8 +1941,17 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                 // Add an Open in New Tab entry.
                 menu.add(R.string.open_in_new_tab).setOnMenuItemClickListener((MenuItem item) -> {
-                    // Load the link URL in a new tab.
-                    addNewTab(linkUrl);
+                    // Load the link URL in a new tab and move to it.
+                    addNewTab(linkUrl, true);
+
+                    // Consume the event.
+                    return true;
+                });
+
+                // Add an Open in Background entry.
+                menu.add(R.string.open_in_background).setOnMenuItemClickListener((MenuItem item) -> {
+                    // Load the link URL in a new tab but do not move to it.
+                    addNewTab(linkUrl, false);
 
                     // Consume the event.
                     return true;
@@ -2151,9 +2076,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 menu.setHeaderTitle(imageUrl);
 
                 // Add an Open in New Tab entry.
-                menu.add(R.string.open_in_new_tab).setOnMenuItemClickListener((MenuItem item) -> {
-                    // Load the image URL in a new tab.
-                    addNewTab(imageUrl);
+                menu.add(R.string.open_image_in_new_tab).setOnMenuItemClickListener((MenuItem item) -> {
+                    // Load the image in a new tab.
+                    addNewTab(imageUrl, true);
 
                     // Consume the event.
                     return true;
@@ -2259,8 +2184,26 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                 // Add an Open in New Tab entry.
                 menu.add(R.string.open_in_new_tab).setOnMenuItemClickListener((MenuItem item) -> {
-                    // Load the link URL in a new tab.
-                    addNewTab(linkUrl);
+                    // Load the link URL in a new tab and move to it.
+                    addNewTab(linkUrl, true);
+
+                    // Consume the event.
+                    return true;
+                });
+
+                // Add an Open in Background entry.
+                menu.add(R.string.open_in_background).setOnMenuItemClickListener((MenuItem item) -> {
+                    // Lod the link URL in a new tab but do not move to it.
+                    addNewTab(linkUrl, false);
+
+                    // Consume the event.
+                    return true;
+                });
+
+                // Add an Open Image in New Tab entry.
+                menu.add(R.string.open_image_in_new_tab).setOnMenuItemClickListener((MenuItem item) -> {
+                    // Load the image in a new tab and move to it.
+                    addNewTab(imageUrl, true);
 
                     // Consume the event.
                     return true;
@@ -2351,9 +2294,15 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Get a handle for the bookmarks list view.
         ListView bookmarksListView = findViewById(R.id.bookmarks_drawer_listview);
 
+        // Get the dialog.
+        Dialog dialog = dialogFragment.getDialog();
+
+        // Remove the incorrect lint warning below that the dialog might be null.
+        assert dialog != null;
+
         // Get the views from the dialog fragment.
-        EditText createBookmarkNameEditText = dialogFragment.getDialog().findViewById(R.id.create_bookmark_name_edittext);
-        EditText createBookmarkUrlEditText = dialogFragment.getDialog().findViewById(R.id.create_bookmark_url_edittext);
+        EditText createBookmarkNameEditText = dialog.findViewById(R.id.create_bookmark_name_edittext);
+        EditText createBookmarkUrlEditText = dialog.findViewById(R.id.create_bookmark_url_edittext);
 
         // Extract the strings from the edit texts.
         String bookmarkNameString = createBookmarkNameEditText.getText().toString();
@@ -2389,10 +2338,16 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         // Get a handle for the bookmarks list view.
         ListView bookmarksListView = findViewById(R.id.bookmarks_drawer_listview);
 
+        // 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 in the dialog fragment.
-        EditText createFolderNameEditText = dialogFragment.getDialog().findViewById(R.id.create_folder_name_edittext);
-        RadioButton defaultFolderIconRadioButton = dialogFragment.getDialog().findViewById(R.id.create_folder_default_icon_radiobutton);
-        ImageView folderIconImageView = dialogFragment.getDialog().findViewById(R.id.create_folder_default_icon);
+        EditText createFolderNameEditText = dialog.findViewById(R.id.create_folder_name_edittext);
+        RadioButton defaultFolderIconRadioButton = dialog.findViewById(R.id.create_folder_default_icon_radiobutton);
+        ImageView folderIconImageView = dialog.findViewById(R.id.create_folder_default_icon);
 
         // Get new folder name string.
         String folderNameString = createFolderNameEditText.getText().toString();
@@ -2445,10 +2400,16 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
     @Override
     public void onSaveBookmark(DialogFragment dialogFragment, int selectedBookmarkDatabaseId, Bitmap favoriteIconBitmap) {
-        // Get handles for the views from `dialogFragment`.
-        EditText editBookmarkNameEditText = dialogFragment.getDialog().findViewById(R.id.edit_bookmark_name_edittext);
-        EditText editBookmarkUrlEditText = dialogFragment.getDialog().findViewById(R.id.edit_bookmark_url_edittext);
-        RadioButton currentBookmarkIconRadioButton = dialogFragment.getDialog().findViewById(R.id.edit_bookmark_current_icon_radiobutton);
+        // 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 the dialog.
+        EditText editBookmarkNameEditText = dialog.findViewById(R.id.edit_bookmark_name_edittext);
+        EditText editBookmarkUrlEditText = dialog.findViewById(R.id.edit_bookmark_url_edittext);
+        RadioButton currentBookmarkIconRadioButton = dialog.findViewById(R.id.edit_bookmark_current_icon_radiobutton);
 
         // Store the bookmark strings.
         String bookmarkNameString = editBookmarkNameEditText.getText().toString();
@@ -2480,11 +2441,17 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
     @Override
     public void onSaveBookmarkFolder(DialogFragment dialogFragment, int selectedFolderDatabaseId, Bitmap favoriteIconBitmap) {
+        // 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`.
-        EditText editFolderNameEditText = dialogFragment.getDialog().findViewById(R.id.edit_folder_name_edittext);
-        RadioButton currentFolderIconRadioButton = dialogFragment.getDialog().findViewById(R.id.edit_folder_current_icon_radiobutton);
-        RadioButton defaultFolderIconRadioButton = dialogFragment.getDialog().findViewById(R.id.edit_folder_default_icon_radiobutton);
-        ImageView defaultFolderIconImageView = dialogFragment.getDialog().findViewById(R.id.edit_folder_default_icon_imageview);
+        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);
+        ImageView defaultFolderIconImageView = dialog.findViewById(R.id.edit_folder_default_icon_imageview);
 
         // Get the new folder name.
         String newFolderNameString = editFolderNameEditText.getText().toString();
@@ -2652,8 +2619,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 downloadRequest.addRequestHeader("Cookie", cookies);
             }
 
+            // Get the dialog.
+            Dialog dialog = dialogFragment.getDialog();
+
+            // Remove the incorrect lint warning below that the dialog might be null.
+            assert dialog != null;
+
             // Get the file name from the dialog fragment.
-            EditText downloadImageNameEditText = dialogFragment.getDialog().findViewById(R.id.download_image_name);
+            EditText downloadImageNameEditText = dialog.findViewById(R.id.download_image_name);
             String imageName = downloadImageNameEditText.getText().toString();
 
             // Specify the download location.
@@ -2707,8 +2680,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 downloadRequest.addRequestHeader("Cookie", cookies);
             }
 
-            // Get the file name from the dialog fragment.
-            EditText downloadFileNameEditText = dialogFragment.getDialog().findViewById(R.id.download_file_name);
+            // Get the dialog.
+            Dialog dialog = dialogFragment.getDialog();
+
+            // Remove the incorrect lint warning below that the dialog might be null.
+            assert dialog != null;
+
+            // Get the file name from the dialog.
+            EditText downloadFileNameEditText = dialog.findViewById(R.id.download_file_name);
             String fileName = downloadFileNameEditText.getText().toString();
 
             // Specify the download location.
@@ -2739,7 +2718,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         }
     }
 
-    // Override `onBackPressed` to handle the navigation drawer and and the WebView.
+    // 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.
@@ -2760,12 +2739,86 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 // Load the new folder.
                 loadBookmarksFolder();
             }
+        } else if (displayingFullScreenVideo) {  // A full screen video is shown.
+            // Get a handle for the layouts.
+            FrameLayout rootFrameLayout = findViewById(R.id.root_framelayout);
+            RelativeLayout mainContentRelativeLayout = findViewById(R.id.main_content_relativelayout);
+            FrameLayout fullScreenVideoFrameLayout = findViewById(R.id.full_screen_video_framelayout);
+
+            // Re-enable the screen timeout.
+            fullScreenVideoFrameLayout.setKeepScreenOn(false);
+
+            // Unset the full screen video flag.
+            displayingFullScreenVideo = false;
+
+            // Remove all the views from the full screen video frame layout.
+            fullScreenVideoFrameLayout.removeAllViews();
+
+            // Hide the full screen video frame layout.
+            fullScreenVideoFrameLayout.setVisibility(View.GONE);
+
+            // Enable the sliding drawers.
+            drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
+
+            // Show the main content relative layout.
+            mainContentRelativeLayout.setVisibility(View.VISIBLE);
+
+            // Apply the appropriate full screen mode flags.
+            if (fullScreenBrowsingModeEnabled && inFullScreenBrowsingMode) {  // Privacy Browser is currently in full screen browsing mode.
+                // Hide the app bar if specified.
+                if (hideAppBar) {
+                    // Get handles for the views.
+                    LinearLayout tabsLinearLayout = findViewById(R.id.tabs_linearlayout);
+                    ActionBar actionBar = getSupportActionBar();
+
+                    // Remove the incorrect lint warning below that the action bar might be null.
+                    assert actionBar != null;
+
+                    // Hide the tab linear layout.
+                    tabsLinearLayout.setVisibility(View.GONE);
+
+                    // Hide the action bar.
+                    actionBar.hide();
+                }
+
+                // Hide the banner ad in the free flavor.
+                if (BuildConfig.FLAVOR.contentEquals("free")) {
+                    AdHelper.hideAd(findViewById(R.id.adview));
+                }
+
+                // Remove the translucent status flag.  This is necessary so the root frame layout can fill the entire screen.
+                getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
+
+                /* 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 {  // Switch to normal viewing mode.
+                // Remove the `SYSTEM_UI` flags from the root frame layout.
+                rootFrameLayout.setSystemUiVisibility(0);
+
+                // Add the translucent status flag.
+                getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
+            }
+
+            // Reload the ad for the free flavor if not in full screen mode.
+            if (BuildConfig.FLAVOR.contentEquals("free") && !inFullScreenBrowsingMode) {
+                // Reload the ad.
+                AdHelper.loadAd(findViewById(R.id.adview), getApplicationContext(), getString(R.string.ad_unit_id));
+            }
         } else if (currentWebView.canGoBack()) {  // There is at least one item in the current WebView history.
-            // Reset the current domain name so that navigation works if third-party requests are blocked.
-            currentWebView.resetCurrentDomainName();
+            // Get the current web back forward list.
+            WebBackForwardList webBackForwardList = currentWebView.copyBackForwardList();
 
-            // Set navigating history so that the domain settings are applied when the new URL is loaded.
-            currentWebView.setNavigatingHistory(true);
+            // Get the previous entry URL.
+            String previousUrl = webBackForwardList.getItemAtIndex(webBackForwardList.getCurrentIndex() - 1).getUrl();
+
+            // Apply the domain settings.
+            applyDomainSettings(currentWebView, previousUrl, false, false);
 
             // Go back.
             currentWebView.goBack();
@@ -2773,8 +2826,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             // Close the current tab.
             closeCurrentTab();
         } else {  // There isn't anything to do in Privacy Browser.
-            // Run the default commands.
-            super.onBackPressed();
+            // Close Privacy Browser.  `finishAndRemoveTask()` also removes Privacy Browser from the recent app list.
+            if (Build.VERSION.SDK_INT >= 21) {
+                finishAndRemoveTask();
+            } else {
+                finish();
+            }
 
             // Manually kill Privacy Browser.  Otherwise, it is glitchy when restarted.
             System.exit(0);
@@ -2784,6 +2841,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
     // Process the results of a file browse.
     @Override
     public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        // Run the default commands.
+        super.onActivityResult(requestCode, resultCode, data);
+
         // Run the commands that correlate to the specified request code.
         switch (requestCode) {
             case FILE_UPLOAD_REQUEST_CODE:
@@ -2805,17 +2865,23 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                         // Get a handle for the save webpage image dialog.
                         Dialog saveWebpageImageDialog = saveWebpageImageDialogFragment.getDialog();
 
+                        // Remove the incorrect lint warning below that the dialog might be null.
+                        assert saveWebpageImageDialog != null;
+
                         // Get a handle for the file name edit text.
                         EditText fileNameEditText = saveWebpageImageDialog.findViewById(R.id.file_name_edittext);
 
                         // Instantiate the file name helper.
                         FileNameHelper fileNameHelper = new FileNameHelper();
 
-                        // Convert the file name URI to a file name path.
-                        String fileNamePath = fileNameHelper.convertUriToFileNamePath(data.getData());
+                        // Get the file path if it isn't null.
+                        if (data.getData() != null) {
+                            // Convert the file name URI to a file name path.
+                            String fileNamePath = fileNameHelper.convertUriToFileNamePath(data.getData());
 
-                        // Set the file name path as the text of the file name edit text.
-                        fileNameEditText.setText(fileNamePath);
+                            // Set the file name path as the text of the file name edit text.
+                            fileNameEditText.setText(fileNamePath);
+                        }
                     }
                 }
                 break;
@@ -2943,10 +3009,41 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         inputMethodManager.hideSoftInputFromWindow(toolbar.getWindowToken(), 0);
     }
 
+    @Override
+    public void onApplyNewFontSize(DialogFragment dialogFragment) {
+        // Get the dialog.
+        Dialog dialog = dialogFragment.getDialog();
+
+        // Remove the incorrect lint warning below tha the dialog might be null.
+        assert dialog != null;
+
+        // Get a handle for the font size edit text.
+        EditText fontSizeEditText = dialog.findViewById(R.id.font_size_edittext);
+
+        // Initialize the new font size variable with the current font size.
+        int newFontSize = currentWebView.getSettings().getTextZoom();
+
+        // Get the font size from the edit text.
+        try {
+            newFontSize = Integer.valueOf(fontSizeEditText.getText().toString());
+        } catch (Exception exception) {
+            // If the edit text does not contain a valid font size do nothing.
+        }
+
+        // Apply the new font size.
+        currentWebView.getSettings().setTextZoom(newFontSize);
+    }
+
     @Override
     public void onSaveWebpageImage(DialogFragment dialogFragment) {
+        // Get the dialog.
+        Dialog dialog = dialogFragment.getDialog();
+
+        // Remove the incorrect lint warning below that the dialog might be null.
+        assert dialog != null;
+
         // Get a handle for the file name edit text.
-        EditText fileNameEditText = dialogFragment.getDialog().findViewById(R.id.file_name_edittext);
+        EditText fileNameEditText = dialog.findViewById(R.id.file_name_edittext);
 
         // Get the file path string.
         saveWebsiteImageFilePath = fileNameEditText.getText().toString();
@@ -3202,7 +3299,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 orbotStatus = intent.getStringExtra("org.torproject.android.intent.extra.STATUS");
 
                 // If Privacy Browser is waiting on Orbot, load the website now that Orbot is connected.
-                if (orbotStatus.equals("ON") && waitingForOrbot) {
+                if ((orbotStatus != null) && orbotStatus.equals("ON") && waitingForOrbot) {
                     // Reset the waiting for Orbot status.
                     waitingForOrbot = false;
 
@@ -3459,10 +3556,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
         bookmarksListView.setOnItemClickListener((parent, view, position, id) -> {
             // Convert the id from long to int to match the format of the bookmarks database.
-            int databaseID = (int) id;
+            int databaseId = (int) id;
+
+            // Get the bookmark cursor for this ID.
+            Cursor bookmarkCursor = bookmarksDatabaseHelper.getBookmark(databaseId);
 
-            // Get the bookmark cursor for this ID and move it to the first row.
-            Cursor bookmarkCursor = bookmarksDatabaseHelper.getBookmark(databaseID);
+            // Move the bookmark cursor to the first row.
             bookmarkCursor.moveToFirst();
 
             // Act upon the bookmark according to the type.
@@ -3495,13 +3594,20 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 // Save the current folder name, which is used in `onSaveEditBookmarkFolder()`.
                 oldFolderNameString = bookmarksCursor.getString(bookmarksCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_NAME));
 
-                // Show the edit bookmark folder `AlertDialog` and name the instance `@string/edit_folder`.
+                // Instantiate the edit folder bookmark dialog.
                 DialogFragment editBookmarkFolderDialog = EditBookmarkFolderDialog.folderDatabaseId(databaseId, currentWebView.getFavoriteOrDefaultIcon());
+
+                // Show the edit folder bookmark dialog.
                 editBookmarkFolderDialog.show(getSupportFragmentManager(), getString(R.string.edit_folder));
             } else {
-                // Show the edit bookmark `AlertDialog` and name the instance `@string/edit_bookmark`.
-                DialogFragment editBookmarkDialog = EditBookmarkDialog.bookmarkDatabaseId(databaseId, currentWebView.getFavoriteOrDefaultIcon());
-                editBookmarkDialog.show(getSupportFragmentManager(), getString(R.string.edit_bookmark));
+                // Get the bookmark cursor for this ID.
+                Cursor bookmarkCursor = bookmarksDatabaseHelper.getBookmark(databaseId);
+
+                // Move the bookmark cursor to the first row.
+                bookmarkCursor.moveToFirst();
+
+                // Load the bookmark in a new tab but do not switch to the tab or close the drawer.
+                addNewTab(bookmarkCursor.getString(bookmarkCursor.getColumnIndex(BookmarksDatabaseHelper.BOOKMARK_URL)), false);
             }
 
             // Consume the event.
@@ -3585,12 +3691,39 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         bareWebView.destroy();
     }
 
+    @Override
+    public void navigateHistory(String url, int steps) {
+        // Apply the domain settings.
+        applyDomainSettings(currentWebView, url, false, false);
+
+        // Load the history entry.
+        currentWebView.goBackOrForward(steps);
+    }
+
+    @Override
+    public void pinnedErrorGoBack() {
+        // 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);
+
+        // Go back.
+        currentWebView.goBack();
+    }
+
     // `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();
 
+        // Store the current URL.
+        nestedScrollWebView.setCurrentUrl(url);
+
         // Parse the URL into a URI.
         Uri uri = Uri.parse(url);
 
@@ -3737,15 +3870,16 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 nestedScrollWebView.getSettings().setDomStorageEnabled(currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_DOM_STORAGE)) == 1);
                 // Form data can be removed once the minimum API >= 26.
                 boolean saveFormData = (currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FORM_DATA)) == 1);
-                nestedScrollWebView.enableBlocklist(NestedScrollWebView.EASY_LIST,
+                nestedScrollWebView.enableBlocklist(NestedScrollWebView.EASYLIST,
                         currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_EASYLIST)) == 1);
-                nestedScrollWebView.enableBlocklist(NestedScrollWebView.EASY_PRIVACY,
+                nestedScrollWebView.enableBlocklist(NestedScrollWebView.EASYPRIVACY,
                         currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_EASYPRIVACY)) == 1);
                 nestedScrollWebView.enableBlocklist(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST,
                         currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FANBOYS_ANNOYANCE_LIST)) == 1);
                 nestedScrollWebView.enableBlocklist(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST,
                         currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST)) == 1);
-                nestedScrollWebView.enableBlocklist(NestedScrollWebView.ULTRA_PRIVACY,
+                nestedScrollWebView.enableBlocklist(NestedScrollWebView.ULTRALIST, currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ULTRALIST)) == 1);
+                nestedScrollWebView.enableBlocklist(NestedScrollWebView.ULTRAPRIVACY,
                         currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_ULTRAPRIVACY)) == 1);
                 nestedScrollWebView.enableBlocklist(NestedScrollWebView.THIRD_PARTY_REQUESTS,
                         currentDomainSettingsCursor.getInt(currentDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.BLOCK_ALL_THIRD_PARTY_REQUESTS)) == 1);
@@ -3838,10 +3972,16 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 }
 
                 // Apply the font size.
-                if (fontSize == 0) {  // Apply the default font size.
-                    nestedScrollWebView.getSettings().setTextZoom(Integer.valueOf(defaultFontSizeString));
-                } else {  // Apply the specified font size.
-                    nestedScrollWebView.getSettings().setTextZoom(fontSize);
+                try {  // Try the specified font size to see if it is valid.
+                    if (fontSize == 0) {  // Apply the default font size.
+                            // Try to set the font size from the value in the app settings.
+                            nestedScrollWebView.getSettings().setTextZoom(Integer.valueOf(defaultFontSizeString));
+                    } else {  // Apply the font size from domain settings.
+                        nestedScrollWebView.getSettings().setTextZoom(fontSize);
+                    }
+                } catch (Exception exception) {  // The specified font size is invalid
+                    // Set the font size to be 100%
+                    nestedScrollWebView.getSettings().setTextZoom(100);
                 }
 
                 // Set the user agent.
@@ -3959,11 +4099,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 boolean defaultThirdPartyCookiesEnabled = sharedPreferences.getBoolean("third_party_cookies", false);
                 nestedScrollWebView.getSettings().setDomStorageEnabled(sharedPreferences.getBoolean("dom_storage", false));
                 boolean saveFormData = sharedPreferences.getBoolean("save_form_data", false);  // Form data can be removed once the minimum API >= 26.
-                nestedScrollWebView.enableBlocklist(NestedScrollWebView.EASY_LIST, sharedPreferences.getBoolean("easylist", true));
-                nestedScrollWebView.enableBlocklist(NestedScrollWebView.EASY_PRIVACY, sharedPreferences.getBoolean("easyprivacy", true));
+                nestedScrollWebView.enableBlocklist(NestedScrollWebView.EASYLIST, sharedPreferences.getBoolean("easylist", true));
+                nestedScrollWebView.enableBlocklist(NestedScrollWebView.EASYPRIVACY, sharedPreferences.getBoolean("easyprivacy", true));
                 nestedScrollWebView.enableBlocklist(NestedScrollWebView.FANBOYS_ANNOYANCE_LIST, sharedPreferences.getBoolean("fanboys_annoyance_list", true));
                 nestedScrollWebView.enableBlocklist(NestedScrollWebView.FANBOYS_SOCIAL_BLOCKING_LIST, sharedPreferences.getBoolean("fanboys_social_blocking_list", true));
-                nestedScrollWebView.enableBlocklist(NestedScrollWebView.ULTRA_PRIVACY, sharedPreferences.getBoolean("ultraprivacy", true));
+                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));
 
@@ -3976,9 +4117,17 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                     nestedScrollWebView.getSettings().setJavaScriptEnabled(defaultJavaScriptEnabled);
                 }
 
-                // Apply the default settings.
+                // Apply the default first-party cookie setting.
                 cookieManager.setAcceptCookie(nestedScrollWebView.getAcceptFirstPartyCookies());
-                nestedScrollWebView.getSettings().setTextZoom(Integer.valueOf(defaultFontSizeString));
+
+                // Apply the default font size setting.
+                try {
+                    // Try to set the font size from the value in the app settings.
+                    nestedScrollWebView.getSettings().setTextZoom(Integer.valueOf(defaultFontSizeString));
+                } catch (Exception exception) {
+                    // If the app settings value is invalid, set the font size to 100%.
+                    nestedScrollWebView.getSettings().setTextZoom(100);
+                }
 
                 // Apply the form data setting if the API < 26.
                 if (Build.VERSION.SDK_INT < 26) {
@@ -4405,6 +4554,16 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             if (url.contains("&fbclid=")) {
                 url = url.substring(0, url.indexOf("&fbclid="));
             }
+
+            // Remove `?fbadid=`.
+            if (url.contains("?fbadid=")) {
+                url = url.substring(0, url.indexOf("?fbadid="));
+            }
+
+            // Remove `&fbadid=`.
+            if (url.contains("&fbadid=")) {
+                url = url.substring(0, url.indexOf("&fbadid="));
+            }
         }
 
         // Sanitize Twitter AMP redirects.
@@ -4425,18 +4584,19 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         easyPrivacy = combinedBlocklists.get(1);
         fanboysAnnoyanceList = combinedBlocklists.get(2);
         fanboysSocialList = combinedBlocklists.get(3);
-        ultraPrivacy = combinedBlocklists.get(4);
+        ultraList = combinedBlocklists.get(4);
+        ultraPrivacy = combinedBlocklists.get(5);
 
         // Add the first tab.
-        addNewTab("");
+        addNewTab("", true);
     }
 
     public void addTab(View view) {
         // Add a new tab with a blank URL.
-        addNewTab("");
+        addNewTab("", true);
     }
 
-    private void addNewTab(String url) {
+    private void addNewTab(String url, boolean moveToTab) {
         // Sanitize the URL.
         url = sanitizeUrl(url);
 
@@ -4460,7 +4620,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
         newTab.setCustomView(R.layout.tab_custom_view);
 
         // Add the new WebView page.
-        webViewPagerAdapter.addPage(newTabNumber, webViewPager, url);
+        webViewPagerAdapter.addPage(newTabNumber, webViewPager, url, moveToTab);
     }
 
     public void closeTab(View view) {
@@ -4998,13 +5158,26 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             }
         });
 
-        // Update the status of swipe to refresh based on the scroll position of the nested scroll WebView.
+        // Update the status of swipe to refresh based on the scroll position of the nested scroll WebView.  Also reinforce full screen browsing mode.
         // Once the minimum API >= 23 this can be replaced with `nestedScrollWebView.setOnScrollChangeListener()`.
         nestedScrollWebView.getViewTreeObserver().addOnScrollChangedListener(() -> {
             if (nestedScrollWebView.getSwipeToRefresh()) {
                 // Only enable swipe to refresh if the WebView is scrolled to the top.
                 swipeRefreshLayout.setEnabled(nestedScrollWebView.getScrollY() == 0);
             }
+
+            // Reinforce the system UI visibility flags if in full screen browsing mode.
+            // This hides the status and navigation bars, which are displayed if other elements are shown, like dialog boxes, the options menu, or the keyboard.
+            if (inFullScreenBrowsingMode) {
+                /* 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);
+            }
         });
 
         // Set the web chrome client.
@@ -5164,6 +5337,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                 // Show the full screen video frame layout.
                 fullScreenVideoFrameLayout.setVisibility(View.VISIBLE);
+
+                // Disable the screen timeout while the video is playing.  YouTube does this automatically, but not all other videos do.
+                fullScreenVideoFrameLayout.setKeepScreenOn(true);
             }
 
             // Exit full screen video.
@@ -5172,6 +5348,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 // Get a handle for the full screen video frame layout.
                 FrameLayout fullScreenVideoFrameLayout = findViewById(R.id.full_screen_video_framelayout);
 
+                // Re-enable the screen timeout.
+                fullScreenVideoFrameLayout.setKeepScreenOn(false);
+
                 // Unset the full screen video flag.
                 displayingFullScreenVideo = false;
 
@@ -5328,6 +5507,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
             // Check requests against the block lists.  The deprecated `shouldInterceptRequest()` must be used until minimum API >= 21.
             @Override
             public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
+                // Check to see if the resource request is for the main URL.
+                if (url.equals(nestedScrollWebView.getCurrentUrl())) {
+                    // `return null` loads the resource request, which should never be blocked if it is the main URL.
+                    return null;
+                }
+
                 // Wait until the blocklists have been populated.  When Privacy Browser is being resumed after having the process killed in the background it will try to load the URLs immediately.
                 while (ultraPrivacy == null) {
                     // The wait must be synchronized, which only lets one thread run on it at a time, or `java.lang.IllegalMonitorStateException` is thrown.
@@ -5430,8 +5615,48 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                     return emptyWebResourceResponse;
                 }
 
+                // Check UltraList if it is enabled.
+                if (nestedScrollWebView.isBlocklistEnabled(NestedScrollWebView.ULTRALIST)) {
+                    // Check the URL against UltraList.
+                    String[] ultraListResults = blocklistHelper.checkBlocklist(currentDomain, url, isThirdPartyRequest, ultraList);
+
+                    // Process the UltraList results.
+                    if (ultraListResults[0].equals(BlocklistHelper.REQUEST_BLOCKED)) {  // The resource request matched UltraLists's blacklist.
+                        // Add the result to the resource requests.
+                        nestedScrollWebView.addResourceRequest(new String[] {ultraListResults[0], ultraListResults[1], ultraListResults[2], ultraListResults[3], ultraListResults[4], ultraListResults[5]});
+
+                        // Increment the blocked requests counters.
+                        nestedScrollWebView.incrementRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS);
+                        nestedScrollWebView.incrementRequestsCount(NestedScrollWebView.ULTRALIST);
+
+                        // Update the titles of the blocklist menu items if the WebView is currently displayed.
+                        if (webViewDisplayed) {
+                            // Updating the UI must be run from the UI thread.
+                            activity.runOnUiThread(() -> {
+                                // Update the menu item titles.
+                                navigationRequestsMenuItem.setTitle(getString(R.string.requests) + " - " + nestedScrollWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS));
+
+                                // Update the options menu if it has been populated.
+                                if (optionsMenu != null) {
+                                    optionsMenu.findItem(R.id.blocklists).setTitle(getString(R.string.blocklists) + " - " + nestedScrollWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS));
+                                    optionsMenu.findItem(R.id.ultralist).setTitle(nestedScrollWebView.getRequestsCount(NestedScrollWebView.ULTRALIST) + " - " + getString(R.string.ultralist));
+                                }
+                            });
+                        }
+
+                        // The resource request was blocked.  Return an empty web resource response.
+                        return emptyWebResourceResponse;
+                    } else if (ultraListResults[0].equals(BlocklistHelper.REQUEST_ALLOWED)) {  // The resource request matched UltraList's whitelist.
+                        // Add a whitelist entry to the resource requests array.
+                        nestedScrollWebView.addResourceRequest(new String[] {ultraListResults[0], ultraListResults[1], ultraListResults[2], ultraListResults[3], ultraListResults[4], ultraListResults[5]});
+
+                        // The resource request has been allowed by UltraPrivacy.  `return null` loads the requested resource.
+                        return null;
+                    }
+                }
+
                 // Check UltraPrivacy if it is enabled.
-                if (nestedScrollWebView.isBlocklistEnabled(NestedScrollWebView.ULTRA_PRIVACY)) {
+                if (nestedScrollWebView.isBlocklistEnabled(NestedScrollWebView.ULTRAPRIVACY)) {
                     // Check the URL against UltraPrivacy.
                     String[] ultraPrivacyResults = blocklistHelper.checkBlocklist(currentDomain, url, isThirdPartyRequest, ultraPrivacy);
 
@@ -5443,7 +5668,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                         // Increment the blocked requests counters.
                         nestedScrollWebView.incrementRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS);
-                        nestedScrollWebView.incrementRequestsCount(NestedScrollWebView.ULTRA_PRIVACY);
+                        nestedScrollWebView.incrementRequestsCount(NestedScrollWebView.ULTRAPRIVACY);
 
                         // Update the titles of the blocklist menu items if the WebView is currently displayed.
                         if (webViewDisplayed) {
@@ -5455,7 +5680,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                                 // Update the options menu if it has been populated.
                                 if (optionsMenu != null) {
                                     optionsMenu.findItem(R.id.blocklists).setTitle(getString(R.string.blocklists) + " - " + nestedScrollWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS));
-                                    optionsMenu.findItem(R.id.ultraprivacy).setTitle(nestedScrollWebView.getRequestsCount(NestedScrollWebView.ULTRA_PRIVACY) + " - " + getString(R.string.ultraprivacy));
+                                    optionsMenu.findItem(R.id.ultraprivacy).setTitle(nestedScrollWebView.getRequestsCount(NestedScrollWebView.ULTRAPRIVACY) + " - " + getString(R.string.ultraprivacy));
                                 }
                             });
                         }
@@ -5473,7 +5698,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 }
 
                 // Check EasyList if it is enabled.
-                if (nestedScrollWebView.isBlocklistEnabled(NestedScrollWebView.EASY_LIST)) {
+                if (nestedScrollWebView.isBlocklistEnabled(NestedScrollWebView.EASYLIST)) {
                     // Check the URL against EasyList.
                     String[] easyListResults = blocklistHelper.checkBlocklist(currentDomain, url, isThirdPartyRequest, easyList);
 
@@ -5484,7 +5709,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                         // Increment the blocked requests counters.
                         nestedScrollWebView.incrementRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS);
-                        nestedScrollWebView.incrementRequestsCount(NestedScrollWebView.EASY_LIST);
+                        nestedScrollWebView.incrementRequestsCount(NestedScrollWebView.EASYLIST);
 
                         // Update the titles of the blocklist menu items if the WebView is currently displayed.
                         if (webViewDisplayed) {
@@ -5496,7 +5721,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                                 // Update the options menu if it has been populated.
                                 if (optionsMenu != null) {
                                     optionsMenu.findItem(R.id.blocklists).setTitle(getString(R.string.blocklists) + " - " + nestedScrollWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS));
-                                    optionsMenu.findItem(R.id.easylist).setTitle(nestedScrollWebView.getRequestsCount(NestedScrollWebView.EASY_LIST) + " - " + getString(R.string.easylist));
+                                    optionsMenu.findItem(R.id.easylist).setTitle(nestedScrollWebView.getRequestsCount(NestedScrollWebView.EASYLIST) + " - " + getString(R.string.easylist));
                                 }
                             });
                         }
@@ -5510,7 +5735,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                 }
 
                 // Check EasyPrivacy if it is enabled.
-                if (nestedScrollWebView.isBlocklistEnabled(NestedScrollWebView.EASY_PRIVACY)) {
+                if (nestedScrollWebView.isBlocklistEnabled(NestedScrollWebView.EASYPRIVACY)) {
                     // Check the URL against EasyPrivacy.
                     String[] easyPrivacyResults = blocklistHelper.checkBlocklist(currentDomain, url, isThirdPartyRequest, easyPrivacy);
 
@@ -5522,7 +5747,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
 
                         // Increment the blocked requests counters.
                         nestedScrollWebView.incrementRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS);
-                        nestedScrollWebView.incrementRequestsCount(NestedScrollWebView.EASY_PRIVACY);
+                        nestedScrollWebView.incrementRequestsCount(NestedScrollWebView.EASYPRIVACY);
 
                         // Update the titles of the blocklist menu items if the WebView is currently displayed.
                         if (webViewDisplayed) {
@@ -5534,7 +5759,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                                 // Update the options menu if it has been populated.
                                 if (optionsMenu != null) {
                                     optionsMenu.findItem(R.id.blocklists).setTitle(getString(R.string.blocklists) + " - " + nestedScrollWebView.getRequestsCount(NestedScrollWebView.BLOCKED_REQUESTS));
-                                    optionsMenu.findItem(R.id.easyprivacy).setTitle(nestedScrollWebView.getRequestsCount(NestedScrollWebView.EASY_PRIVACY) + " - " + getString(R.string.easyprivacy));
+                                    optionsMenu.findItem(R.id.easyprivacy).setTitle(nestedScrollWebView.getRequestsCount(NestedScrollWebView.EASYPRIVACY) + " - " + getString(R.string.easyprivacy));
                                 }
                             });
                         }
@@ -5717,20 +5942,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                     // Get the IP addresses for the host.
                     new GetHostIpAddresses(activity, getSupportFragmentManager(), nestedScrollWebView).execute(currentUri.getHost());
 
-                    // Apply any custom domain settings if the URL was loaded by navigating history.
-                    if (nestedScrollWebView.getNavigatingHistory()) {
-                        // Reset navigating history.
-                        nestedScrollWebView.setNavigatingHistory(false);
-
-                        // Apply the domain settings.
-                        boolean userAgentChanged = applyDomainSettings(nestedScrollWebView, url, true, false);
-
-                        // Manually load the URL if the user agent has changed, which will have caused the previous URL to be reloaded.
-                        if (userAgentChanged) {
-                            loadUrl(url);
-                        }
-                    }
-
                     // Replace Refresh with Stop if the options menu has been created.  (The first WebView typically begins loading before the menu items are instantiated.)
                     if (optionsMenu != null) {
                         // Get a handle for the refresh menu item.
@@ -5858,11 +6069,17 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook
                                 tabTitleTextView.setText(R.string.new_tab);
                             }
                         } else {  // The WebView has loaded a webpage.
-                            // Display the final URL.  Getting the URL from the WebView instead of using the one provided by `onPageFinished()` makes websites like YouTube function correctly.
-                            urlEditText.setText(currentUrl);
+                            // Update the URL edit text if it is not currently being edited.
+                            if (!urlEditText.hasFocus()) {
+                                // Sanitize the current URL.  This removes unwanted URL elements that were added by redirects, so that they won't be included if the URL is shared.
+                                String sanitizedUrl = sanitizeUrl(currentUrl);
+
+                                // Display the final URL.  Getting the URL from the WebView instead of using the one provided by `onPageFinished()` makes websites like YouTube function correctly.
+                                urlEditText.setText(sanitizedUrl);
 
-                            // Apply text highlighting to the URL.
-                            highlightUrlText();
+                                // Apply text highlighting to the URL.
+                                highlightUrlText();
+                            }
 
                             // Only populate the title text view if the tab has been fully created.
                             if (tab != null) {