[WebView Support Library] guard existing APIs with feature flags.

This CL adds feature flags for a bunch of WebView Support Library APIs
(related to ServiceWorkers, SafeBrowsing, and WebSettings) and ensures
that those APIs are guarded by the corresponding feature flags.
These feature flags are used to determine which APIs the current WebView
APK implements.

Bug: 73454029
Bug: 73151166
Bug: 73151403
Test: manually, ensuring features declared in the WebView APK are
marked as supported in the support library, and features not declared in
the WebView APK are marked as unsupported.
Change-Id: I6928769648ce68c73aadcaf0563426abdebb0992
diff --git a/webkit/api/current.txt b/webkit/api/current.txt
index 5271a24..b565173 100644
--- a/webkit/api/current.txt
+++ b/webkit/api/current.txt
@@ -56,6 +56,17 @@
 
   public class WebViewFeature {
     method public static boolean isFeatureSupported(java.lang.String);
+    field public static final java.lang.String DISABLED_ACTION_MODE_MENU_ITEMS = "DISABLED_ACTION_MODE_MENU_ITEMS";
+    field public static final java.lang.String OFF_SCREEN_PRERASTER = "OFF_SCREEN_PRERASTER";
+    field public static final java.lang.String SAFE_BROWSING_ENABLE = "SAFE_BROWSING_ENABLE";
+    field public static final java.lang.String SAFE_BROWSING_PRIVACY_POLICY_URL = "SAFE_BROWSING_PRIVACY_POLICY_URL";
+    field public static final java.lang.String SAFE_BROWSING_WHITELIST = "SAFE_BROWSING_WHITELIST";
+    field public static final java.lang.String SERVICE_WORKER_BASIC_USAGE = "SERVICE_WORKER_BASIC_USAGE";
+    field public static final java.lang.String SERVICE_WORKER_BLOCK_NETWORK_LOADS = "SERVICE_WORKER_BLOCK_NETWORK_LOADS";
+    field public static final java.lang.String SERVICE_WORKER_CACHE_MODE = "SERVICE_WORKER_CACHE_MODE";
+    field public static final java.lang.String SERVICE_WORKER_CONTENT_ACCESS = "SERVICE_WORKER_CONTENT_ACCESS";
+    field public static final java.lang.String SERVICE_WORKER_FILE_ACCESS = "SERVICE_WORKER_FILE_ACCESS";
+    field public static final java.lang.String START_SAFE_BROWSING = "START_SAFE_BROWSING";
     field public static final java.lang.String VISUAL_STATE_CALLBACK = "VISUAL_STATE_CALLBACK";
   }
 
diff --git a/webkit/src/main/java/androidx/webkit/ServiceWorkerControllerCompat.java b/webkit/src/main/java/androidx/webkit/ServiceWorkerControllerCompat.java
index 3eb55d2..8d9d683 100644
--- a/webkit/src/main/java/androidx/webkit/ServiceWorkerControllerCompat.java
+++ b/webkit/src/main/java/androidx/webkit/ServiceWorkerControllerCompat.java
@@ -22,13 +22,13 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
+import androidx.annotation.RequiresFeature;
 import androidx.annotation.RestrictTo;
 import androidx.webkit.internal.FrameworkServiceWorkerController;
 import androidx.webkit.internal.ServiceWorkerControllerAdapter;
+import androidx.webkit.internal.WebViewFeatureInternal;
 import androidx.webkit.internal.WebViewGlueCommunicator;
 
-// TODO(gsennton) guard APIs with isFeatureSupported(String)
-
 /**
  * Manages Service Workers used by WebView.
  *
@@ -61,14 +61,27 @@
      * @return the default ServiceWorkerController instance
      */
     @NonNull
+    @RequiresFeature(name = WebViewFeature.SERVICE_WORKER_BASIC_USAGE,
+            enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
     public static ServiceWorkerControllerCompat getInstance() {
         return LAZY_HOLDER.INSTANCE;
     }
 
     private static class LAZY_HOLDER {
-        static final ServiceWorkerControllerCompat INSTANCE =
-                Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
-                        ? getFrameworkControllerCompat() : getSupportLibraryControllerCompat();
+        static final ServiceWorkerControllerCompat INSTANCE = createController();
+
+        @SuppressWarnings("NewApi")
+        private static ServiceWorkerControllerCompat createController() {
+            WebViewFeatureInternal webviewFeature =
+                    WebViewFeatureInternal.getFeature(WebViewFeature.SERVICE_WORKER_BASIC_USAGE);
+            if (webviewFeature.isSupportedByFramework()) {
+                return getFrameworkControllerCompat();
+            } else if (webviewFeature.isSupportedByWebView()) {
+                return getSupportLibraryControllerCompat();
+            } else {
+                throw WebViewFeatureInternal.getUnsupportedOperationException();
+            }
+        }
     }
 
     /**
diff --git a/webkit/src/main/java/androidx/webkit/ServiceWorkerWebSettingsCompat.java b/webkit/src/main/java/androidx/webkit/ServiceWorkerWebSettingsCompat.java
index 61c46c3..6763db4 100644
--- a/webkit/src/main/java/androidx/webkit/ServiceWorkerWebSettingsCompat.java
+++ b/webkit/src/main/java/androidx/webkit/ServiceWorkerWebSettingsCompat.java
@@ -19,6 +19,7 @@
 import android.webkit.WebSettings;
 
 import androidx.annotation.IntDef;
+import androidx.annotation.RequiresFeature;
 import androidx.annotation.RestrictTo;
 
 import java.lang.annotation.Retention;
@@ -58,6 +59,8 @@
      * {@link WebSettings#LOAD_DEFAULT}.
      *
      */
+    @RequiresFeature(name = WebViewFeature.SERVICE_WORKER_CACHE_MODE,
+            enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
     public abstract void setCacheMode(@CacheMode int mode);
 
     /**
@@ -68,6 +71,8 @@
      * @see #setCacheMode
      *
      */
+    @RequiresFeature(name = WebViewFeature.SERVICE_WORKER_CACHE_MODE,
+            enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
     public abstract @CacheMode int getCacheMode();
 
     /**
@@ -76,6 +81,8 @@
      * {@link WebSettings#setAllowContentAccess}.
      *
      */
+    @RequiresFeature(name = WebViewFeature.SERVICE_WORKER_CONTENT_ACCESS,
+            enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
     public abstract void setAllowContentAccess(boolean allow);
 
     /**
@@ -85,6 +92,8 @@
      * @see #setAllowContentAccess
      *
      */
+    @RequiresFeature(name = WebViewFeature.SERVICE_WORKER_CONTENT_ACCESS,
+            enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
     public abstract boolean getAllowContentAccess();
 
     /**
@@ -93,6 +102,8 @@
      * {@link WebSettings#setAllowFileAccess}.
      *
      */
+    @RequiresFeature(name = WebViewFeature.SERVICE_WORKER_FILE_ACCESS,
+            enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
     public abstract void setAllowFileAccess(boolean allow);
 
     /**
@@ -102,6 +113,8 @@
      * @see #setAllowFileAccess
      *
      */
+    @RequiresFeature(name = WebViewFeature.SERVICE_WORKER_FILE_ACCESS,
+            enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
     public abstract boolean getAllowFileAccess();
 
     /**
@@ -112,6 +125,8 @@
      * @param flag {@code true} means block network loads by the Service Workers
      *
      */
+    @RequiresFeature(name = WebViewFeature.SERVICE_WORKER_BLOCK_NETWORK_LOADS,
+            enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
     public abstract void setBlockNetworkLoads(boolean flag);
 
     /**
@@ -123,5 +138,7 @@
      * @see #setBlockNetworkLoads
      *
      */
+    @RequiresFeature(name = WebViewFeature.SERVICE_WORKER_BLOCK_NETWORK_LOADS,
+            enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
     public abstract boolean getBlockNetworkLoads();
 }
diff --git a/webkit/src/main/java/androidx/webkit/WebSettingsCompat.java b/webkit/src/main/java/androidx/webkit/WebSettingsCompat.java
index 12d7e63..bff6170 100644
--- a/webkit/src/main/java/androidx/webkit/WebSettingsCompat.java
+++ b/webkit/src/main/java/androidx/webkit/WebSettingsCompat.java
@@ -16,27 +16,27 @@
 
 package androidx.webkit;
 
-import android.os.Build;
+import android.annotation.SuppressLint;
 import android.webkit.WebSettings;
 
+import androidx.annotation.IntDef;
+import androidx.annotation.RequiresFeature;
+import androidx.annotation.RestrictTo;
+import androidx.webkit.internal.WebSettingsAdapter;
+import androidx.webkit.internal.WebViewFeatureInternal;
+import androidx.webkit.internal.WebViewGlueCommunicator;
+
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
-import androidx.annotation.IntDef;
-import androidx.annotation.RestrictTo;
-import androidx.webkit.internal.WebSettingsAdapter;
-import androidx.webkit.internal.WebViewGlueCommunicator;
-
 /**
  * Compatibility version of {@link android.webkit.WebSettings}
  */
 public class WebSettingsCompat {
     private WebSettingsCompat() {}
 
-    // TODO(gsennton): add feature detection
-
     /**
      * Sets whether this WebView should raster tiles when it is
      * offscreen but attached to a window. Turning this on can avoid
@@ -50,11 +50,18 @@
      *   visible WebViews and WebViews about to be animated to visible.
      * </ul>
      */
+    @SuppressLint("NewApi")
+    @RequiresFeature(name = WebViewFeature.OFF_SCREEN_PRERASTER,
+            enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
     public static void setOffscreenPreRaster(WebSettings webSettings, boolean enabled) {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+        WebViewFeatureInternal webviewFeature =
+                WebViewFeatureInternal.getFeature(WebViewFeature.OFF_SCREEN_PRERASTER);
+        if (webviewFeature.isSupportedByFramework()) {
             webSettings.setOffscreenPreRaster(enabled);
-        } else {
+        } else if (webviewFeature.isSupportedByWebView()) {
             getAdapter(webSettings).setOffscreenPreRaster(enabled);
+        } else {
+            throw WebViewFeatureInternal.getUnsupportedOperationException();
         }
     }
 
@@ -64,11 +71,18 @@
      * @return {@code true} if this WebView will raster tiles when it is
      * offscreen but attached to a window.
      */
+    @SuppressLint("NewApi")
+    @RequiresFeature(name = WebViewFeature.OFF_SCREEN_PRERASTER,
+            enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
     public static boolean getOffscreenPreRaster(WebSettings webSettings) {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+        WebViewFeatureInternal webviewFeature =
+                WebViewFeatureInternal.getFeature(WebViewFeature.OFF_SCREEN_PRERASTER);
+        if (webviewFeature.isSupportedByFramework()) {
             return webSettings.getOffscreenPreRaster();
-        } else {
+        } else if (webviewFeature.isSupportedByWebView()) {
             return getAdapter(webSettings).getOffscreenPreRaster();
+        } else {
+            throw WebViewFeatureInternal.getUnsupportedOperationException();
         }
     }
 
@@ -86,11 +100,18 @@
      *
      * @param enabled Whether Safe Browsing is enabled.
      */
+    @SuppressLint("NewApi")
+    @RequiresFeature(name = WebViewFeature.SAFE_BROWSING_ENABLE,
+            enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
     public static void setSafeBrowsingEnabled(WebSettings webSettings, boolean enabled) {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+        WebViewFeatureInternal webviewFeature =
+                WebViewFeatureInternal.getFeature(WebViewFeature.SAFE_BROWSING_ENABLE);
+        if (webviewFeature.isSupportedByFramework()) {
             webSettings.setSafeBrowsingEnabled(enabled);
-        } else {
+        } else if (webviewFeature.isSupportedByWebView()) {
             getAdapter(webSettings).setSafeBrowsingEnabled(enabled);
+        } else {
+            throw WebViewFeatureInternal.getUnsupportedOperationException();
         }
     }
 
@@ -100,11 +121,18 @@
      *
      * @return {@code true} if Safe Browsing is enabled and {@code false} otherwise.
      */
+    @SuppressLint("NewApi")
+    @RequiresFeature(name = WebViewFeature.SAFE_BROWSING_ENABLE,
+            enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
     public static boolean getSafeBrowsingEnabled(WebSettings webSettings) {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+        WebViewFeatureInternal webviewFeature =
+                WebViewFeatureInternal.getFeature(WebViewFeature.SAFE_BROWSING_ENABLE);
+        if (webviewFeature.isSupportedByFramework()) {
             return webSettings.getSafeBrowsingEnabled();
-        } else {
+        } else if (webviewFeature.isSupportedByWebView()) {
             return getAdapter(webSettings).getSafeBrowsingEnabled();
+        } else {
+            throw WebViewFeatureInternal.getUnsupportedOperationException();
         }
     }
 
@@ -126,12 +154,19 @@
      * Disables the action mode menu items according to {@code menuItems} flag.
      * @param menuItems an integer field flag for the menu items to be disabled.
      */
+    @SuppressLint("NewApi")
+    @RequiresFeature(name = WebViewFeature.DISABLED_ACTION_MODE_MENU_ITEMS,
+            enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
     public static void setDisabledActionModeMenuItems(WebSettings webSettings,
             @MenuItemFlags int menuItems) {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+        WebViewFeatureInternal webviewFeature =
+                WebViewFeatureInternal.getFeature(WebViewFeature.DISABLED_ACTION_MODE_MENU_ITEMS);
+        if (webviewFeature.isSupportedByFramework()) {
             webSettings.setDisabledActionModeMenuItems(menuItems);
-        } else {
+        } else if (webviewFeature.isSupportedByWebView()) {
             getAdapter(webSettings).setDisabledActionModeMenuItems(menuItems);
+        } else {
+            throw WebViewFeatureInternal.getUnsupportedOperationException();
         }
     }
 
@@ -141,11 +176,18 @@
      *
      * @return all the disabled menu item flags combined with bitwise OR.
      */
+    @SuppressLint("NewApi")
+    @RequiresFeature(name = WebViewFeature.DISABLED_ACTION_MODE_MENU_ITEMS,
+            enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
     public static @MenuItemFlags int getDisabledActionModeMenuItems(WebSettings webSettings) {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+        WebViewFeatureInternal webviewFeature =
+                WebViewFeatureInternal.getFeature(WebViewFeature.DISABLED_ACTION_MODE_MENU_ITEMS);
+        if (webviewFeature.isSupportedByFramework()) {
             return webSettings.getDisabledActionModeMenuItems();
-        } else {
+        } else if (webviewFeature.isSupportedByWebView()) {
             return getAdapter(webSettings).getDisabledActionModeMenuItems();
+        } else {
+            throw WebViewFeatureInternal.getUnsupportedOperationException();
         }
     }
 
diff --git a/webkit/src/main/java/androidx/webkit/WebViewCompat.java b/webkit/src/main/java/androidx/webkit/WebViewCompat.java
index 7cb0bfd..273a79d 100644
--- a/webkit/src/main/java/androidx/webkit/WebViewCompat.java
+++ b/webkit/src/main/java/androidx/webkit/WebViewCompat.java
@@ -16,6 +16,7 @@
 
 package androidx.webkit;
 
+import android.annotation.SuppressLint;
 import android.content.Context;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
@@ -135,7 +136,7 @@
             checkThread(webview);
             getProvider(webview).insertVisualStateCallback(requestId, callback);
         } else {
-            WebViewFeatureInternal.throwUnsupportedOperationException("postVisualStateCallback");
+            throw WebViewFeatureInternal.getUnsupportedOperationException();
         }
     }
 
@@ -157,12 +158,19 @@
      * @param callback will be called on the UI thread with {@code true} if initialization is
      * successful, {@code false} otherwise.
      */
+    @SuppressLint("NewApi")
+    @RequiresFeature(name = WebViewFeature.START_SAFE_BROWSING,
+            enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
     public static void startSafeBrowsing(@NonNull Context context,
             @Nullable ValueCallback<Boolean> callback) {
-        if (Build.VERSION.SDK_INT >= 27) {
+        WebViewFeatureInternal webviewFeature =
+                WebViewFeatureInternal.getFeature(WebViewFeature.START_SAFE_BROWSING);
+        if (webviewFeature.isSupportedByFramework()) {
             WebView.startSafeBrowsing(context, callback);
-        } else { // TODO(gsennton): guard with WebViewApk.hasFeature(SafeBrowsing)
+        } else if (webviewFeature.isSupportedByWebView()) {
             getFactory().getStatics().initSafeBrowsing(context, callback);
+        } else {
+            throw WebViewFeatureInternal.getUnsupportedOperationException();
         }
     }
 
@@ -189,12 +197,19 @@
      * whitelist. It will be called with {@code false} if any hosts are malformed. The callback
      * will be run on the UI thread
      */
+    @SuppressLint("NewApi")
+    @RequiresFeature(name = WebViewFeature.SAFE_BROWSING_WHITELIST,
+            enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
     public static void setSafeBrowsingWhitelist(@NonNull List<String> hosts,
             @Nullable ValueCallback<Boolean> callback) {
-        if (Build.VERSION.SDK_INT >= 27) {
+        WebViewFeatureInternal webviewFeature =
+                WebViewFeatureInternal.getFeature(WebViewFeature.SAFE_BROWSING_WHITELIST);
+        if (webviewFeature.isSupportedByFramework()) {
             WebView.setSafeBrowsingWhitelist(hosts, callback);
-        } else { // TODO(gsennton): guard with WebViewApk.hasFeature(SafeBrowsing)
+        } else if (webviewFeature.isSupportedByWebView()) {
             getFactory().getStatics().setSafeBrowsingWhitelist(hosts, callback);
+        } else {
+            throw WebViewFeatureInternal.getUnsupportedOperationException();
         }
     }
 
@@ -203,12 +218,19 @@
      *
      * @return the url pointing to a privacy policy document which can be displayed to users.
      */
+    @SuppressLint("NewApi")
     @NonNull
+    @RequiresFeature(name = WebViewFeature.SAFE_BROWSING_PRIVACY_POLICY_URL,
+            enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
     public static Uri getSafeBrowsingPrivacyPolicyUrl() {
-        if (Build.VERSION.SDK_INT >= 27) {
+        WebViewFeatureInternal webviewFeature =
+                WebViewFeatureInternal.getFeature(WebViewFeature.SAFE_BROWSING_PRIVACY_POLICY_URL);
+        if (webviewFeature.isSupportedByFramework()) {
             return WebView.getSafeBrowsingPrivacyPolicyUrl();
-        } else { // TODO(gsennton): guard with WebViewApk.hasFeature(SafeBrowsing)
+        } else if (webviewFeature.isSupportedByWebView()) {
             return getFactory().getStatics().getSafeBrowsingPrivacyPolicyUrl();
+        } else {
+            throw WebViewFeatureInternal.getUnsupportedOperationException();
         }
     }
 
diff --git a/webkit/src/main/java/androidx/webkit/WebViewFeature.java b/webkit/src/main/java/androidx/webkit/WebViewFeature.java
index d514477..eb54eed 100644
--- a/webkit/src/main/java/androidx/webkit/WebViewFeature.java
+++ b/webkit/src/main/java/androidx/webkit/WebViewFeature.java
@@ -16,6 +16,10 @@
 
 package androidx.webkit;
 
+import android.content.Context;
+import android.webkit.ValueCallback;
+import android.webkit.WebSettings;
+
 import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.StringDef;
@@ -27,6 +31,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
+import java.util.List;
 
 /**
  * Utility class for checking which WebView Support Library features are supported on the device.
@@ -41,6 +46,18 @@
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     @StringDef(value = {
             VISUAL_STATE_CALLBACK,
+            OFF_SCREEN_PRERASTER,
+            SAFE_BROWSING_ENABLE,
+            DISABLED_ACTION_MODE_MENU_ITEMS,
+            START_SAFE_BROWSING,
+            SAFE_BROWSING_WHITELIST,
+            SAFE_BROWSING_PRIVACY_POLICY_URL,
+            SERVICE_WORKER_BASIC_USAGE,
+            SERVICE_WORKER_CACHE_MODE,
+            SERVICE_WORKER_CONTENT_ACCESS,
+            SERVICE_WORKER_FILE_ACCESS,
+            SERVICE_WORKER_BLOCK_NETWORK_LOADS
+
     })
     @Retention(RetentionPolicy.SOURCE)
     @Target({ElementType.PARAMETER, ElementType.METHOD})
@@ -55,6 +72,95 @@
     public static final String VISUAL_STATE_CALLBACK = Features.VISUAL_STATE_CALLBACK;
 
     /**
+     * Feature for {@link #isFeatureSupported(String)}.
+     * This feature covers
+     * {@link androidx.webkit.WebSettingsCompat#getOffscreenPreRaster(WebSettings)}, and
+     * {@link androidx.webkit.WebSettingsCompat#setOffscreenPreRaster(WebSettings, boolean)}.
+     */
+    public static final String OFF_SCREEN_PRERASTER = Features.OFF_SCREEN_PRERASTER;
+
+    /**
+     * Feature for {@link #isFeatureSupported(String)}.
+     * This feature covers
+     * {@link androidx.webkit.WebSettingsCompat#getSafeBrowsingEnabled(WebSettings)}, and
+     * {@link androidx.webkit.WebSettingsCompat#setSafeBrowsingEnabled(WebSettings, boolean)}.
+     */
+    public static final String SAFE_BROWSING_ENABLE = Features.SAFE_BROWSING_ENABLE;
+
+    /**
+     * Feature for {@link #isFeatureSupported(String)}.
+     * This feature covers
+     * {@link androidx.webkit.WebSettingsCompat#getDisabledActionModeMenuItems(WebSettings)}, and
+     * {@link androidx.webkit.WebSettingsCompat#setDisabledActionModeMenuItems(WebSettings, int)}.
+     */
+    public static final String DISABLED_ACTION_MODE_MENU_ITEMS =
+            Features.DISABLED_ACTION_MODE_MENU_ITEMS;
+
+    /**
+     * Feature for {@link #isFeatureSupported(String)}.
+     * This feature covers
+     * {@link androidx.webkit.WebViewCompat#startSafeBrowsing(Context, ValueCallback)}.
+     */
+    public static final String START_SAFE_BROWSING = Features.START_SAFE_BROWSING;
+
+    /**
+     * Feature for {@link #isFeatureSupported(String)}.
+     * This feature covers
+     * {@link androidx.webkit.WebViewCompat#setSafeBrowsingWhitelist(List, ValueCallback)}.
+     */
+    public static final String SAFE_BROWSING_WHITELIST = Features.SAFE_BROWSING_WHITELIST;
+
+    /**
+     * Feature for {@link #isFeatureSupported(String)}.
+     * This feature covers
+     * {@link WebViewCompat#getSafeBrowsingPrivacyPolicyUrl()}.
+     */
+    public static final String SAFE_BROWSING_PRIVACY_POLICY_URL =
+            Features.SAFE_BROWSING_PRIVACY_POLICY_URL;
+
+    /**
+     * Feature for {@link #isFeatureSupported(String)}.
+     * This feature covers
+     * {@link ServiceWorkerControllerCompat#getInstance()}.
+     */
+    public static final String SERVICE_WORKER_BASIC_USAGE = Features.SERVICE_WORKER_BASIC_USAGE;
+
+    /**
+     * Feature for {@link #isFeatureSupported(String)}.
+     * This feature covers
+     * {@link ServiceWorkerWebSettingsCompat#getCacheMode()}, and
+     * {@link ServiceWorkerWebSettingsCompat#setCacheMode(int)}.
+     */
+    public static final String SERVICE_WORKER_CACHE_MODE = Features.SERVICE_WORKER_CACHE_MODE;
+
+    /**
+     * Feature for {@link #isFeatureSupported(String)}.
+     * This feature covers
+     * {@link ServiceWorkerWebSettingsCompat#getAllowContentAccess()}, and
+     * {@link ServiceWorkerWebSettingsCompat#setAllowContentAccess(boolean)}.
+     */
+    public static final String SERVICE_WORKER_CONTENT_ACCESS =
+            Features.SERVICE_WORKER_CONTENT_ACCESS;
+
+    /**
+     * Feature for {@link #isFeatureSupported(String)}.
+     * This feature covers
+     * {@link ServiceWorkerWebSettingsCompat#getAllowFileAccess()}, and
+     * {@link ServiceWorkerWebSettingsCompat#setAllowFileAccess(boolean)}.
+     */
+    public static final String SERVICE_WORKER_FILE_ACCESS = Features.SERVICE_WORKER_FILE_ACCESS;
+
+    /**
+     * Feature for {@link #isFeatureSupported(String)}.
+     * This feature covers
+     * {@link ServiceWorkerWebSettingsCompat#getBlockNetworkLoads()}, and
+     * {@link ServiceWorkerWebSettingsCompat#setBlockNetworkLoads(boolean)}.
+     */
+    public static final String SERVICE_WORKER_BLOCK_NETWORK_LOADS =
+            Features.SERVICE_WORKER_BLOCK_NETWORK_LOADS;
+
+
+    /**
      * Return whether a feature is supported at run-time. This depends on the Android version of the
      * device and the WebView APK on the device.
      */
diff --git a/webkit/src/main/java/androidx/webkit/internal/WebViewFeatureInternal.java b/webkit/src/main/java/androidx/webkit/internal/WebViewFeatureInternal.java
index 74f67e4..52189e9 100644
--- a/webkit/src/main/java/androidx/webkit/internal/WebViewFeatureInternal.java
+++ b/webkit/src/main/java/androidx/webkit/internal/WebViewFeatureInternal.java
@@ -16,11 +16,17 @@
 
 package androidx.webkit.internal;
 
+import android.content.Context;
 import android.os.Build;
+import android.webkit.ValueCallback;
+import android.webkit.WebSettings;
 
+import androidx.webkit.WebViewCompat;
 import androidx.webkit.WebViewFeature;
 import androidx.webkit.WebViewFeature.WebViewSupportFeature;
 
+import java.util.List;
+
 /**
  * Enum representing a WebView feature, this provides functionality for determining whether a
  * feature is supported by the current framework and/or WebView APK.
@@ -31,7 +37,84 @@
      * {@link androidx.webkit.WebViewCompat#postVisualStateCallback(android.webkit.WebView, long,
      * androidx.webkit.WebViewCompat.VisualStateCallback)}.
      */
-    VISUAL_STATE_CALLBACK_FEATURE(WebViewFeature.VISUAL_STATE_CALLBACK, Build.VERSION_CODES.M);
+    VISUAL_STATE_CALLBACK_FEATURE(WebViewFeature.VISUAL_STATE_CALLBACK, Build.VERSION_CODES.M),
+
+    /**
+     * This feature covers
+     * {@link androidx.webkit.WebSettingsCompat#getOffscreenPreRaster(WebSettings)}, and
+     * {@link androidx.webkit.WebSettingsCompat#setOffscreenPreRaster(WebSettings, boolean)}.
+     */
+    OFF_SCREEN_PRERASTER(WebViewFeature.OFF_SCREEN_PRERASTER, Build.VERSION_CODES.M),
+
+    /**
+     * This feature covers
+     * {@link androidx.webkit.WebSettingsCompat#getSafeBrowsingEnabled(WebSettings)}, and
+     * {@link androidx.webkit.WebSettingsCompat#setSafeBrowsingEnabled(WebSettings, boolean)}.
+     */
+    SAFE_BROWSING_ENABLE(WebViewFeature.SAFE_BROWSING_ENABLE, Build.VERSION_CODES.O),
+
+    /**
+     * This feature covers
+     * {@link androidx.webkit.WebSettingsCompat#getDisabledActionModeMenuItems(WebSettings)}, and
+     * {@link androidx.webkit.WebSettingsCompat#setDisabledActionModeMenuItems(WebSettings, int)}.
+     */
+    DISABLED_ACTION_MODE_MENU_ITEMS(WebViewFeature.DISABLED_ACTION_MODE_MENU_ITEMS,
+                                    Build.VERSION_CODES.N),
+
+    /**
+     * This feature covers
+     * {@link androidx.webkit.WebViewCompat#startSafeBrowsing(Context, ValueCallback)}.
+     */
+    START_SAFE_BROWSING(WebViewFeature.START_SAFE_BROWSING, Build.VERSION_CODES.O_MR1),
+
+    /**
+     * This feature covers
+     * {@link androidx.webkit.WebViewCompat#setSafeBrowsingWhitelist(List, ValueCallback)}.
+     */
+    SAFE_BROWSING_WHITELIST(WebViewFeature.SAFE_BROWSING_WHITELIST, Build.VERSION_CODES.O_MR1),
+
+    /**
+     * This feature covers
+     * {@link WebViewCompat#getSafeBrowsingPrivacyPolicyUrl()}.
+     */
+    SAFE_BROWSING_PRIVACY_POLICY_URL(WebViewFeature.SAFE_BROWSING_PRIVACY_POLICY_URL,
+            Build.VERSION_CODES.O_MR1),
+
+    /**
+     * This feature covers
+     * {@link androidx.webkit.ServiceWorkerControllerCompat#getInstance()}.
+     */
+    SERVICE_WORKER_BASIC_USAGE(WebViewFeature.SERVICE_WORKER_BASIC_USAGE, Build.VERSION_CODES.N),
+
+    /**
+     * This feature covers
+     * {@link androidx.webkit.ServiceWorkerWebSettingsCompat#getCacheMode()}, and
+     * {@link androidx.webkit.ServiceWorkerWebSettingsCompat#setCacheMode(int)}.
+     */
+    SERVICE_WORKER_CACHE_MODE(WebViewFeature.SERVICE_WORKER_CACHE_MODE, Build.VERSION_CODES.N),
+
+    /**
+     * This feature covers
+     * {@link androidx.webkit.ServiceWorkerWebSettingsCompat#getAllowContentAccess()}, and
+     * {@link androidx.webkit.ServiceWorkerWebSettingsCompat#setAllowContentAccess(boolean)}.
+     */
+    SERVICE_WORKER_CONTENT_ACCESS(WebViewFeature.SERVICE_WORKER_CONTENT_ACCESS,
+            Build.VERSION_CODES.N),
+
+    /**
+     * This feature covers
+     * {@link androidx.webkit.ServiceWorkerWebSettingsCompat#getAllowFileAccess()}, and
+     * {@link androidx.webkit.ServiceWorkerWebSettingsCompat#setAllowFileAccess(boolean)}.
+     */
+    SERVICE_WORKER_FILE_ACCESS(WebViewFeature.SERVICE_WORKER_FILE_ACCESS, Build.VERSION_CODES.N),
+
+    /**
+     * This feature covers
+     * {@link androidx.webkit.ServiceWorkerWebSettingsCompat#getBlockNetworkLoads()}, and
+     * {@link androidx.webkit.ServiceWorkerWebSettingsCompat#setBlockNetworkLoads(boolean)}.
+     */
+    SERVICE_WORKER_BLOCK_NETWORK_LOADS(WebViewFeature.SERVICE_WORKER_BLOCK_NETWORK_LOADS,
+            Build.VERSION_CODES.N);
 
     private final String mFeatureValue;
     private final int mOsVersion;
@@ -45,12 +128,10 @@
      * Return the {@link WebViewFeatureInternal} corresponding to {@param feature}.
      */
     public static WebViewFeatureInternal getFeature(@WebViewSupportFeature String feature) {
-        switch (feature) {
-            case WebViewFeature.VISUAL_STATE_CALLBACK:
-                return VISUAL_STATE_CALLBACK_FEATURE;
-            default:
-                throw new RuntimeException("Unknown feature " + feature);
+        for (WebViewFeatureInternal internalFeature : WebViewFeatureInternal.values()) {
+            if (internalFeature.mFeatureValue.equals(feature)) return internalFeature;
         }
+        throw new RuntimeException("Unknown feature " + feature);
     }
 
     /**
@@ -86,8 +167,8 @@
      * Utility method for throwing an exception explaining that the feature the app trying to use
      * isn't supported.
      */
-    public static void throwUnsupportedOperationException(String feature) {
-        throw new UnsupportedOperationException("Feature " + feature
-                + " is not supported by the current version of the framework and WebView APK");
+    public static UnsupportedOperationException getUnsupportedOperationException() {
+        return new UnsupportedOperationException("This method is not supported by the current "
+                + "version of the framework and the current WebView APK");
     }
 }