Title: [295679] trunk
Revision
295679
Author
[email protected]
Date
2022-06-20 20:54:18 -0700 (Mon, 20 Jun 2022)

Log Message

[GLIB] Add API to set WebView's Content-Security-Policy
https://bugs.webkit.org/show_bug.cgi?id=240221

Reviewed by Michael Catanzaro and Adrian Perez de Castro.

This adds API to set the WebView's default policy as well as API to
indicate that the WebView is in a WebExtension. This changes the internal
behavior of CSP to block some options.

Both of these are required for a complete WebExtension implementation in
browsers such as Epiphany.

* Tools/TestWebKitAPI/Tests/WebKitGLib/TestWebKitWebView.cpp:
(testWebViewDefaultContentSecurityPolicy):
(testWebViewWebExtensionMode):
(beforeAll):
* Tools/TestWebKitAPI/glib/WebKitGLib/WebViewTest.cpp:
(WebViewTest::loadHtml):
* Tools/TestWebKitAPI/glib/WebKitGLib/WebViewTest.h:
* Source/WebKit/UIProcess/API/glib/WebKitWebContext.cpp:
(webkitWebContextCreatePageForWebView):
* Source/WebKit/UIProcess/API/glib/WebKitWebView.cpp:
(webkitWebViewSetProperty):
(webkitWebViewGetProperty):
(webkit_web_view_class_init):
(webkit_web_view_get_web_extension_mode):
(webkit_web_view_get_default_content_security_policy):
* Source/WebKit/UIProcess/API/gtk/WebKitWebView.h:
* Source/WebKit/UIProcess/API/wpe/WebKitWebView.h:

Canonical link: https://commits.webkit.org/251684@main

Modified Paths

Diff

Modified: trunk/Source/WebKit/UIProcess/API/glib/WebKitWebContext.cpp (295678 => 295679)


--- trunk/Source/WebKit/UIProcess/API/glib/WebKitWebContext.cpp	2022-06-21 00:29:18 UTC (rev 295678)
+++ trunk/Source/WebKit/UIProcess/API/glib/WebKitWebContext.cpp	2022-06-21 03:54:18 UTC (rev 295679)
@@ -60,6 +60,7 @@
 #include "WebsiteDataStore.h"
 #include "WebsiteDataType.h"
 #include <_javascript_Core/RemoteInspector.h>
+#include <WebCore/ContentSecurityPolicy.h>
 #include <WebCore/ResourceLoaderIdentifier.h>
 #include <glib/gi18n-lib.h>
 #include <libintl.h>
@@ -1943,6 +1944,17 @@
     pageConfiguration->setUserContentController(userContentManager ? webkitUserContentManagerGetUserContentControllerProxy(userContentManager) : nullptr);
     pageConfiguration->setControlledByAutomation(webkit_web_view_is_controlled_by_automation(webView));
 
+    WebKitWebExtensionMode webExtensionMode = webkit_web_view_get_web_extension_mode(webView);
+    const char* defaultContentSecurityPolicy = webkit_web_view_get_default_content_security_policy(webView);
+
+    if (webExtensionMode == WEBKIT_WEB_EXTENSION_MODE_MANIFESTV3)
+        pageConfiguration->setContentSecurityPolicyModeForExtension(WebCore::ContentSecurityPolicyModeForExtension::ManifestV3);
+    else if (webExtensionMode == WEBKIT_WEB_EXTENSION_MODE_MANIFESTV2)
+        pageConfiguration->setContentSecurityPolicyModeForExtension(WebCore::ContentSecurityPolicyModeForExtension::ManifestV2);
+
+    if (defaultContentSecurityPolicy)
+        pageConfiguration->setOverrideContentSecurityPolicy(String::fromUTF8(defaultContentSecurityPolicy));
+
     WebKitWebsiteDataManager* manager = webkitWebViewGetWebsiteDataManager(webView);
     if (!manager)
         manager = context->priv->websiteDataManager.get();

Modified: trunk/Source/WebKit/UIProcess/API/glib/WebKitWebView.cpp (295678 => 295679)


--- trunk/Source/WebKit/UIProcess/API/glib/WebKitWebView.cpp	2022-06-21 00:29:18 UTC (rev 295678)
+++ trunk/Source/WebKit/UIProcess/API/glib/WebKitWebView.cpp	2022-06-21 03:54:18 UTC (rev 295679)
@@ -211,6 +211,9 @@
     PROP_MICROPHONE_CAPTURE_STATE,
     PROP_DISPLAY_CAPTURE_STATE,
 
+    PROP_WEB_EXTENSION_MODE,
+    PROP_DEFAULT_CONTENT_SECURITY_POLICY,
+
     N_PROPERTIES,
 };
 
@@ -317,6 +320,9 @@
     GRefPtr<WebKitWebsiteDataManager> websiteDataManager;
     GRefPtr<WebKitWebsitePolicies> websitePolicies;
 
+    CString defaultContentSecurityPolicy;
+    WebKitWebExtensionMode webExtensionMode;
+
     double textScaleFactor;
 
     bool isWebProcessResponsive;
@@ -901,6 +907,12 @@
     case PROP_DISPLAY_CAPTURE_STATE:
         webkit_web_view_set_display_capture_state(webView, static_cast<WebKitMediaCaptureState>(g_value_get_enum(value)));
         break;
+    case PROP_WEB_EXTENSION_MODE:
+        webView->priv->webExtensionMode = static_cast<WebKitWebExtensionMode>(g_value_get_enum(value));
+        break;
+    case PROP_DEFAULT_CONTENT_SECURITY_POLICY:
+        webView->priv->defaultContentSecurityPolicy = CString(g_value_get_string(value));
+        break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, paramSpec);
     }
@@ -981,6 +993,12 @@
     case PROP_DISPLAY_CAPTURE_STATE:
         g_value_set_enum(value, webkit_web_view_get_display_capture_state(webView));
         break;
+    case PROP_WEB_EXTENSION_MODE:
+        g_value_set_enum(value, webkit_web_view_get_web_extension_mode(webView));
+        break;
+    case PROP_DEFAULT_CONTENT_SECURITY_POLICY:
+        g_value_set_string(value, webkit_web_view_get_default_content_security_policy(webView));
+        break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, paramSpec);
     }
@@ -1450,6 +1468,50 @@
         WEBKIT_MEDIA_CAPTURE_STATE_NONE,
         WEBKIT_PARAM_READWRITE);
 
+    /**
+     * WebKitWebView:web-extension-mode:
+     *
+     * This configures @web_view to treat the content as a WebExtension.
+     *
+     * Note that this refers to the web standard [WebExtensions](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions)
+     * and not WebKitWebExtensions.
+     * 
+     * In practice this limits the Content-Security-Policies that are allowed to be set. Some details can be found in
+     * [Chrome's documentation](https://developer.chrome.com/docs/extensions/mv3/intro/mv3-migration/#content-security-policy).
+     *
+     * Since: 2.38
+     */
+    sObjProperties[PROP_WEB_EXTENSION_MODE] = g_param_spec_enum(
+        "web-extension-mode",
+        "WebExtension Mode",
+        _("Enables WebExtension mode"),
+        WEBKIT_TYPE_WEB_EXTENSION_MODE,
+        WEBKIT_WEB_EXTENSION_MODE_NONE,
+        static_cast<GParamFlags>(WEBKIT_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+    /**
+     * WebKitWebView:default-content-security-policy:
+     *
+     * The default Content-Security-Policy used by the webview as if it were set
+     * by an HTTP header.
+     * 
+     * This applies to all content loaded including through navigation or via the various
+     * webkit_web_view_load_\* APIs. However do note that many WebKit APIs bypass
+     * Content-Security-Policy in general such as #WebKitUserContentManager and
+     * webkit_web_view_run_javascript().
+     *
+     * Policies are additive so if a website sets its own policy it still applies
+     * on top of the policy set here.
+     * 
+     * Since: 2.38
+     */
+    sObjProperties[PROP_DEFAULT_CONTENT_SECURITY_POLICY] = g_param_spec_string(
+        "default-content-security-policy",
+        "Default Content-Security-Policy",
+        _("The default Content-Security-Policy"),
+        nullptr,
+        static_cast<GParamFlags>(WEBKIT_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
     g_object_class_install_properties(gObjectClass, N_PROPERTIES, sObjProperties);
 
     /**
@@ -5178,3 +5240,41 @@
 {
     WebKit::WebsiteDataStore::setCachedProcessSuspensionDelayForTesting(Seconds(seconds));
 }
+
+/**
+ * webkit_web_view_get_web_extension_mode:
+ * @web_view: a #WebKitWebView
+ *
+ * Get the view's #WebKitWebExtensionMode.
+ *
+ * Returns: the #WebKitWebExtensionMode
+ *
+ * Since: 2.38
+ */
+WebKitWebExtensionMode webkit_web_view_get_web_extension_mode(WebKitWebView* webView)
+{
+    g_return_val_if_fail(WEBKIT_IS_WEB_VIEW(webView), WEBKIT_WEB_EXTENSION_MODE_NONE);
+
+    return webView->priv->webExtensionMode;
+}
+
+/**
+ * webkit_web_view_get_default_content_security_policy:
+ * @web_view: a #WebKitWebView
+ *
+ * Gets the configured default Content-Security-Policy.
+ *
+ * Returns: (nullable): The default policy or %NULL
+ *
+ * Since: 2.38
+ */
+const gchar*
+webkit_web_view_get_default_content_security_policy(WebKitWebView* webView)
+{
+    g_return_val_if_fail(WEBKIT_IS_WEB_VIEW(webView), nullptr);
+
+    if (webView->priv->defaultContentSecurityPolicy.isNull())
+        return nullptr;
+
+    return webView->priv->defaultContentSecurityPolicy.data();
+}

Modified: trunk/Source/WebKit/UIProcess/API/gtk/WebKitWebView.h (295678 => 295679)


--- trunk/Source/WebKit/UIProcess/API/gtk/WebKitWebView.h	2022-06-21 00:29:18 UTC (rev 295678)
+++ trunk/Source/WebKit/UIProcess/API/gtk/WebKitWebView.h	2022-06-21 03:54:18 UTC (rev 295679)
@@ -221,6 +221,23 @@
     WEBKIT_MEDIA_CAPTURE_STATE_MUTED,
 } WebKitMediaCaptureState;
 
+/**
+ * WebKitWebExtensionMode:
+ * @WEBKIT_WEB_EXTENSION_MODE_NONE: Not for an extension.
+ * @WEBKIT_WEB_EXTENSION_MODE_MANIFESTV2: For a ManifestV2 extension.
+ * @WEBKIT_WEB_EXTENSION_MODE_MANIFESTV3: For a ManifestV3 extension.
+ *
+ * Enum values used for setting if a #WebKitWebView is intended for
+ * WebExtensions.
+ *
+ * Since: 2.38
+ */
+typedef enum {
+    WEBKIT_WEB_EXTENSION_MODE_NONE,
+    WEBKIT_WEB_EXTENSION_MODE_MANIFESTV2,
+    WEBKIT_WEB_EXTENSION_MODE_MANIFESTV3,
+} WebKitWebExtensionMode;
+
 struct _WebKitWebView {
     WebKitWebViewBase parent;
 
@@ -648,6 +665,12 @@
 webkit_web_view_set_display_capture_state            (WebKitWebView             *web_view,
                                                       WebKitMediaCaptureState    state);
 
+WEBKIT_API WebKitWebExtensionMode
+webkit_web_view_get_web_extension_mode               (WebKitWebView             *web_view);
+
+WEBKIT_API const gchar*
+webkit_web_view_get_default_content_security_policy  (WebKitWebView             *web_view);
+
 G_END_DECLS
 
 #endif

Modified: trunk/Source/WebKit/UIProcess/API/wpe/WebKitWebView.h (295678 => 295679)


--- trunk/Source/WebKit/UIProcess/API/wpe/WebKitWebView.h	2022-06-21 00:29:18 UTC (rev 295678)
+++ trunk/Source/WebKit/UIProcess/API/wpe/WebKitWebView.h	2022-06-21 03:54:18 UTC (rev 295679)
@@ -202,6 +202,23 @@
     WEBKIT_MEDIA_CAPTURE_STATE_MUTED,
 } WebKitMediaCaptureState;
 
+/**
+ * WebKitWebExtensionMode:
+ * @WEBKIT_WEB_EXTENSION_MODE_NONE: Not for an extension.
+ * @WEBKIT_WEB_EXTENSION_MODE_MANIFESTV2: For a ManifestV2 extension.
+ * @WEBKIT_WEB_EXTENSION_MODE_MANIFESTV3: For a ManifestV3 extension.
+ *
+ * Enum values used for setting if a #WebKitWebView is intended for
+ * WebExtensions.
+ *
+ * Since: 2.38
+ */
+typedef enum {
+    WEBKIT_WEB_EXTENSION_MODE_NONE,
+    WEBKIT_WEB_EXTENSION_MODE_MANIFESTV2,
+    WEBKIT_WEB_EXTENSION_MODE_MANIFESTV3,
+} WebKitWebExtensionMode;
+
 struct _WebKitWebView {
     GObject parent;
 
@@ -625,6 +642,12 @@
 webkit_web_view_set_display_capture_state            (WebKitWebView             *web_view,
                                                       WebKitMediaCaptureState    state);
 
+WEBKIT_API WebKitWebExtensionMode
+webkit_web_view_get_web_extension_mode               (WebKitWebView             *web_view);
+
+WEBKIT_API const gchar*
+webkit_web_view_get_default_content_security_policy  (WebKitWebView             *web_view);
+
 G_END_DECLS
 
 #endif

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitGLib/TestWebKitWebView.cpp (295678 => 295679)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitGLib/TestWebKitWebView.cpp	2022-06-21 00:29:18 UTC (rev 295678)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitGLib/TestWebKitWebView.cpp	2022-06-21 03:54:18 UTC (rev 295679)
@@ -1857,6 +1857,71 @@
     g_assert_cmpint(waitForFooChanged(), ==, 200);
 }
 
+static void testWebViewDefaultContentSecurityPolicy(WebViewTest* test, gconstpointer)
+{
+    GUniqueOutPtr<GError> error;
+    WebKitJavascriptResult* _javascript_Result;
+
+    // Sanity check that eval works normally.
+    _javascript_Result = test->runJavaScriptAndWaitUntilFinished("eval('\"allowed\"')", &error.outPtr());
+    g_assert_nonnull(_javascript_Result);
+    g_assert_no_error(error.get());
+    GUniquePtr<char> evalValue(WebViewTest::_javascript_ResultToCString(_javascript_Result));
+    g_assert_cmpstr(evalValue.get(), ==, "allowed");
+    webkit_javascript_result_unref(_javascript_Result);
+
+    // Create a new web view with a policy that blocks eval().
+    auto webView = Test::adoptView(g_object_new(WEBKIT_TYPE_WEB_VIEW,
+        "default-content-security-policy", "script-src 'self'", nullptr));
+
+    // Ensure _javascript_ still functions.
+    _javascript_Result = test->runJavaScriptAndWaitUntilFinished("'allowed'", &error.outPtr(), webView.get());
+    g_assert_nonnull(_javascript_Result);
+    g_assert_no_error(error.get());
+    GUniquePtr<char> value(WebViewTest::_javascript_ResultToCString(_javascript_Result));
+    g_assert_cmpstr(value.get(), ==, "allowed");
+    webkit_javascript_result_unref(_javascript_Result);
+
+    // Then ensure eval is blocked.
+    _javascript_Result = test->runJavaScriptAndWaitUntilFinished("eval('\"allowed\"')", &error.outPtr(), webView.get());
+    g_assert_null(_javascript_Result);
+    g_assert_error(error.get(), WEBKIT_JAVASCRIPT_ERROR, WEBKIT_JAVASCRIPT_ERROR_SCRIPT_FAILED);
+}
+
+static void testWebViewWebExtensionMode(WebViewTest* test, gconstpointer)
+{
+    GUniqueOutPtr<GError> error;
+    WebKitJavascriptResult* _javascript_Result;
+    static const char* html =
+        "<html>"
+        "  <head>"
+        "    <title>unset</title>"
+        "    <meta http-equiv=\"Content-Security-Policy\" content=\"script-src 'unsafe-inline';\">"
+        "    <script>document.title = 'set';</script>"
+        "  </head>"
+        "</html>";
+
+    // Sanity check that this HTML works as expected.
+    test->loadHtml(html, nullptr);
+    test->waitUntilLoadFinished();
+    _javascript_Result = test->runJavaScriptAndWaitUntilFinished("document.title == 'set';", &error.outPtr());
+    g_assert_nonnull(_javascript_Result);
+    g_assert_no_error(error.get());
+    g_assert_true(WebViewTest::_javascript_ResultToBoolean(_javascript_Result));
+    webkit_javascript_result_unref(_javascript_Result);
+
+    // Create a new web view with an extension mode that blocks the unsafe-inline keyword.
+    auto webView = Test::adoptView(g_object_new(WEBKIT_TYPE_WEB_VIEW,
+        "web-extension-mode", WEBKIT_WEB_EXTENSION_MODE_MANIFESTV3,
+        nullptr));
+    test->loadHtml(html, nullptr, webView.get());
+    test->waitUntilLoadFinished(webView.get());
+    _javascript_Result = test->runJavaScriptAndWaitUntilFinished("document.title == 'unset';", &error.outPtr(), webView.get());
+    g_assert_nonnull(_javascript_Result);
+    g_assert_no_error(error.get());
+    g_assert_true(WebViewTest::_javascript_ResultToBoolean(_javascript_Result));
+}
+
 #if USE(SOUP2)
 static void serverCallback(SoupServer* server, SoupMessage* message, const char* path, GHashTable*, SoupClientContext*, gpointer)
 #else
@@ -1930,6 +1995,8 @@
     WebViewTerminateWebProcessTest::add("WebKitWebView", "terminate-web-process", testWebViewTerminateWebProcess);
     WebViewTerminateWebProcessTest::add("WebKitWebView", "terminate-unresponsive-web-process", testWebViewTerminateUnresponsiveWebProcess);
     WebViewTest::add("WebKitWebView", "cors-allowlist", testWebViewCORSAllowlist);
+    WebViewTest::add("WebKitWebView", "default-content-security-policy", testWebViewDefaultContentSecurityPolicy);
+    WebViewTest::add("WebKitWebView", "web-extension-mode", testWebViewWebExtensionMode);
 }
 
 void afterAll()

Modified: trunk/Tools/TestWebKitAPI/glib/WebKitGLib/WebViewTest.cpp (295678 => 295679)


--- trunk/Tools/TestWebKitAPI/glib/WebKitGLib/WebViewTest.cpp	2022-06-21 00:29:18 UTC (rev 295678)
+++ trunk/Tools/TestWebKitAPI/glib/WebKitGLib/WebViewTest.cpp	2022-06-21 03:54:18 UTC (rev 295679)
@@ -85,15 +85,19 @@
     g_assert_cmpstr(webkit_web_view_get_uri(m_webView), ==, m_activeURI.data());
 }
 
-void WebViewTest::loadHtml(const char* html, const char* baseURI)
+void WebViewTest::loadHtml(const char* html, const char* baseURI, WebKitWebView* webView)
 {
     if (!baseURI)
         m_activeURI = "about:blank";
     else
         m_activeURI = baseURI;
-    webkit_web_view_load_html(m_webView, html, baseURI);
-    g_assert_true(webkit_web_view_is_loading(m_webView));
-    g_assert_cmpstr(webkit_web_view_get_uri(m_webView), ==, m_activeURI.data());
+
+    if (!webView)
+        webView = m_webView;
+
+    webkit_web_view_load_html(webView, html, baseURI);
+    g_assert_true(webkit_web_view_is_loading(webView));
+    g_assert_cmpstr(webkit_web_view_get_uri(webView), ==, m_activeURI.data());
 }
 
 void WebViewTest::loadPlainText(const char* plainText)

Modified: trunk/Tools/TestWebKitAPI/glib/WebKitGLib/WebViewTest.h (295678 => 295679)


--- trunk/Tools/TestWebKitAPI/glib/WebKitGLib/WebViewTest.h	2022-06-21 00:29:18 UTC (rev 295678)
+++ trunk/Tools/TestWebKitAPI/glib/WebKitGLib/WebViewTest.h	2022-06-21 03:54:18 UTC (rev 295679)
@@ -37,7 +37,7 @@
     void platformDestroy();
 
     virtual void loadURI(const char* uri);
-    virtual void loadHtml(const char* html, const char* baseURI);
+    virtual void loadHtml(const char* html, const char* baseURI, WebKitWebView* = nullptr);
     virtual void loadPlainText(const char* plainText);
     virtual void loadRequest(WebKitURIRequest*);
     virtual void loadBytes(GBytes*, const char* mimeType, const char* encoding, const char* baseURI);
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to