[GTK] No way for applications to set notification permissions without waiting for...
authormcatanzaro@igalia.com <mcatanzaro@igalia.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 11 Jan 2017 03:56:12 +0000 (03:56 +0000)
committermcatanzaro@igalia.com <mcatanzaro@igalia.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 11 Jan 2017 03:56:12 +0000 (03:56 +0000)
https://bugs.webkit.org/show_bug.cgi?id=163366

Reviewed by Carlos Garcia Campos.

Source/WebKit2:

Websites can check the Notification.permission DOM property to check if they have permission
to show notifications *without triggering a permission request*. But the WebKitGTK+ API has
no way to affirmatively indicate notifications are allowed without a permission request. The
only way is via the permission request API, which is too late. It's a problem for Epiphany.
For example, open the Riot Matrix client in a web app; you will immediately see an info bar
informing the user that Riot does not have permission to send desktop notifications, even
though Epiphany automatically grants notification permission in web app mode when requested.
This problem is not exclusive to web apps; there is simply no way for notification
permission to be set correctly prior to a permission request for it. Fix this by introducing
a webkit_web_context_initialize_notification_permissions() API, and add a signal
WebKitWebContext::initialize-notification-permissions to indicate when it should be called.

* UIProcess/API/gtk/WebKitNotificationProvider.cpp:
(notificationPermissionsCallback):
(WebKitNotificationProvider::create):
(WebKitNotificationProvider::WebKitNotificationProvider):
(WebKitNotificationProvider::notificationPermissions):
(WebKitNotificationProvider::setNotificationPermissions):
* UIProcess/API/gtk/WebKitNotificationProvider.h:
* UIProcess/API/gtk/WebKitSecurityOrigin.cpp:
(webkitSecurityOriginGetSecurityOrigin):
* UIProcess/API/gtk/WebKitSecurityOriginPrivate.h:
* UIProcess/API/gtk/WebKitWebContext.cpp:
(webkitWebContextConstructed):
(webkit_web_context_class_init):
(addOriginToMap):
(webkit_web_context_initialize_notification_permissions):
(webkitWebContextInitializeNotificationPermissions):
* UIProcess/API/gtk/WebKitWebContext.h:
* UIProcess/API/gtk/WebKitWebContextPrivate.h:
* UIProcess/API/gtk/docs/webkit2gtk-4.0-sections.txt:

Tools:

* TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitWebView.cpp:
(testWebViewNotification):
(setInitialNotificationPermissionsAllowedCallback):
(setInitialNotificationPermissionsDisallowedCallback):
(testWebViewNotificationInitialPermissionAllowed):
(testWebViewNotificationInitialPermissionDisallowed):
(beforeAll):
* TestWebKitAPI/gtk/WebKit2Gtk/TestMain.h:
* TestWebKitAPI/gtk/WebKit2Gtk/WebViewTest.cpp:
(WebViewTest::WebViewTest):
(WebViewTest::initializeWebView):
* TestWebKitAPI/gtk/WebKit2Gtk/WebViewTest.h:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@210574 268f45cc-cd09-0410-ab3c-d52691b4dbfc

14 files changed:
Source/WebKit2/ChangeLog
Source/WebKit2/UIProcess/API/gtk/WebKitNotificationProvider.cpp
Source/WebKit2/UIProcess/API/gtk/WebKitNotificationProvider.h
Source/WebKit2/UIProcess/API/gtk/WebKitSecurityOrigin.cpp
Source/WebKit2/UIProcess/API/gtk/WebKitSecurityOriginPrivate.h
Source/WebKit2/UIProcess/API/gtk/WebKitWebContext.cpp
Source/WebKit2/UIProcess/API/gtk/WebKitWebContext.h
Source/WebKit2/UIProcess/API/gtk/WebKitWebContextPrivate.h
Source/WebKit2/UIProcess/API/gtk/docs/webkit2gtk-4.0-sections.txt
Tools/ChangeLog
Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitWebView.cpp
Tools/TestWebKitAPI/gtk/WebKit2Gtk/TestMain.h
Tools/TestWebKitAPI/gtk/WebKit2Gtk/WebViewTest.cpp
Tools/TestWebKitAPI/gtk/WebKit2Gtk/WebViewTest.h

index a5c0be3..531e1b2 100644 (file)
@@ -1,3 +1,42 @@
+2017-01-10  Michael Catanzaro  <mcatanzaro@igalia.com>
+
+        [GTK] No way for applications to set notification permissions without waiting for permission request
+        https://bugs.webkit.org/show_bug.cgi?id=163366
+
+        Reviewed by Carlos Garcia Campos.
+
+        Websites can check the Notification.permission DOM property to check if they have permission
+        to show notifications *without triggering a permission request*. But the WebKitGTK+ API has
+        no way to affirmatively indicate notifications are allowed without a permission request. The
+        only way is via the permission request API, which is too late. It's a problem for Epiphany.
+        For example, open the Riot Matrix client in a web app; you will immediately see an info bar
+        informing the user that Riot does not have permission to send desktop notifications, even
+        though Epiphany automatically grants notification permission in web app mode when requested.
+        This problem is not exclusive to web apps; there is simply no way for notification
+        permission to be set correctly prior to a permission request for it. Fix this by introducing
+        a webkit_web_context_initialize_notification_permissions() API, and add a signal
+        WebKitWebContext::initialize-notification-permissions to indicate when it should be called.
+
+        * UIProcess/API/gtk/WebKitNotificationProvider.cpp:
+        (notificationPermissionsCallback):
+        (WebKitNotificationProvider::create):
+        (WebKitNotificationProvider::WebKitNotificationProvider):
+        (WebKitNotificationProvider::notificationPermissions):
+        (WebKitNotificationProvider::setNotificationPermissions):
+        * UIProcess/API/gtk/WebKitNotificationProvider.h:
+        * UIProcess/API/gtk/WebKitSecurityOrigin.cpp:
+        (webkitSecurityOriginGetSecurityOrigin):
+        * UIProcess/API/gtk/WebKitSecurityOriginPrivate.h:
+        * UIProcess/API/gtk/WebKitWebContext.cpp:
+        (webkitWebContextConstructed):
+        (webkit_web_context_class_init):
+        (addOriginToMap):
+        (webkit_web_context_initialize_notification_permissions):
+        (webkitWebContextInitializeNotificationPermissions):
+        * UIProcess/API/gtk/WebKitWebContext.h:
+        * UIProcess/API/gtk/WebKitWebContextPrivate.h:
+        * UIProcess/API/gtk/docs/webkit2gtk-4.0-sections.txt:
+
 2017-01-10  Keith Rollin  <krollin@apple.com>
 
         Record/replay: fix range used for fuzzy matching
index a80e686..b60669a 100644 (file)
 #include "WebKitNotificationProvider.h"
 
 #include "APIArray.h"
+#include "APIDictionary.h"
 #include "WKNotificationManager.h"
 #include "WebKitNotificationPrivate.h"
+#include "WebKitWebContextPrivate.h"
 #include "WebKitWebViewPrivate.h"
 #include "WebNotificationManagerProxy.h"
 #include "WebPageProxy.h"
@@ -52,6 +54,11 @@ static void cancelCallback(WKNotificationRef notification, const void* clientInf
     toNotificationProvider(clientInfo)->cancel(*toImpl(notification));
 }
 
+static WKDictionaryRef notificationPermissionsCallback(const void* clientInfo)
+{
+    return toAPI(toNotificationProvider(clientInfo)->notificationPermissions().leakRef());
+}
+
 static void clearNotificationsCallback(WKArrayRef notificationIDs, const void* clientInfo)
 {
     toNotificationProvider(clientInfo)->clearNotifications(toImpl(notificationIDs));
@@ -61,13 +68,14 @@ WebKitNotificationProvider::~WebKitNotificationProvider()
 {
 }
 
-Ref<WebKitNotificationProvider> WebKitNotificationProvider::create(WebNotificationManagerProxy* notificationManager)
+Ref<WebKitNotificationProvider> WebKitNotificationProvider::create(WebNotificationManagerProxy* notificationManager, WebKitWebContext* webContext)
 {
-    return adoptRef(*new WebKitNotificationProvider(notificationManager));
+    return adoptRef(*new WebKitNotificationProvider(notificationManager, webContext));
 }
 
-WebKitNotificationProvider::WebKitNotificationProvider(WebNotificationManagerProxy* notificationManager)
-    : m_notificationManager(notificationManager)
+WebKitNotificationProvider::WebKitNotificationProvider(WebNotificationManagerProxy* notificationManager, WebKitWebContext* webContext)
+    : m_webContext(webContext)
+    , m_notificationManager(notificationManager)
 {
     ASSERT(notificationManager);
 
@@ -81,7 +89,7 @@ WebKitNotificationProvider::WebKitNotificationProvider(WebNotificationManagerPro
         0, // didDestroyNotificationCallback,
         0, // addNotificationManagerCallback,
         0, // removeNotificationManagerCallback,
-        0, // notificationPermissionsCallback,
+        notificationPermissionsCallback,
         clearNotificationsCallback,
     };
 
@@ -152,3 +160,14 @@ void WebKitNotificationProvider::clearNotifications(const API::Array* notificati
     for (const auto& item : notificationIDs->elementsOfType<API::UInt64>())
         cancelNotificationByID(item->value());
 }
+
+RefPtr<API::Dictionary> WebKitNotificationProvider::notificationPermissions()
+{
+    webkitWebContextInitializeNotificationPermissions(m_webContext);
+    return m_notificationPermissions;
+}
+
+void WebKitNotificationProvider::setNotificationPermissions(HashMap<String, RefPtr<API::Object>>&& permissionsMap)
+{
+    m_notificationPermissions = API::Dictionary::create(WTFMove(permissionsMap));
+}
index 2894ba0..49c9b9a 100644 (file)
@@ -22,6 +22,7 @@
 
 #include "WebKitPrivate.h"
 #include "WebKitNotification.h"
+#include "WebKitWebContext.h"
 #include <wtf/HashMap.h>
 #include <wtf/Ref.h>
 #include <wtf/RefCounted.h>
@@ -35,14 +36,17 @@ namespace WebKit {
 class WebKitNotificationProvider : public RefCounted<WebKitNotificationProvider> {
 public:
     virtual ~WebKitNotificationProvider();
-    static Ref<WebKitNotificationProvider> create(WebNotificationManagerProxy*);
+    static Ref<WebKitNotificationProvider> create(WebNotificationManagerProxy*, WebKitWebContext*);
 
     void show(WebPageProxy*, const WebNotification&);
     void cancel(const WebNotification&);
     void clearNotifications(const API::Array*);
 
+    RefPtr<API::Dictionary> notificationPermissions();
+    void setNotificationPermissions(HashMap<String, RefPtr<API::Object>>&&);
+
 private:
-    WebKitNotificationProvider(WebNotificationManagerProxy*);
+    WebKitNotificationProvider(WebNotificationManagerProxy*, WebKitWebContext*);
 
     void cancelNotificationByID(uint64_t);
     static void notificationCloseCallback(WebKitNotification*, WebKitNotificationProvider*);
@@ -50,6 +54,8 @@ private:
 
     void withdrawAnyPreviousNotificationMatchingTag(const CString&);
 
+    WebKitWebContext* m_webContext;
+    RefPtr<API::Dictionary> m_notificationPermissions;
     RefPtr<WebNotificationManagerProxy> m_notificationManager;
     HashMap<uint64_t, GRefPtr<WebKitNotification>> m_notifications;
 };
index 2334ccc..39224ce 100644 (file)
@@ -70,6 +70,12 @@ WebKitSecurityOrigin* webkitSecurityOriginCreate(Ref<WebCore::SecurityOrigin>&&
     return origin;
 }
 
+WebCore::SecurityOrigin& webkitSecurityOriginGetSecurityOrigin(WebKitSecurityOrigin* origin)
+{
+    ASSERT(origin);
+    return origin->securityOrigin.get();
+}
+
 /**
  * webkit_security_origin_new:
  * @protocol: The protocol for the new origin
index 1db3e37..d4e4a05 100644 (file)
@@ -30,3 +30,4 @@
 #include <WebCore/SecurityOrigin.h>
 
 WebKitSecurityOrigin* webkitSecurityOriginCreate(Ref<WebCore::SecurityOrigin>&&);
+WebCore::SecurityOrigin& webkitSecurityOriginGetSecurityOrigin(WebKitSecurityOrigin*);
index 73834cf..a99260e 100644 (file)
@@ -41,6 +41,7 @@
 #include "WebKitPluginPrivate.h"
 #include "WebKitPrivate.h"
 #include "WebKitSecurityManagerPrivate.h"
+#include "WebKitSecurityOriginPrivate.h"
 #include "WebKitSettingsPrivate.h"
 #include "WebKitURISchemeRequestPrivate.h"
 #include "WebKitUserContentManagerPrivate.h"
@@ -110,6 +111,7 @@ enum {
 enum {
     DOWNLOAD_STARTED,
     INITIALIZE_WEB_EXTENSIONS,
+    INITIALIZE_NOTIFICATION_PERMISSIONS,
 
     LAST_SIGNAL
 };
@@ -285,7 +287,7 @@ static void webkitWebContextConstructed(GObject* object)
     priv->geolocationProvider = WebKitGeolocationProvider::create(priv->processPool->supplement<WebGeolocationManagerProxy>());
 #endif
 #if ENABLE(NOTIFICATIONS)
-    priv->notificationProvider = WebKitNotificationProvider::create(priv->processPool->supplement<WebNotificationManagerProxy>());
+    priv->notificationProvider = WebKitNotificationProvider::create(priv->processPool->supplement<WebNotificationManagerProxy>(), webContext);
 #endif
 }
 
@@ -386,6 +388,30 @@ static void webkit_web_context_class_init(WebKitWebContextClass* webContextClass
             nullptr, nullptr,
             g_cclosure_marshal_VOID__VOID,
             G_TYPE_NONE, 0);
+
+    /**
+     * WebKitWebContext::initialize-notification-permissions:
+     * @context: the #WebKitWebContext
+     *
+     * This signal is emitted when a #WebKitWebContext needs to set
+     * initial notification permissions for a web process. It is emitted
+     * when a new web process is about to be launched, and signals the
+     * most appropriate moment to use
+     * webkit_web_context_initialize_notification_permissions(). If no
+     * notification permissions have changed since the last time this
+     * signal was emitted, then there is no need to call
+     * webkit_web_context_initialize_notification_permissions() again.
+     *
+     * Since: 2.16
+     */
+    signals[INITIALIZE_NOTIFICATION_PERMISSIONS] =
+        g_signal_new("initialize-notification-permissions",
+            G_TYPE_FROM_CLASS(gObjectClass),
+            G_SIGNAL_RUN_LAST,
+            G_STRUCT_OFFSET(WebKitWebContextClass, initialize_notification_permissions),
+            nullptr, nullptr,
+            g_cclosure_marshal_VOID__VOID,
+            G_TYPE_NONE, 0);
 }
 
 static gpointer createDefaultWebContext(gpointer)
@@ -1218,6 +1244,54 @@ guint webkit_web_context_get_web_process_count_limit(WebKitWebContext* context)
     return context->priv->processCountLimit;
 }
 
+static void addOriginToMap(WebKitSecurityOrigin* origin, HashMap<String, RefPtr<API::Object>>* map, bool allowed)
+{
+    String string = webkitSecurityOriginGetSecurityOrigin(origin).toString();
+    if (string != "null")
+        map->set(string, API::Boolean::create(allowed));
+}
+
+/**
+ * webkit_web_context_initialize_notification_permissions:
+ * @context: the #WebKitWebContext
+ * @allowed_origins: (element-type WebKitSecurityOrigin): a #GList of security origins
+ * @disallowed_origins: (element-type WebKitSecurityOrigin): a #GList of security origins
+ *
+ * Sets initial desktop notification permissions for the @context.
+ * @allowed_origins and @disallowed_origins must each be #GList of
+ * #WebKitSecurityOrigin objects representing origins that will,
+ * respectively, either always or never have permission to show desktop
+ * notifications. No #WebKitNotificationPermissionRequest will ever be
+ * generated for any of the security origins represented in
+ * @allowed_origins or @disallowed_origins. This function is necessary
+ * because some webpages proactively check whether they have permission
+ * to display notifications without ever creating a permission request.
+ *
+ * This function only affects web processes that have not already been
+ * created. The best time to call it is when handling
+ * #WebKitWebContext::initialize-notification-permissions so as to
+ * ensure that new web processes receive the most recent set of
+ * permissions.
+ *
+ * Since: 2.16
+ */
+void webkit_web_context_initialize_notification_permissions(WebKitWebContext* context, GList* allowedOrigins, GList* disallowedOrigins)
+{
+    HashMap<String, RefPtr<API::Object>> map;
+    g_list_foreach(allowedOrigins, [](gpointer data, gpointer userData) {
+        addOriginToMap(static_cast<WebKitSecurityOrigin*>(data), static_cast<HashMap<String, RefPtr<API::Object>>*>(userData), true);
+    }, &map);
+    g_list_foreach(disallowedOrigins, [](gpointer data, gpointer userData) {
+        addOriginToMap(static_cast<WebKitSecurityOrigin*>(data), static_cast<HashMap<String, RefPtr<API::Object>>*>(userData), false);
+    }, &map);
+    context->priv->notificationProvider->setNotificationPermissions(WTFMove(map));
+}
+
+void webkitWebContextInitializeNotificationPermissions(WebKitWebContext* context)
+{
+    g_signal_emit(context, signals[INITIALIZE_NOTIFICATION_PERMISSIONS], 0);
+}
+
 WebKitDownload* webkitWebContextGetOrCreateDownload(DownloadProxy* downloadProxy)
 {
     GRefPtr<WebKitDownload> download = downloadsMap().get(downloadProxy);
index cd55486..fc3793a 100644 (file)
@@ -128,16 +128,16 @@ struct _WebKitWebContext {
 struct _WebKitWebContextClass {
     GObjectClass parent;
 
-    void (* download_started)          (WebKitWebContext *context,
-                                        WebKitDownload   *download);
-    void (* initialize_web_extensions) (WebKitWebContext *context);
+    void (* download_started)                    (WebKitWebContext *context,
+                                                  WebKitDownload   *download);
+    void (* initialize_web_extensions)           (WebKitWebContext *context);
+    void (* initialize_notification_permissions) (WebKitWebContext *context);
 
     void (*_webkit_reserved0) (void);
     void (*_webkit_reserved1) (void);
     void (*_webkit_reserved2) (void);
     void (*_webkit_reserved3) (void);
     void (*_webkit_reserved4) (void);
-    void (*_webkit_reserved5) (void);
 };
 
 WEBKIT_API GType
@@ -264,6 +264,12 @@ webkit_web_context_set_process_model                (WebKitWebContext
 WEBKIT_API WebKitProcessModel
 webkit_web_context_get_process_model                (WebKitWebContext              *context);
 
+WEBKIT_API void
+webkit_web_context_initialize_notification_permissions
+                                                    (WebKitWebContext              *context,
+                                                     GList                         *allowed_origins,
+                                                     GList                         *disallowed_origins);
+
 G_END_DECLS
 
 #endif
index 63eb21a..b94e518 100644 (file)
@@ -48,5 +48,6 @@ void webkitWebContextCreatePageForWebView(WebKitWebContext*, WebKitWebView*, Web
 void webkitWebContextWebViewDestroyed(WebKitWebContext*, WebKitWebView*);
 WebKitWebView* webkitWebContextGetWebViewForPage(WebKitWebContext*, WebKit::WebPageProxy*);
 GVariant* webkitWebContextInitializeWebExtensions(WebKitWebContext*);
+void webkitWebContextInitializeNotificationPermissions(WebKitWebContext*);
 
 #endif // WebKitWebContextPrivate_h
index ee3afbb..35a053e 100644 (file)
@@ -59,6 +59,7 @@ webkit_web_context_set_disk_cache_directory
 webkit_web_context_allow_tls_certificate_for_host
 webkit_web_context_get_process_model
 webkit_web_context_set_process_model
+webkit_web_context_initialize_notification_permissions
 
 <SUBSECTION URI Scheme>
 WebKitURISchemeRequestCallback
index 3fd1558..4132758 100644 (file)
@@ -1,3 +1,23 @@
+2017-01-10  Michael Catanzaro  <mcatanzaro@igalia.com>
+
+        [GTK] No way for applications to set notification permissions without waiting for permission request
+        https://bugs.webkit.org/show_bug.cgi?id=163366
+
+        Reviewed by Carlos Garcia Campos.
+
+        * TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitWebView.cpp:
+        (testWebViewNotification):
+        (setInitialNotificationPermissionsAllowedCallback):
+        (setInitialNotificationPermissionsDisallowedCallback):
+        (testWebViewNotificationInitialPermissionAllowed):
+        (testWebViewNotificationInitialPermissionDisallowed):
+        (beforeAll):
+        * TestWebKitAPI/gtk/WebKit2Gtk/TestMain.h:
+        * TestWebKitAPI/gtk/WebKit2Gtk/WebViewTest.cpp:
+        (WebViewTest::WebViewTest):
+        (WebViewTest::initializeWebView):
+        * TestWebKitAPI/gtk/WebKit2Gtk/WebViewTest.h:
+
 2017-01-10  Tim Horton  <timothy_horton@apple.com>
 
         MobileMiniBrowser tests don't work by default
index 292f556..d967307 100644 (file)
@@ -632,7 +632,17 @@ static void testWebViewSnapshot(SnapshotWebViewTest* test, gconstpointer)
 
 class NotificationWebViewTest: public WebViewTest {
 public:
-    MAKE_GLIB_TEST_FIXTURE(NotificationWebViewTest);
+    MAKE_GLIB_TEST_FIXTURE_WITH_SETUP_TEARDOWN(NotificationWebViewTest, setup, teardown);
+
+    static void setup()
+    {
+        WebViewTest::shouldInitializeWebViewInConstructor = false;
+    }
+
+    static void teardown()
+    {
+        WebViewTest::shouldInitializeWebViewInConstructor = true;
+    }
 
     enum NotificationEvent {
         None,
@@ -647,6 +657,7 @@ public:
     static gboolean permissionRequestCallback(WebKitWebView*, WebKitPermissionRequest *request, NotificationWebViewTest* test)
     {
         g_assert(WEBKIT_IS_NOTIFICATION_PERMISSION_REQUEST(request));
+        g_assert(test->m_isExpectingPermissionRequest);
         test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(request));
 
         test->m_event = Permission;
@@ -699,10 +710,9 @@ public:
         g_main_loop_quit(test->m_mainLoop);
     }
 
-    NotificationWebViewTest()
-        : m_notification(nullptr)
-        , m_event(None)
+    void initialize()
     {
+        initializeWebView();
         g_signal_connect(m_webView, "permission-request", G_CALLBACK(permissionRequestCallback), this);
         g_signal_connect(m_webView, "show-notification", G_CALLBACK(showNotificationCallback), this);
         webkit_user_content_manager_register_script_message_handler(m_userContentManager.get(), "notifications");
@@ -716,9 +726,18 @@ public:
         webkit_user_content_manager_unregister_script_message_handler(m_userContentManager.get(), "notifications");
     }
 
+    bool hasPermission()
+    {
+        auto* result = runJavaScriptAndWaitUntilFinished("Notification.permission;", nullptr);
+        g_assert(result);
+        GUniquePtr<char> value(javascriptResultToCString(result));
+        return !g_strcmp0(value.get(), "granted");
+    }
+
    void requestPermissionAndWaitUntilGiven()
     {
         m_event = None;
+        m_isExpectingPermissionRequest = true;
         webkit_web_view_run_javascript(m_webView, "Notification.requestPermission();", nullptr, nullptr, nullptr);
         g_main_loop_run(m_mainLoop);
     }
@@ -769,18 +788,24 @@ public:
         g_main_loop_run(m_mainLoop);
     }
 
-    NotificationEvent m_event;
-    WebKitNotification* m_notification;
+    NotificationEvent m_event { None };
+    WebKitNotification* m_notification { nullptr };
+    bool m_isExpectingPermissionRequest { false };
+    bool m_hasPermission { false };
 };
 
 static void testWebViewNotification(NotificationWebViewTest* test, gconstpointer)
 {
+    test->initialize();
+
     // Notifications don't work with local or special schemes.
     test->loadURI(gServer->getURIForPath("/").data());
     test->waitUntilLoadFinished();
+    g_assert(!test->hasPermission());
 
     test->requestPermissionAndWaitUntilGiven();
     g_assert(test->m_event == NotificationWebViewTest::Permission);
+    g_assert(test->hasPermission());
 
     static const char* title = "This is a notification";
     static const char* body = "This is the body.";
@@ -818,6 +843,45 @@ static void testWebViewNotification(NotificationWebViewTest* test, gconstpointer
     g_assert(test->m_event == NotificationWebViewTest::Closed);
 }
 
+static void setInitialNotificationPermissionsAllowedCallback(WebKitWebContext* context, NotificationWebViewTest* test)
+{
+    GUniquePtr<char> baseURI(soup_uri_to_string(gServer->baseURI(), FALSE));
+    GList* allowedOrigins = g_list_prepend(nullptr, webkit_security_origin_new_for_uri(baseURI.get()));
+    webkit_web_context_initialize_notification_permissions(test->m_webContext.get(), allowedOrigins, nullptr);
+    g_list_free_full(allowedOrigins, reinterpret_cast<GDestroyNotify>(webkit_security_origin_unref));
+}
+
+static void setInitialNotificationPermissionsDisallowedCallback(WebKitWebContext* context, NotificationWebViewTest* test)
+{
+    GUniquePtr<char> baseURI(soup_uri_to_string(gServer->baseURI(), FALSE));
+    GList* disallowedOrigins = g_list_prepend(nullptr, webkit_security_origin_new_for_uri(baseURI.get()));
+    webkit_web_context_initialize_notification_permissions(test->m_webContext.get(), nullptr, disallowedOrigins);
+    g_list_free_full(disallowedOrigins, reinterpret_cast<GDestroyNotify>(webkit_security_origin_unref));
+}
+
+static void testWebViewNotificationInitialPermissionAllowed(NotificationWebViewTest* test, gconstpointer)
+{
+    g_signal_connect(test->m_webContext.get(), "initialize-notification-permissions", G_CALLBACK(setInitialNotificationPermissionsAllowedCallback), test);
+    test->initialize();
+
+    test->loadURI(gServer->getURIForPath("/").data());
+    test->waitUntilLoadFinished();
+    g_assert(test->hasPermission());
+
+    test->requestNotificationAndWaitUntilShown("This is a notification", "This is the body.");
+    g_assert(test->m_event == NotificationWebViewTest::Shown);
+}
+
+static void testWebViewNotificationInitialPermissionDisallowed(NotificationWebViewTest* test, gconstpointer)
+{
+    g_signal_connect(test->m_webContext.get(), "initialize-notification-permissions", G_CALLBACK(setInitialNotificationPermissionsDisallowedCallback), test);
+    test->initialize();
+
+    test->loadURI(gServer->getURIForPath("/").data());
+    test->waitUntilLoadFinished();
+    g_assert(!test->hasPermission());
+}
+
 static void testWebViewIsPlayingAudio(IsPlayingAudioWebViewTest* test, gconstpointer)
 {
     // The web view must be realized for the video to start playback and
@@ -915,6 +979,8 @@ void beforeAll()
     SnapshotWebViewTest::add("WebKitWebView", "snapshot", testWebViewSnapshot);
     WebViewTest::add("WebKitWebView", "page-visibility", testWebViewPageVisibility);
     NotificationWebViewTest::add("WebKitWebView", "notification", testWebViewNotification);
+    NotificationWebViewTest::add("WebKitWebView", "notification-initial-permission-allowed", testWebViewNotificationInitialPermissionAllowed);
+    NotificationWebViewTest::add("WebKitWebView", "notification-initial-permission-disallowed", testWebViewNotificationInitialPermissionDisallowed);
     IsPlayingAudioWebViewTest::add("WebKitWebView", "is-playing-audio", testWebViewIsPlayingAudio);
     WebViewTest::add("WebKitWebView", "background-color", testWebViewBackgroundColor);
     WebViewTest::add("WebKitWebView", "preferred-size", testWebViewPreferredSize);
index 6054fe0..f68af2a 100644 (file)
         g_test_add(testPath.get(), ClassName, 0, ClassName::setUp, testFunc, ClassName::tearDown); \
     }
 
+#define MAKE_GLIB_TEST_FIXTURE_WITH_SETUP_TEARDOWN(ClassName, setup, teardown) \
+    static void setUp(ClassName* fixture, gconstpointer data) \
+    { \
+        if (setup) \
+            setup(); \
+        new (fixture) ClassName; \
+    } \
+    static void tearDown(ClassName* fixture, gconstpointer data) \
+    { \
+        fixture->~ClassName(); \
+        if (teardown) \
+            teardown(); \
+    } \
+    static void add(const char* suiteName, const char* testName, void (*testFunc)(ClassName*, const void*)) \
+    { \
+        GUniquePtr<gchar> testPath(g_strdup_printf("/webkit2/%s/%s", suiteName, testName)); \
+        g_test_add(testPath.get(), ClassName, 0, ClassName::setUp, testFunc, ClassName::tearDown); \
+    }
+
 #define ASSERT_CMP_CSTRING(s1, cmp, s2) \
     do {                                                                 \
         CString __s1 = (s1);                                             \
index 4a22145..880d064 100644 (file)
 #include <JavaScriptCore/JSRetainPtr.h>
 #include <WebCore/GUniquePtrGtk.h>
 
+bool WebViewTest::shouldInitializeWebViewInConstructor = true;
+
 WebViewTest::WebViewTest()
     : m_userContentManager(adoptGRef(webkit_user_content_manager_new()))
-    , m_webView(WEBKIT_WEB_VIEW(g_object_ref_sink(g_object_new(WEBKIT_TYPE_WEB_VIEW, "web-context", m_webContext.get(), "user-content-manager", m_userContentManager.get(), nullptr))))
     , m_mainLoop(g_main_loop_new(nullptr, TRUE))
-    , m_parentWindow(nullptr)
-    , m_javascriptResult(nullptr)
-    , m_resourceDataSize(0)
-    , m_surface(nullptr)
-    , m_expectedWebProcessCrash(false)
 {
-    assertObjectIsDeletedWhenTestFinishes(G_OBJECT(m_webView));
+    if (shouldInitializeWebViewInConstructor)
+        initializeWebView();
     assertObjectIsDeletedWhenTestFinishes(G_OBJECT(m_userContentManager.get()));
-    g_signal_connect(m_webView, "web-process-crashed", G_CALLBACK(WebViewTest::webProcessCrashed), this);
 }
 
 WebViewTest::~WebViewTest()
@@ -51,6 +47,15 @@ WebViewTest::~WebViewTest()
     g_main_loop_unref(m_mainLoop);
 }
 
+void WebViewTest::initializeWebView()
+{
+    g_assert(!m_webView);
+    m_webView = WEBKIT_WEB_VIEW(g_object_ref_sink(g_object_new(WEBKIT_TYPE_WEB_VIEW, "web-context", m_webContext.get(), "user-content-manager", m_userContentManager.get(), nullptr)));
+    assertObjectIsDeletedWhenTestFinishes(G_OBJECT(m_webView));
+
+    g_signal_connect(m_webView, "web-process-crashed", G_CALLBACK(WebViewTest::webProcessCrashed), this);
+}
+
 gboolean WebViewTest::webProcessCrashed(WebKitWebView*, WebViewTest* test)
 {
     if (test->m_expectedWebProcessCrash) {
index a3001b2..4f0c4c7 100644 (file)
@@ -31,6 +31,9 @@ public:
     WebViewTest();
     virtual ~WebViewTest();
 
+    static bool shouldInitializeWebViewInConstructor;
+    void initializeWebView();
+
     virtual void loadURI(const char* uri);
     virtual void loadHtml(const char* html, const char* baseURI);
     virtual void loadPlainText(const char* plainText);
@@ -81,17 +84,17 @@ public:
     static gboolean webProcessCrashed(WebKitWebView*, WebViewTest*);
 
     GRefPtr<WebKitUserContentManager> m_userContentManager;
-    WebKitWebView* m_webView;
+    WebKitWebView* m_webView { nullptr };
     GMainLoop* m_mainLoop;
     CString m_activeURI;
-    GtkWidget* m_parentWindow;
+    GtkWidget* m_parentWindow { nullptr };
     CString m_expectedTitle;
-    WebKitJavascriptResult* m_javascriptResult;
-    GError** m_javascriptError;
-    GUniquePtr<char> m_resourceData;
-    size_t m_resourceDataSize;
-    cairo_surface_t* m_surface;
-    bool m_expectedWebProcessCrash;
+    WebKitJavascriptResult* m_javascriptResult { nullptr };
+    GError** m_javascriptError { nullptr };
+    GUniquePtr<char> m_resourceData { nullptr };
+    size_t m_resourceDataSize { 0 };
+    cairo_surface_t* m_surface { nullptr };
+    bool m_expectedWebProcessCrash { false };
 
 private:
     void doMouseButtonEvent(GdkEventType, int, int, unsigned, unsigned);