Title: [216006] trunk
Revision
216006
Author
[email protected]
Date
2017-05-01 00:03:58 -0700 (Mon, 01 May 2017)

Log Message

[GTK] Add automation session API
https://bugs.webkit.org/show_bug.cgi?id=171428

Reviewed by Carlos Alberto Lopez Perez.

Source/WebCore/platform/gtk/po:

* POTFILES.in: Add WebKitAutomationSession.cpp.

Source/WebKit2:

Add new API to allow WebKitGTK+ to be controlled by automation. By default it's disabled, applications have to
explicitly call webkit_web_context_set_automation_allowed() to enabled it. When a new automation session is
requested, the signal WebKitWebContext::automation-started is emitted with a newly created
WebKitAutomationSession, similar to how WebKitWebContext::download-started works. The user should connect to it
have access to the WebKitAutomationSession and connect it its signals. When a new web view is requested to be
created in a WebKitAutomationSession, the signal WebKitAutomationSession::create-web-view is emitted. The user
should connect to it and return a WebKitWebView. The web views used for automation are special ones and need to
be created with the new WebKitWebView:is-controlled-by-automation construct property enabled, otherwise we will
refuse to create the requested browsing context.

* PlatformGTK.cmake:
* UIProcess/API/gtk/WebKitAutomationSession.cpp: Added.
(webkitAutomationSessionSetProperty):
(webkitAutomationSessionConstructed):
(webkit_automation_session_class_init):
(webkitAutomationSessionCreate):
(webkitAutomationSessionGetSession):
(webkit_automation_session_get_id):
* UIProcess/API/gtk/WebKitAutomationSession.h: Added.
* UIProcess/API/gtk/WebKitAutomationSessionPrivate.h: Added.
* UIProcess/API/gtk/WebKitWebContext.cpp:
(webkitWebContextConstructed):
(webkit_web_context_class_init):
(webkit_web_context_is_automation_allowed):
(webkit_web_context_set_automation_allowed):
(webkitWebContextCreatePageForWebView):
* UIProcess/API/gtk/WebKitWebContext.h:
* UIProcess/API/gtk/WebKitWebView.cpp:
(webkitWebViewConstructed):
(webkitWebViewSetProperty):
(webkitWebViewGetProperty):
(webkit_web_view_class_init):
(webkit_web_view_is_controlled_by_automation):
* UIProcess/API/gtk/WebKitWebView.h:
* UIProcess/API/gtk/docs/webkit2gtk-4.0-sections.txt:
* UIProcess/API/gtk/docs/webkit2gtk-4.0.types:
* UIProcess/API/gtk/docs/webkit2gtk-docs.sgml:
* UIProcess/API/gtk/webkit2.h:

Tools:

Add an automation mode to MiniBrowser using the new API and add a unit test too.

* MiniBrowser/gtk/BrowserWindow.c:
(webViewTitleChanged):
(webViewDecidePolicy):
(newTabCallback):
(openPrivateWindow):
(browserWindowFinalize):
(browser_window_init):
(browser_window_get_or_create_web_view_for_automation):
* MiniBrowser/gtk/BrowserWindow.h:
* MiniBrowser/gtk/main.c:
(createBrowserTab):
(createWebViewForAutomationCallback):
(automationStartedCallback):
(main):
* TestWebKitAPI/Tests/WebKit2Gtk/CMakeLists.txt:
* TestWebKitAPI/Tests/WebKit2Gtk/TestAutomationSession.cpp: Added.
(testAutomationSessionRequestSession):
(beforeAll):
(afterAll):

Modified Paths

Added Paths

Diff

Modified: trunk/Source/WebCore/platform/gtk/po/ChangeLog (216005 => 216006)


--- trunk/Source/WebCore/platform/gtk/po/ChangeLog	2017-05-01 07:02:07 UTC (rev 216005)
+++ trunk/Source/WebCore/platform/gtk/po/ChangeLog	2017-05-01 07:03:58 UTC (rev 216006)
@@ -1,3 +1,12 @@
+2017-05-01  Carlos Garcia Campos  <[email protected]>
+
+        [GTK] Add automation session API
+        https://bugs.webkit.org/show_bug.cgi?id=171428
+
+        Reviewed by Carlos Alberto Lopez Perez.
+
+        * POTFILES.in: Add WebKitAutomationSession.cpp.
+
 2017-04-25  Carlos Garcia Campos  <[email protected]>
 
         [GTK] Add WebKitInspectorWindow to create inspector windows from local and remote inspector

Modified: trunk/Source/WebCore/platform/gtk/po/POTFILES.in (216005 => 216006)


--- trunk/Source/WebCore/platform/gtk/po/POTFILES.in	2017-05-01 07:02:07 UTC (rev 216005)
+++ trunk/Source/WebCore/platform/gtk/po/POTFILES.in	2017-05-01 07:03:58 UTC (rev 216006)
@@ -7,6 +7,7 @@
 ../../../WebKit2/Shared/gtk/WebErrorsGtk.cpp
 ../../../WebKit2/Shared/soup/WebErrorsSoup.cpp
 ../../../WebKit2/UIProcess/API/gtk/WebKitAuthenticationDialog.cpp
+../../../WebKit2/UIProcess/API/gtk/WebKitAutomationSession.cpp
 ../../../WebKit2/UIProcess/API/gtk/WebKitColorChooserRequest.cpp
 ../../../WebKit2/UIProcess/API/gtk/WebKitDownload.cpp
 ../../../WebKit2/UIProcess/API/gtk/WebKitEditorState.cpp

Modified: trunk/Source/WebKit2/ChangeLog (216005 => 216006)


--- trunk/Source/WebKit2/ChangeLog	2017-05-01 07:02:07 UTC (rev 216005)
+++ trunk/Source/WebKit2/ChangeLog	2017-05-01 07:03:58 UTC (rev 216006)
@@ -1,5 +1,51 @@
 2017-05-01  Carlos Garcia Campos  <[email protected]>
 
+        [GTK] Add automation session API
+        https://bugs.webkit.org/show_bug.cgi?id=171428
+
+        Reviewed by Carlos Alberto Lopez Perez.
+
+        Add new API to allow WebKitGTK+ to be controlled by automation. By default it's disabled, applications have to
+        explicitly call webkit_web_context_set_automation_allowed() to enabled it. When a new automation session is
+        requested, the signal WebKitWebContext::automation-started is emitted with a newly created
+        WebKitAutomationSession, similar to how WebKitWebContext::download-started works. The user should connect to it
+        have access to the WebKitAutomationSession and connect it its signals. When a new web view is requested to be
+        created in a WebKitAutomationSession, the signal WebKitAutomationSession::create-web-view is emitted. The user
+        should connect to it and return a WebKitWebView. The web views used for automation are special ones and need to
+        be created with the new WebKitWebView:is-controlled-by-automation construct property enabled, otherwise we will
+        refuse to create the requested browsing context.
+
+        * PlatformGTK.cmake:
+        * UIProcess/API/gtk/WebKitAutomationSession.cpp: Added.
+        (webkitAutomationSessionSetProperty):
+        (webkitAutomationSessionConstructed):
+        (webkit_automation_session_class_init):
+        (webkitAutomationSessionCreate):
+        (webkitAutomationSessionGetSession):
+        (webkit_automation_session_get_id):
+        * UIProcess/API/gtk/WebKitAutomationSession.h: Added.
+        * UIProcess/API/gtk/WebKitAutomationSessionPrivate.h: Added.
+        * UIProcess/API/gtk/WebKitWebContext.cpp:
+        (webkitWebContextConstructed):
+        (webkit_web_context_class_init):
+        (webkit_web_context_is_automation_allowed):
+        (webkit_web_context_set_automation_allowed):
+        (webkitWebContextCreatePageForWebView):
+        * UIProcess/API/gtk/WebKitWebContext.h:
+        * UIProcess/API/gtk/WebKitWebView.cpp:
+        (webkitWebViewConstructed):
+        (webkitWebViewSetProperty):
+        (webkitWebViewGetProperty):
+        (webkit_web_view_class_init):
+        (webkit_web_view_is_controlled_by_automation):
+        * UIProcess/API/gtk/WebKitWebView.h:
+        * UIProcess/API/gtk/docs/webkit2gtk-4.0-sections.txt:
+        * UIProcess/API/gtk/docs/webkit2gtk-4.0.types:
+        * UIProcess/API/gtk/docs/webkit2gtk-docs.sgml:
+        * UIProcess/API/gtk/webkit2.h:
+
+2017-05-01  Carlos Garcia Campos  <[email protected]>
+
         Unreviewed. Fix GTK+ debug build after r216002.
 
         * WebProcess/InjectedBundle/API/gtk/DOM/WebKitDOMClientRectList.cpp:

Modified: trunk/Source/WebKit2/PlatformGTK.cmake (216005 => 216006)


--- trunk/Source/WebKit2/PlatformGTK.cmake	2017-05-01 07:02:07 UTC (rev 216005)
+++ trunk/Source/WebKit2/PlatformGTK.cmake	2017-05-01 07:03:58 UTC (rev 216006)
@@ -124,6 +124,9 @@
     UIProcess/API/gtk/WebKitAuthenticationRequest.cpp
     UIProcess/API/gtk/WebKitAuthenticationRequest.h
     UIProcess/API/gtk/WebKitAutocleanups.h
+    UIProcess/API/gtk/WebKitAutomationSession.cpp
+    UIProcess/API/gtk/WebKitAutomationSession.h
+    UIProcess/API/gtk/WebKitAutomationSessionPrivate.h
     UIProcess/API/gtk/WebKitBackForwardList.cpp
     UIProcess/API/gtk/WebKitBackForwardList.h
     UIProcess/API/gtk/WebKitBackForwardListItem.cpp
@@ -533,6 +536,7 @@
     ${DERIVED_SOURCES_WEBKIT2GTK_API_DIR}/WebKitVersion.h
     ${WEBKIT2_DIR}/UIProcess/API/gtk/WebKitAuthenticationRequest.h
     ${WEBKIT2_DIR}/UIProcess/API/gtk/WebKitAutocleanups.h
+    ${WEBKIT2_DIR}/UIProcess/API/gtk/WebKitAutomationSession.h
     ${WEBKIT2_DIR}/UIProcess/API/gtk/WebKitBackForwardList.h
     ${WEBKIT2_DIR}/UIProcess/API/gtk/WebKitBackForwardListItem.h
     ${WEBKIT2_DIR}/UIProcess/API/gtk/WebKitColorChooserRequest.h

Added: trunk/Source/WebKit2/UIProcess/API/gtk/WebKitAutomationSession.cpp (0 => 216006)


--- trunk/Source/WebKit2/UIProcess/API/gtk/WebKitAutomationSession.cpp	                        (rev 0)
+++ trunk/Source/WebKit2/UIProcess/API/gtk/WebKitAutomationSession.cpp	2017-05-01 07:03:58 UTC (rev 216006)
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2017 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "WebKitAutomationSession.h"
+
+#include "APIAutomationSessionClient.h"
+#include "WebKitAutomationSessionPrivate.h"
+#include "WebKitPrivate.h"
+#include "WebKitWebView.h"
+#include "WebKitWebViewBasePrivate.h"
+#include <glib/gi18n-lib.h>
+#include <wtf/text/CString.h>
+
+using namespace WebKit;
+
+/**
+ * SECTION: WebKitAutomationSession
+ * @Short_description: Automation Session
+ * @Title: WebKitAutomationSession
+ *
+ * WebKitAutomationSession represents an automation session of a WebKitWebContext.
+ * When a new session is requested, a WebKitAutomationSession is created and the signal
+ * WebKitWebContext::automation-started is emitted with the WebKitAutomationSession as
+ * argument. Then, the automation client can request the session to create a new
+ * #WebKitWebView to interact with it. When this happens the signal #WebKitAutomationSession::create-web-view
+ * is emitted.
+ *
+ * Since: 2.18
+ */
+
+enum {
+    PROP_0,
+
+    PROP_ID
+};
+
+enum {
+    CREATE_WEB_VIEW,
+
+    LAST_SIGNAL
+};
+
+struct _WebKitAutomationSessionPrivate {
+    RefPtr<WebAutomationSession> session;
+    CString id;
+};
+
+static guint signals[LAST_SIGNAL] = { 0, };
+
+WEBKIT_DEFINE_TYPE(WebKitAutomationSession, webkit_automation_session, G_TYPE_OBJECT)
+
+class AutomationSessionClient final : public API::AutomationSessionClient {
+public:
+    explicit AutomationSessionClient(WebKitAutomationSession* session)
+        : m_session(session)
+    {
+    }
+
+private:
+    String sessionIdentifier() const override
+    {
+        return String::fromUTF8(m_session->priv->id.data());
+    }
+
+    WebPageProxy* didRequestNewWindow(WebAutomationSession&) override
+    {
+        WebKitWebView* webView = nullptr;
+        g_signal_emit(m_session, signals[CREATE_WEB_VIEW], 0, &webView);
+        if (!webView || !webkit_web_view_is_controlled_by_automation(webView))
+            return nullptr;
+
+        return webkitWebViewBaseGetPage(WEBKIT_WEB_VIEW_BASE(webView));
+    }
+
+    WebKitAutomationSession* m_session;
+};
+
+static void webkitAutomationSessionGetProperty(GObject* object, guint propID, GValue* value, GParamSpec* paramSpec)
+{
+    WebKitAutomationSession* session = WEBKIT_AUTOMATION_SESSION(object);
+
+    switch (propID) {
+    case PROP_ID:
+        g_value_set_string(value, session->priv->id.data());
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, paramSpec);
+    }
+}
+
+static void webkitAutomationSessionSetProperty(GObject* object, guint propID, const GValue* value, GParamSpec* paramSpec)
+{
+    WebKitAutomationSession* session = WEBKIT_AUTOMATION_SESSION(object);
+
+    switch (propID) {
+    case PROP_ID:
+        session->priv->id = g_value_get_string(value);
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, paramSpec);
+    }
+}
+
+static void webkitAutomationSessionConstructed(GObject* object)
+{
+    WebKitAutomationSession* session = WEBKIT_AUTOMATION_SESSION(object);
+
+    G_OBJECT_CLASS(webkit_automation_session_parent_class)->constructed(object);
+
+    session->priv->session = adoptRef(new WebAutomationSession());
+    session->priv->session->setSessionIdentifier(String::fromUTF8(session->priv->id.data()));
+    session->priv->session->setClient(std::make_unique<AutomationSessionClient>(session));
+}
+
+static void webkit_automation_session_class_init(WebKitAutomationSessionClass* sessionClass)
+{
+    GObjectClass* gObjectClass = G_OBJECT_CLASS(sessionClass);
+    gObjectClass->get_property = webkitAutomationSessionGetProperty;
+    gObjectClass->set_property = webkitAutomationSessionSetProperty;
+    gObjectClass->constructed = webkitAutomationSessionConstructed;
+
+    /**
+     * WebKitAutomationSession:id:
+     *
+     * The session unique identifier.
+     *
+     * Since: 2.18
+     */
+    g_object_class_install_property(
+        gObjectClass,
+        PROP_ID,
+        g_param_spec_string(
+            "id",
+            _("Identifier"),
+            _("The automation session identifier"),
+            nullptr,
+            static_cast<GParamFlags>(WEBKIT_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)));
+
+    /**
+     * WebKitAutomationSession::create-web-view:
+     * @session: a #WebKitAutomationSession
+     *
+     * This signal is emitted when the automation client requests a new
+     * browsing context to interact with it. The callback handler should
+     * return a #WebKitWebView created with #WebKitWebView:is-controlled-by-automation
+     * construct property enabled. The returned #WebKitWebView could be an existing
+     * web view or a new one created and added to a new tab or window.
+     *
+     * Returns: (transfer none): a #WebKitWebView widget.
+     *
+     * Since: 2.18
+     */
+    signals[CREATE_WEB_VIEW] = g_signal_new(
+        "create-web-view",
+        G_TYPE_FROM_CLASS(sessionClass),
+        G_SIGNAL_RUN_LAST,
+        0,
+        nullptr, nullptr,
+        g_cclosure_marshal_generic,
+        WEBKIT_TYPE_WEB_VIEW, 0,
+        G_TYPE_NONE);
+}
+
+WebKitAutomationSession* webkitAutomationSessionCreate(const char* sessionID)
+{
+    return WEBKIT_AUTOMATION_SESSION(g_object_new(WEBKIT_TYPE_AUTOMATION_SESSION, "id", sessionID, nullptr));
+}
+
+WebAutomationSession& webkitAutomationSessionGetSession(WebKitAutomationSession* session)
+{
+    return *session->priv->session;
+}
+
+/**
+ * webkit_automation_session_get_id:
+ * @session: a #WebKitAutomationSession
+ *
+ * Get the unique identifier of a #WebKitAutomationSession
+ *
+ * Returns: the unique identifier of @session
+ *
+ * Since: 2.18
+ */
+const char* webkit_automation_session_get_id(WebKitAutomationSession* session)
+{
+    g_return_val_if_fail(WEBKIT_IS_AUTOMATION_SESSION(session), nullptr);
+    return session->priv->id.data();
+}

Added: trunk/Source/WebKit2/UIProcess/API/gtk/WebKitAutomationSession.h (0 => 216006)


--- trunk/Source/WebKit2/UIProcess/API/gtk/WebKitAutomationSession.h	                        (rev 0)
+++ trunk/Source/WebKit2/UIProcess/API/gtk/WebKitAutomationSession.h	2017-05-01 07:03:58 UTC (rev 216006)
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#if !defined(__WEBKIT2_H_INSIDE__) && !defined(WEBKIT2_COMPILATION)
+#error "Only <webkit2/webkit2.h> can be included directly."
+#endif
+
+#ifndef WebKitAutomationSession_h
+#define WebKitAutomationSession_h
+
+#include <glib-object.h>
+#include <webkit2/WebKitDefines.h>
+
+G_BEGIN_DECLS
+
+#define WEBKIT_TYPE_AUTOMATION_SESSION            (webkit_automation_session_get_type())
+#define WEBKIT_AUTOMATION_SESSION(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), WEBKIT_TYPE_AUTOMATION_SESSION, WebKitAutomationSession))
+#define WEBKIT_IS_AUTOMATION_SESSION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), WEBKIT_TYPE_AUTOMATION_SESSION))
+#define WEBKIT_AUTOMATION_SESSION_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass),  WEBKIT_TYPE_AUTOMATION_SESSION, WebKitAutomationSessionClass))
+#define WEBKIT_IS_AUTOMATION_SESSION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),  WEBKIT_TYPE_AUTOMATION_SESSION))
+#define WEBKIT_AUTOMATION_SESSION_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj),  WEBKIT_TYPE_AUTOMATION_SESSION, WebKitAutomationSessionClass))
+
+typedef struct _WebKitAutomationSession        WebKitAutomationSession;
+typedef struct _WebKitAutomationSessionClass   WebKitAutomationSessionClass;
+typedef struct _WebKitAutomationSessionPrivate WebKitAutomationSessionPrivate;
+
+struct _WebKitAutomationSession {
+    GObject parent;
+
+    WebKitAutomationSessionPrivate *priv;
+};
+
+struct _WebKitAutomationSessionClass {
+    GObjectClass parent_class;
+
+    void (*_webkit_reserved0) (void);
+    void (*_webkit_reserved1) (void);
+    void (*_webkit_reserved2) (void);
+    void (*_webkit_reserved3) (void);
+};
+
+WEBKIT_API GType
+webkit_automation_session_get_type (void);
+
+WEBKIT_API const char *
+webkit_automation_session_get_id   (WebKitAutomationSession *session);
+
+G_END_DECLS
+
+#endif /* WebKitAutomationSession_h */

Added: trunk/Source/WebKit2/UIProcess/API/gtk/WebKitAutomationSessionPrivate.h (0 => 216006)


--- trunk/Source/WebKit2/UIProcess/API/gtk/WebKitAutomationSessionPrivate.h	                        (rev 0)
+++ trunk/Source/WebKit2/UIProcess/API/gtk/WebKitAutomationSessionPrivate.h	2017-05-01 07:03:58 UTC (rev 216006)
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2017 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include "WebAutomationSession.h"
+#include "WebKitAutomationSession.h"
+
+WebKitAutomationSession* webkitAutomationSessionCreate(const char* sessionID);
+WebKit::WebAutomationSession& webkitAutomationSessionGetSession(WebKitAutomationSession*);

Modified: trunk/Source/WebKit2/UIProcess/API/gtk/WebKitWebContext.cpp (216005 => 216006)


--- trunk/Source/WebKit2/UIProcess/API/gtk/WebKitWebContext.cpp	2017-05-01 07:02:07 UTC (rev 216005)
+++ trunk/Source/WebKit2/UIProcess/API/gtk/WebKitWebContext.cpp	2017-05-01 07:03:58 UTC (rev 216006)
@@ -20,6 +20,7 @@
 #include "config.h"
 #include "WebKitWebContext.h"
 
+#include "APIAutomationClient.h"
 #include "APICustomProtocolManagerClient.h"
 #include "APIDownloadClient.h"
 #include "APIPageConfiguration.h"
@@ -27,8 +28,10 @@
 #include "APIString.h"
 #include "TextChecker.h"
 #include "TextCheckerState.h"
+#include "WebAutomationSession.h"
 #include "WebCertificateInfo.h"
 #include "WebGeolocationManagerProxy.h"
+#include "WebKitAutomationSessionPrivate.h"
 #include "WebKitCustomProtocolManagerClient.h"
 #include "WebKitDownloadClient.h"
 #include "WebKitDownloadPrivate.h"
@@ -51,6 +54,7 @@
 #include "WebKitWebsiteDataManagerPrivate.h"
 #include "WebNotificationManagerProxy.h"
 #include "WebsiteDataType.h"
+#include <_javascript_Core/RemoteInspector.h>
 #include <WebCore/FileSystem.h>
 #include <WebCore/IconDatabase.h>
 #include <WebCore/Language.h>
@@ -110,6 +114,7 @@
     DOWNLOAD_STARTED,
     INITIALIZE_WEB_EXTENSIONS,
     INITIALIZE_NOTIFICATION_PERMISSIONS,
+    AUTOMATION_STARTED,
 
     LAST_SIGNAL
 };
@@ -150,6 +155,8 @@
 typedef HashMap<String, RefPtr<WebKitURISchemeHandler> > URISchemeHandlerMap;
 typedef HashMap<uint64_t, GRefPtr<WebKitURISchemeRequest> > URISchemeRequestMap;
 
+class WebKitAutomationClient;
+
 struct _WebKitWebContextPrivate {
     RefPtr<WebProcessPool> processPool;
     bool clientsDetached;
@@ -180,11 +187,43 @@
     CString localStorageDirectory;
 #if ENABLE(REMOTE_INSPECTOR)
     std::unique_ptr<RemoteInspectorProtocolHandler> remoteInspectorProtocolHandler;
+    std::unique_ptr<WebKitAutomationClient> automationClient;
+    GRefPtr<WebKitAutomationSession> automationSession;
+    bool automationAllowed;
 #endif
 };
 
 static guint signals[LAST_SIGNAL] = { 0, };
 
+#if ENABLE(REMOTE_INSPECTOR)
+class WebKitAutomationClient final : Inspector::RemoteInspector::Client {
+public:
+    explicit WebKitAutomationClient(WebKitWebContext* context)
+        : m_webContext(context)
+    {
+        Inspector::RemoteInspector::singleton().setRemoteInspectorClient(this);
+    }
+
+    ~WebKitAutomationClient()
+    {
+        Inspector::RemoteInspector::singleton().setRemoteInspectorClient(nullptr);
+    }
+
+private:
+    bool remoteAutomationAllowed() const override { return m_webContext->priv->automationAllowed; }
+
+    void requestAutomationSession(const String& sessionIdentifier) override
+    {
+        ASSERT(!m_webContext->priv->automationSession);
+        m_webContext->priv->automationSession = adoptGRef(webkitAutomationSessionCreate(sessionIdentifier.utf8().data()));
+        m_webContext->priv->processPool->setAutomationSession(&webkitAutomationSessionGetSession(m_webContext->priv->automationSession.get()));
+        g_signal_emit(m_webContext, signals[AUTOMATION_STARTED], 0, m_webContext->priv->automationSession.get());
+    }
+
+    WebKitWebContext* m_webContext;
+};
+#endif // ENABLE(REMOTE_INSPECTOR)
+
 WEBKIT_DEFINE_TYPE(WebKitWebContext, webkit_web_context, G_TYPE_OBJECT)
 
 static const char* injectedBundleDirectory()
@@ -294,6 +333,7 @@
 #endif
 #if ENABLE(REMOTE_INSPECTOR)
     priv->remoteInspectorProtocolHandler = std::make_unique<RemoteInspectorProtocolHandler>(webContext);
+    priv->automationClient = std::make_unique<WebKitAutomationClient>(webContext);
 #endif
 }
 
@@ -423,6 +463,27 @@
             nullptr, nullptr,
             g_cclosure_marshal_VOID__VOID,
             G_TYPE_NONE, 0);
+
+    /**
+     * WebKitWebContext::automation-started:
+     * @context: the #WebKitWebContext
+     * @session: the #WebKitAutomationSession associated with this event
+     *
+     * This signal is emitted when a new automation request is made.
+     * Note that it will never be emitted if automation is not enabled in @context,
+     * see webkit_web_context_set_automation_allowed() for more details.
+     *
+     * Since: 2.18
+     */
+    signals[AUTOMATION_STARTED] =
+        g_signal_new("automation-started",
+            G_TYPE_FROM_CLASS(gObjectClass),
+            G_SIGNAL_RUN_LAST,
+            G_STRUCT_OFFSET(WebKitWebContextClass, automation_started),
+            nullptr, nullptr,
+            g_cclosure_marshal_VOID__OBJECT,
+            G_TYPE_NONE, 1,
+            WEBKIT_TYPE_AUTOMATION_SESSION);
 }
 
 static gpointer createDefaultWebContext(gpointer)
@@ -530,6 +591,49 @@
 }
 
 /**
+ * webkit_web_context_is_automation_allowed:
+ * @context: the #WebKitWebContext
+ *
+ * Get whether automation is allowed in @context.
+ * See also webkit_web_context_set_automation_allowed().
+ *
+ * Returns: %TRUE if automation is allowed or %FALSE otherwise.
+ *
+ * Since: 2.18
+ */
+gboolean webkit_web_context_is_automation_allowed(WebKitWebContext* context)
+{
+    g_return_val_if_fail(WEBKIT_IS_WEB_CONTEXT(context), FALSE);
+
+    return context->priv->automationAllowed;
+}
+
+/**
+ * webkit_web_context_set_automation_allowed:
+ * @context: the #WebKitWebContext
+ * @allowed: value to set
+ *
+ * Set whether automation is allowed in @context. When automation is enabled the browser could
+ * be controlled by another process by requesting an automation session. When a new automation
+ * session is requested the signal #WebKitWebContext::automation-started is emitted.
+ * Automation is disabled by default, so you need to explicitly call this method passing %TRUE
+ * to enable it.
+ *
+ * Since: 2.18
+ */
+void webkit_web_context_set_automation_allowed(WebKitWebContext* context, gboolean allowed)
+{
+    g_return_if_fail(WEBKIT_IS_WEB_CONTEXT(context));
+
+    if (context->priv->automationAllowed == allowed)
+        return;
+    context->priv->automationAllowed = allowed;
+#if ENABLE(REMOTE_INSPECTOR)
+    Inspector::RemoteInspector::singleton().clientCapabilitiesDidChange();
+#endif
+}
+
+/**
  * webkit_web_context_set_cache_model:
  * @context: the #WebKitWebContext
  * @cache_model: a #WebKitCacheModel
@@ -1501,6 +1605,7 @@
     pageConfiguration->setPreferences(webkitSettingsGetPreferences(webkit_web_view_get_settings(webView)));
     pageConfiguration->setRelatedPage(relatedView ? webkitWebViewBaseGetPage(WEBKIT_WEB_VIEW_BASE(relatedView)) : nullptr);
     pageConfiguration->setUserContentController(userContentManager ? webkitUserContentManagerGetUserContentControllerProxy(userContentManager) : nullptr);
+    pageConfiguration->setControlledByAutomation(webkit_web_view_is_controlled_by_automation(webView));
 
     WebKitWebsiteDataManager* manager = webkitWebViewGetWebsiteDataManager(webView);
     if (!manager)

Modified: trunk/Source/WebKit2/UIProcess/API/gtk/WebKitWebContext.h (216005 => 216006)


--- trunk/Source/WebKit2/UIProcess/API/gtk/WebKitWebContext.h	2017-05-01 07:02:07 UTC (rev 216005)
+++ trunk/Source/WebKit2/UIProcess/API/gtk/WebKitWebContext.h	2017-05-01 07:03:58 UTC (rev 216006)
@@ -25,6 +25,7 @@
 #define WebKitWebContext_h
 
 #include <glib-object.h>
+#include <webkit2/WebKitAutomationSession.h>
 #include <webkit2/WebKitCookieManager.h>
 #include <webkit2/WebKitDefines.h>
 #include <webkit2/WebKitDownload.h>
@@ -145,16 +146,17 @@
 struct _WebKitWebContextClass {
     GObjectClass parent;
 
-    void (* download_started)                    (WebKitWebContext *context,
-                                                  WebKitDownload   *download);
-    void (* initialize_web_extensions)           (WebKitWebContext *context);
-    void (* initialize_notification_permissions) (WebKitWebContext *context);
+    void (* download_started)                    (WebKitWebContext        *context,
+                                                  WebKitDownload          *download);
+    void (* initialize_web_extensions)           (WebKitWebContext        *context);
+    void (* initialize_notification_permissions) (WebKitWebContext        *context);
+    void (* automation_started)                  (WebKitWebContext        *context,
+                                                  WebKitAutomationSession *session);
 
     void (*_webkit_reserved0) (void);
     void (*_webkit_reserved1) (void);
     void (*_webkit_reserved2) (void);
     void (*_webkit_reserved3) (void);
-    void (*_webkit_reserved4) (void);
 };
 
 WEBKIT_API GType
@@ -178,7 +180,13 @@
 WEBKIT_API gboolean
 webkit_web_context_is_ephemeral                     (WebKitWebContext              *context);
 
+WEBKIT_API gboolean
+webkit_web_context_is_automation_allowed            (WebKitWebContext              *context);
+
 WEBKIT_API void
+webkit_web_context_set_automation_allowed           (WebKitWebContext              *context,
+                                                     gboolean                       allowed);
+WEBKIT_API void
 webkit_web_context_set_cache_model                  (WebKitWebContext              *context,
                                                      WebKitCacheModel               cache_model);
 WEBKIT_API WebKitCacheModel

Modified: trunk/Source/WebKit2/UIProcess/API/gtk/WebKitWebView.cpp (216005 => 216006)


--- trunk/Source/WebKit2/UIProcess/API/gtk/WebKitWebView.cpp	2017-05-01 07:02:07 UTC (rev 216005)
+++ trunk/Source/WebKit2/UIProcess/API/gtk/WebKitWebView.cpp	2017-05-01 07:03:58 UTC (rev 216006)
@@ -155,6 +155,7 @@
     PROP_IS_LOADING,
     PROP_IS_PLAYING_AUDIO,
     PROP_IS_EPHEMERAL,
+    PROP_IS_CONTROLLED_BY_AUTOMATION,
     PROP_EDITABLE
 };
 
@@ -180,6 +181,7 @@
     CString activeURI;
     bool isLoading;
     bool isEphemeral;
+    bool isControlledByAutomation;
 
     std::unique_ptr<PageLoadStateObserver> loadObserver;
 
@@ -667,6 +669,7 @@
     if (priv->relatedView) {
         priv->context = webkit_web_view_get_context(priv->relatedView);
         priv->isEphemeral = webkit_web_view_is_ephemeral(priv->relatedView);
+        priv->isControlledByAutomation = webkit_web_view_is_controlled_by_automation(priv->relatedView);
     } else if (!priv->context)
         priv->context = webkit_web_context_get_default();
     else if (!priv->isEphemeral)
@@ -739,6 +742,9 @@
     case PROP_IS_EPHEMERAL:
         webView->priv->isEphemeral = g_value_get_boolean(value);
         break;
+    case PROP_IS_CONTROLLED_BY_AUTOMATION:
+        webView->priv->isControlledByAutomation = g_value_get_boolean(value);
+        break;
     case PROP_EDITABLE:
         webkit_web_view_set_editable(webView, g_value_get_boolean(value));
         break;
@@ -785,6 +791,9 @@
     case PROP_IS_EPHEMERAL:
         g_value_set_boolean(value, webkit_web_view_is_ephemeral(webView));
         break;
+    case PROP_IS_CONTROLLED_BY_AUTOMATION:
+        g_value_set_boolean(value, webkit_web_view_is_controlled_by_automation(webView));
+        break;
     case PROP_EDITABLE:
         g_value_set_boolean(value, webkit_web_view_is_editable(webView));
         break;
@@ -1048,6 +1057,25 @@
             static_cast<GParamFlags>(WEBKIT_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)));
 
     /**
+     * WebKitWebView:is-controlled-by-automation:
+     *
+     * Whether the #WebKitWebView is controlled by automation. This should only be used when
+     * creating a new #WebKitWebView as a response to #WebKitAutomationSession::create-web-view
+     * signal request.
+     *
+     * Since: 2.18
+     */
+    g_object_class_install_property(
+        gObjectClass,
+        PROP_IS_CONTROLLED_BY_AUTOMATION,
+        g_param_spec_boolean(
+            "is-controlled-by-automation",
+            "Is Controlled By Automation",
+            _("Whether the web view is crontrolled by automation"),
+            FALSE,
+            static_cast<GParamFlags>(WEBKIT_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)));
+
+    /**
      * WebKitWebView:editable:
      *
      * Whether the pages loaded inside #WebKitWebView are editable. For more
@@ -2317,6 +2345,25 @@
 }
 
 /**
+ * webkit_web_view_is_controlled_by_automation:
+ * @web_view: a #WebKitWebView
+ *
+ * Get whether a #WebKitWebView was created with #WebKitWebView:is-controlled-by-automation
+ * property enabled. Only #WebKitWebView<!-- -->s controlled by automation can be used in an
+ * automation session.
+ *
+ * Returns: %TRUE if @web_view is controlled by automation, or %FALSE otherwise.
+ *
+ * Since: 2.18
+ */
+gboolean webkit_web_view_is_controlled_by_automation(WebKitWebView* webView)
+{
+    g_return_val_if_fail(WEBKIT_IS_WEB_VIEW(webView), FALSE);
+
+    return webView->priv->isControlledByAutomation;
+}
+
+/**
  * webkit_web_view_get_website_data_manager:
  * @web_view: a #WebKitWebView
  *

Modified: trunk/Source/WebKit2/UIProcess/API/gtk/WebKitWebView.h (216005 => 216006)


--- trunk/Source/WebKit2/UIProcess/API/gtk/WebKitWebView.h	2017-05-01 07:02:07 UTC (rev 216005)
+++ trunk/Source/WebKit2/UIProcess/API/gtk/WebKitWebView.h	2017-05-01 07:03:58 UTC (rev 216006)
@@ -277,6 +277,9 @@
 WEBKIT_API gboolean
 webkit_web_view_is_ephemeral                         (WebKitWebView             *web_view);
 
+WEBKIT_API gboolean
+webkit_web_view_is_controlled_by_automation          (WebKitWebView             *web_view);
+
 WEBKIT_API WebKitWebsiteDataManager *
 webkit_web_view_get_website_data_manager             (WebKitWebView             *web_view);
 

Modified: trunk/Source/WebKit2/UIProcess/API/gtk/docs/webkit2gtk-4.0-sections.txt (216005 => 216006)


--- trunk/Source/WebKit2/UIProcess/API/gtk/docs/webkit2gtk-4.0-sections.txt	2017-05-01 07:02:07 UTC (rev 216005)
+++ trunk/Source/WebKit2/UIProcess/API/gtk/docs/webkit2gtk-4.0-sections.txt	2017-05-01 07:03:58 UTC (rev 216006)
@@ -33,6 +33,8 @@
 webkit_web_context_new_ephemeral
 webkit_web_context_new_with_website_data_manager
 webkit_web_context_is_ephemeral
+webkit_web_context_is_automation_allowed
+webkit_web_context_set_automation_allowed
 webkit_web_context_get_website_data_manager
 webkit_web_context_get_cache_model
 webkit_web_context_set_cache_model
@@ -161,6 +163,7 @@
 webkit_web_view_new_with_settings
 webkit_web_view_new_with_user_content_manager
 webkit_web_view_is_ephemeral
+webkit_web_view_is_controlled_by_automation
 webkit_web_view_get_context
 webkit_web_view_get_user_content_manager
 webkit_web_view_get_website_data_manager
@@ -1308,6 +1311,25 @@
 </SECTION>
 
 <SECTION>
+<FILE>WebKitAutomationSession</FILE>
+WebKitAutomationSession
+webkit_automation_session_get_id
+
+<SUBSECTION Private>
+WebKitAutomationSessionPrivate
+webkit_automation_session_get_type
+
+<SUBSECTION Standard>
+WebKitAutomationSessionClass
+WEBKIT_TYPE_AUTOMATION_SESSION
+WEBKIT_AUTOMATION_SESSION
+WEBKIT_IS_AUTOMATION_SESSION
+WEBKIT_AUTOMATION_SESSION_CLASS
+WEBKIT_IS_AUTOMATION_SESSION_CLASS
+WEBKIT_AUTOMATION_SESSION_GET_CLASS
+</SECTION>
+
+<SECTION>
 <FILE>WebKitWebExtension</FILE>
 WebKitWebExtension
 WebKitWebExtensionInitializeFunction

Modified: trunk/Source/WebKit2/UIProcess/API/gtk/docs/webkit2gtk-4.0.types (216005 => 216006)


--- trunk/Source/WebKit2/UIProcess/API/gtk/docs/webkit2gtk-4.0.types	2017-05-01 07:02:07 UTC (rev 216005)
+++ trunk/Source/WebKit2/UIProcess/API/gtk/docs/webkit2gtk-4.0.types	2017-05-01 07:03:58 UTC (rev 216006)
@@ -35,3 +35,4 @@
 webkit_console_message_get_type
 webkit_web_view_session_state_get_type
 webkit_print_custom_widget_get_type
+webkit_automation_session_get_type

Modified: trunk/Source/WebKit2/UIProcess/API/gtk/docs/webkit2gtk-docs.sgml (216005 => 216006)


--- trunk/Source/WebKit2/UIProcess/API/gtk/docs/webkit2gtk-docs.sgml	2017-05-01 07:02:07 UTC (rev 216005)
+++ trunk/Source/WebKit2/UIProcess/API/gtk/docs/webkit2gtk-docs.sgml	2017-05-01 07:03:58 UTC (rev 216006)
@@ -56,6 +56,7 @@
     <xi:include href=""
     <xi:include href=""
     <xi:include href=""
+    <xi:include href=""
   </chapter>
 
   <chapter>
@@ -118,5 +119,10 @@
     <xi:include href="" /></xi:include>
   </index>
 
+  <index id="api-index-2-18" role="2.18">
+    <title>Index of new symbols in 2.18</title>
+    <xi:include href="" /></xi:include>
+    </index>
+
   <xi:include href="" /></xi:include>
 </book>

Modified: trunk/Source/WebKit2/UIProcess/API/gtk/webkit2.h (216005 => 216006)


--- trunk/Source/WebKit2/UIProcess/API/gtk/webkit2.h	2017-05-01 07:02:07 UTC (rev 216005)
+++ trunk/Source/WebKit2/UIProcess/API/gtk/webkit2.h	2017-05-01 07:03:58 UTC (rev 216006)
@@ -28,6 +28,7 @@
 #define __WEBKIT2_H_INSIDE__
 
 #include <webkit2/WebKitAuthenticationRequest.h>
+#include <webkit2/WebKitAutomationSession.h>
 #include <webkit2/WebKitBackForwardList.h>
 #include <webkit2/WebKitBackForwardListItem.h>
 #include <webkit2/WebKitContextMenu.h>

Modified: trunk/Tools/ChangeLog (216005 => 216006)


--- trunk/Tools/ChangeLog	2017-05-01 07:02:07 UTC (rev 216005)
+++ trunk/Tools/ChangeLog	2017-05-01 07:03:58 UTC (rev 216006)
@@ -1,3 +1,32 @@
+2017-05-01  Carlos Garcia Campos  <[email protected]>
+
+        [GTK] Add automation session API
+        https://bugs.webkit.org/show_bug.cgi?id=171428
+
+        Reviewed by Carlos Alberto Lopez Perez.
+
+        Add an automation mode to MiniBrowser using the new API and add a unit test too.
+
+        * MiniBrowser/gtk/BrowserWindow.c:
+        (webViewTitleChanged):
+        (webViewDecidePolicy):
+        (newTabCallback):
+        (openPrivateWindow):
+        (browserWindowFinalize):
+        (browser_window_init):
+        (browser_window_get_or_create_web_view_for_automation):
+        * MiniBrowser/gtk/BrowserWindow.h:
+        * MiniBrowser/gtk/main.c:
+        (createBrowserTab):
+        (createWebViewForAutomationCallback):
+        (automationStartedCallback):
+        (main):
+        * TestWebKitAPI/Tests/WebKit2Gtk/CMakeLists.txt:
+        * TestWebKitAPI/Tests/WebKit2Gtk/TestAutomationSession.cpp: Added.
+        (testAutomationSessionRequestSession):
+        (beforeAll):
+        (afterAll):
+
 2017-04-30  Dan Bernstein  <[email protected]>
 
         [Cocoa] Have check-webkit-style advise against use of [get…Class() alloc]

Modified: trunk/Tools/MiniBrowser/gtk/BrowserWindow.c (216005 => 216006)


--- trunk/Tools/MiniBrowser/gtk/BrowserWindow.c	2017-05-01 07:02:07 UTC (rev 216005)
+++ trunk/Tools/MiniBrowser/gtk/BrowserWindow.c	2017-05-01 07:03:58 UTC (rev 216006)
@@ -76,7 +76,7 @@
 static const gdouble maximumZoomLevel = 3;
 static const gdouble defaultZoomLevel = 1;
 static const gdouble zoomStep = 1.2;
-static gint windowCount = 0;
+static GList *windowList;
 
 G_DEFINE_TYPE(BrowserWindow, browser_window, GTK_TYPE_WINDOW)
 
@@ -155,7 +155,9 @@
     if (!title)
         title = defaultWindowTitle;
     char *privateTitle = NULL;
-    if (webkit_web_view_is_ephemeral(webView))
+    if (webkit_web_view_is_controlled_by_automation(webView))
+        privateTitle = g_strdup_printf("[Automation] %s", title);
+    else if (webkit_web_view_is_ephemeral(webView))
         privateTitle = g_strdup_printf("[Private] %s", title);
     gtk_window_set_title(GTK_WINDOW(window), privateTitle ? privateTitle : title);
     g_free(privateTitle);
@@ -355,6 +357,7 @@
         "web-context", webkit_web_view_get_context(webView),
         "settings", webkit_web_view_get_settings(webView),
         "user-content-manager", webkit_web_view_get_user_content_manager(webView),
+        "is-controlled-by-automation", webkit_web_view_is_controlled_by_automation(webView),
         NULL));
     browser_window_append_view(window, newWebView);
     webkit_web_view_load_request(newWebView, webkit_navigation_action_get_request(navigationAction));
@@ -496,6 +499,7 @@
         "web-context", webkit_web_view_get_context(webView),
         "settings", webkit_web_view_get_settings(webView),
         "user-content-manager", webkit_web_view_get_user_content_manager(webView),
+        "is-controlled-by-automation", webkit_web_view_is_controlled_by_automation(webView),
         NULL)));
     gtk_notebook_set_current_page(GTK_NOTEBOOK(window->notebook), -1);
 }
@@ -513,6 +517,7 @@
         "settings", webkit_web_view_get_settings(webView),
         "user-content-manager", webkit_web_view_get_user_content_manager(webView),
         "is-ephemeral", TRUE,
+        "is-controlled-by-automation", webkit_web_view_is_controlled_by_automation(webView),
         NULL));
     GtkWidget *newWindow = browser_window_new(GTK_WINDOW(window), window->webContext);
     browser_window_append_view(BROWSER_WINDOW(newWindow), newWebView);
@@ -662,9 +667,11 @@
 
     g_free(window->sessionFile);
 
+    windowList = g_list_remove(windowList, window);
+
     G_OBJECT_CLASS(browser_window_parent_class)->finalize(gObject);
 
-    if (g_atomic_int_dec_and_test(&windowCount))
+    if (!windowList)
         gtk_main_quit();
 }
 
@@ -862,7 +869,7 @@
 
 static void browser_window_init(BrowserWindow *window)
 {
-    g_atomic_int_inc(&windowCount);
+    windowList = g_list_append(windowList, window);
 
     gtk_window_set_title(GTK_WINDOW(window), defaultWindowTitle);
     gtk_window_set_default_size(GTK_WINDOW(window), 800, 600);
@@ -1157,3 +1164,23 @@
 
     webkit_web_view_set_background_color(webView, rgba);
 }
+
+WebKitWebView *browser_window_get_or_create_web_view_for_automation(void)
+{
+    if (!windowList)
+        return NULL;
+
+    BrowserWindow *window = (BrowserWindow *)windowList->data;
+    WebKitWebView *webView = browser_tab_get_web_view(window->activeTab);
+    if (gtk_notebook_get_n_pages(GTK_NOTEBOOK(window->notebook)) == 1 && !webkit_web_view_get_uri(webView));
+        return webView;
+
+    WebKitWebView *newWebView = WEBKIT_WEB_VIEW(g_object_new(WEBKIT_TYPE_WEB_VIEW,
+        "web-context", webkit_web_view_get_context(webView),
+        "settings", webkit_web_view_get_settings(webView),
+        "user-content-manager", webkit_web_view_get_user_content_manager(webView),
+        "is-controlled-by-automation", TRUE,
+        NULL));
+    browser_window_append_view(window, newWebView);
+    return newWebView;
+}

Modified: trunk/Tools/MiniBrowser/gtk/BrowserWindow.h (216005 => 216006)


--- trunk/Tools/MiniBrowser/gtk/BrowserWindow.h	2017-05-01 07:02:07 UTC (rev 216005)
+++ trunk/Tools/MiniBrowser/gtk/BrowserWindow.h	2017-05-01 07:03:58 UTC (rev 216006)
@@ -51,6 +51,7 @@
 void browser_window_load_uri(BrowserWindow*, const char *uri);
 void browser_window_load_session(BrowserWindow *, const char *sessionFile);
 void browser_window_set_background_color(BrowserWindow*, GdkRGBA*);
+WebKitWebView* browser_window_get_or_create_web_view_for_automation(void);
 
 G_END_DECLS
 

Modified: trunk/Tools/MiniBrowser/gtk/main.c (216005 => 216006)


--- trunk/Tools/MiniBrowser/gtk/main.c	2017-05-01 07:02:07 UTC (rev 216005)
+++ trunk/Tools/MiniBrowser/gtk/main.c	2017-05-01 07:03:58 UTC (rev 216006)
@@ -42,6 +42,7 @@
 static const char *sessionFile;
 static char *geometry;
 static gboolean privateMode;
+static gboolean automationMode;
 
 typedef enum {
     MINI_BROWSER_ERROR_INVALID_ABOUT_PATH
@@ -67,6 +68,7 @@
         "web-context", browser_window_get_web_context(window),
         "settings", webkitSettings,
         "user-content-manager", userContentManager,
+        "is-controlled-by-automation", automationMode,
         NULL));
 
     if (editorMode)
@@ -95,6 +97,7 @@
     { "session-file", 's', 0, G_OPTION_ARG_FILENAME, &sessionFile, "Session file", "FILE" },
     { "geometry", 'g', 0, G_OPTION_ARG_STRING, &geometry, "Set the size and position of the window (WIDTHxHEIGHT+X+Y)", "GEOMETRY" },
     { "private", 'p', 0, G_OPTION_ARG_NONE, &privateMode, "Run in private browsing mode", NULL },
+    { "automation", 0, 0, G_OPTION_ARG_NONE, &automationMode, "Run in automation mode", NULL },
     { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &uriArguments, 0, "[URL…]" },
     { 0, 0, 0, 0, 0, 0, 0 }
 };
@@ -448,6 +451,16 @@
     }
 }
 
+static GtkWidget *createWebViewForAutomationCallback(WebKitAutomationSession* session)
+{
+    return GTK_WIDGET(browser_window_get_or_create_web_view_for_automation());
+}
+
+static void automationStartedCallback(WebKitWebContext *webContext, WebKitAutomationSession *session)
+{
+    g_signal_connect(session, "create-web-view", G_CALLBACK(createWebViewForAutomationCallback), NULL);
+}
+
 int main(int argc, char *argv[])
 {
     gtk_init(&argc, &argv);
@@ -476,7 +489,7 @@
     }
     g_option_context_free (context);
 
-    WebKitWebContext *webContext = privateMode ? webkit_web_context_new_ephemeral() : webkit_web_context_get_default();
+    WebKitWebContext *webContext = (privateMode || automationMode) ? webkit_web_context_new_ephemeral() : webkit_web_context_get_default();
 
     const gchar *singleprocess = g_getenv("MINIBROWSER_SINGLEPROCESS");
     webkit_web_context_set_process_model(webContext, (singleprocess && *singleprocess) ?
@@ -491,6 +504,9 @@
     webkit_user_content_manager_register_script_message_handler(userContentManager, "aboutData");
     g_signal_connect(userContentManager, "script-message-received::aboutData", G_CALLBACK(aboutDataScriptMessageReceivedCallback), webContext);
 
+    webkit_web_context_set_automation_allowed(webContext, automationMode);
+    g_signal_connect(webContext, "automation-started", G_CALLBACK(automationStartedCallback), NULL);
+
     BrowserWindow *mainWindow = BROWSER_WINDOW(browser_window_new(NULL, webContext));
     if (geometry)
         gtk_window_parse_geometry(GTK_WINDOW(mainWindow), geometry);
@@ -517,7 +533,7 @@
         if (!editorMode) {
             if (sessionFile)
                 browser_window_load_session(mainWindow, sessionFile);
-            else
+            else if (!automationMode)
                 webkit_web_view_load_uri(webView, BROWSER_DEFAULT_URL);
         }
     }

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKit2Gtk/CMakeLists.txt (216005 => 216006)


--- trunk/Tools/TestWebKitAPI/Tests/WebKit2Gtk/CMakeLists.txt	2017-05-01 07:02:07 UTC (rev 216005)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKit2Gtk/CMakeLists.txt	2017-05-01 07:03:58 UTC (rev 216006)
@@ -97,6 +97,7 @@
 
 ADD_WK2_TEST(InspectorTestServer InspectorTestServer.cpp)
 ADD_WK2_TEST(TestAuthentication TestAuthentication.cpp)
+ADD_WK2_TEST(TestAutomationSession TestAutomationSession.cpp)
 ADD_WK2_TEST(TestAutocleanups TestAutocleanups.cpp)
 ADD_WK2_TEST(TestBackForwardList TestBackForwardList.cpp)
 ADD_WK2_TEST(TestContextMenu TestContextMenu.cpp)

Added: trunk/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestAutomationSession.cpp (0 => 216006)


--- trunk/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestAutomationSession.cpp	                        (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestAutomationSession.cpp	2017-05-01 07:03:58 UTC (rev 216006)
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2017 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2,1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include "TestMain.h"
+#include <gio/gio.h>
+#include <wtf/UUID.h>
+#include <wtf/text/StringBuilder.h>
+
+class AutomationTest: public Test {
+public:
+    MAKE_GLIB_TEST_FIXTURE(AutomationTest);
+
+    AutomationTest()
+        : m_mainLoop(adoptGRef(g_main_loop_new(nullptr, TRUE)))
+    {
+        g_dbus_connection_new_for_address("tcp:host=127.0.0.1,port=2229",
+            G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, nullptr, nullptr, [](GObject*, GAsyncResult* result, gpointer userData) {
+                GRefPtr<GDBusConnection> connection = adoptGRef(g_dbus_connection_new_for_address_finish(result, nullptr));
+                static_cast<AutomationTest*>(userData)->setConnection(WTFMove(connection));
+            }, this);
+        g_main_loop_run(m_mainLoop.get());
+    }
+
+    ~AutomationTest()
+    {
+    }
+
+    struct Target {
+        Target() = default;
+        Target(guint64 id, CString name, bool isPaired)
+            : id(id)
+            , name(name)
+            , isPaired(isPaired)
+        {
+        }
+
+        guint64 id { 0 };
+        CString name;
+        bool isPaired { false };
+    };
+
+    const GDBusInterfaceVTable s_interfaceVTable = {
+        // method_call
+        [](GDBusConnection* connection, const gchar* sender, const gchar* objectPath, const gchar* interfaceName, const gchar* methodName, GVariant* parameters, GDBusMethodInvocation* invocation, gpointer userData) {
+            auto* test = static_cast<AutomationTest*>(userData);
+            if (!g_strcmp0(methodName, "SetTargetList")) {
+                guint64 connectionID;
+                GUniqueOutPtr<GVariantIter> iter;
+                g_variant_get(parameters, "(ta(tsssb))", &connectionID, &iter.outPtr());
+                guint64 targetID;
+                const char* type;
+                const char* name;
+                const char* dummy;
+                gboolean isPaired;
+                while (g_variant_iter_loop(iter.get(), "(t&s&s&sb)", &targetID, &type, &name, &dummy, &isPaired)) {
+                    if (!g_strcmp0(type, "Automation")) {
+                        test->setTarget(connectionID, Target(targetID, name, isPaired));
+                        break;
+                    }
+                }
+                g_dbus_method_invocation_return_value(invocation, nullptr);
+            } else if (!g_strcmp0(methodName, "SendMessageToFrontend")) {
+                guint64 connectionID, targetID;
+                const char* message;
+                g_variant_get(parameters, "(tt&s)", &connectionID, &targetID, &message);
+                test->receivedMessage(connectionID, targetID, message);
+                g_dbus_method_invocation_return_value(invocation, nullptr);
+            }
+        },
+        // get_property
+        nullptr,
+        // set_property
+        nullptr,
+    };
+
+    void registerDBusObject()
+    {
+        static const char introspectionXML[] =
+            "<node>"
+            "  <interface name='org.webkitgtk.RemoteInspectorClient'>"
+            "    <method name='SetTargetList'>"
+            "      <arg type='t' name='connectionID' direction='in'/>"
+            "      <arg type='a(tsssb)' name='list' direction='in'/>"
+            "    </method>"
+            "    <method name='SendMessageToFrontend'>"
+            "      <arg type='t' name='connectionID' direction='in'/>"
+            "      <arg type='t' name='target' direction='in'/>"
+            "      <arg type='s' name='message' direction='in'/>"
+            "    </method>"
+            "  </interface>"
+            "</node>";
+        static GDBusNodeInfo* introspectionData = nullptr;
+        if (!introspectionData)
+            introspectionData = g_dbus_node_info_new_for_xml(introspectionXML, nullptr);
+        g_dbus_connection_register_object(m_connection.get(), "/org/webkitgtk/RemoteInspectorClient", introspectionData->interfaces[0], &s_interfaceVTable, this, nullptr, nullptr);
+    }
+
+    void setConnection(GRefPtr<GDBusConnection>&& connection)
+    {
+        g_assert(G_IS_DBUS_CONNECTION(connection.get()));
+        m_connection = WTFMove(connection);
+        registerDBusObject();
+        g_main_loop_quit(m_mainLoop.get());
+    }
+
+    void setTarget(guint64 connectionID, Target&& target)
+    {
+        bool newConnection = !m_connectionID;
+        bool wasPaired = m_target.isPaired;
+        m_connectionID = connectionID;
+        m_target = WTFMove(target);
+        if (newConnection || (!wasPaired && m_target.isPaired))
+            g_main_loop_quit(m_mainLoop.get());
+    }
+
+    void receivedMessage(guint64 connectionID, guint64 targetID, const char* message)
+    {
+        g_assert(connectionID == m_connectionID);
+        g_assert(targetID == m_target.id);
+        m_message = message;
+        g_main_loop_quit(m_mainLoop.get());
+    }
+
+    void sendCommandToBackend(const String& command, const String& parameters = String())
+    {
+        static long sequenceID = 0;
+        StringBuilder messageBuilder;
+        messageBuilder.appendLiteral("{\"id\":");
+        messageBuilder.appendNumber(++sequenceID);
+        messageBuilder.appendLiteral(",\"method\":\"Automation.");
+        messageBuilder.append(command);
+        messageBuilder.append('"');
+        if (!parameters.isNull()) {
+            messageBuilder.appendLiteral(",\"params\":");
+            messageBuilder.append(parameters);
+        }
+        messageBuilder.append('}');
+        g_dbus_connection_call(m_connection.get(), nullptr, "/org/webkitgtk/Inspector", "org.webkitgtk.Inspector",
+            "SendMessageToBackend", g_variant_new("(tts)", m_connectionID, m_target.id, messageBuilder.toString().utf8().data()),
+            nullptr, G_DBUS_CALL_FLAGS_NO_AUTO_START, -1, nullptr, nullptr, nullptr);
+    }
+
+    static WebKitWebView* createWebViewCallback(WebKitAutomationSession* session, AutomationTest* test)
+    {
+        test->m_createWebViewWasCalled = true;
+        return test->m_webViewForAutomation;
+    }
+
+    void automationStarted(WebKitAutomationSession* session)
+    {
+        m_session = session;
+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(m_session));
+    }
+
+    static void automationStartedCallback(WebKitWebContext* webContext, WebKitAutomationSession* session, AutomationTest* test)
+    {
+        g_assert(webContext == test->m_webContext.get());
+        g_assert(WEBKIT_IS_AUTOMATION_SESSION(session));
+        test->automationStarted(session);
+    }
+
+    WebKitAutomationSession* requestSession(const char* sessionID)
+    {
+        auto signalID = g_signal_connect(m_webContext.get(), "automation-started", G_CALLBACK(automationStartedCallback), this);
+        g_dbus_connection_call(m_connection.get(), nullptr, "/org/webkitgtk/Inspector", "org.webkitgtk.Inspector",
+            "StartAutomationSession", g_variant_new("(s)", sessionID), nullptr, G_DBUS_CALL_FLAGS_NO_AUTO_START, -1, nullptr, nullptr, nullptr);
+        auto timeoutID = g_timeout_add(1000, [](gpointer userData) -> gboolean {
+            g_main_loop_quit(static_cast<GMainLoop*>(userData));
+            return G_SOURCE_REMOVE;
+        }, m_mainLoop.get());
+        g_main_loop_run(m_mainLoop.get());
+        if (!m_connectionID)
+            m_session = nullptr;
+        if (m_session && m_connectionID)
+            g_source_remove(timeoutID);
+        g_signal_handler_disconnect(m_webContext.get(), signalID);
+        return m_session;
+    }
+
+    void setupIfNeeded()
+    {
+        if (m_target.isPaired)
+            return;
+        g_assert(m_target.id);
+        g_dbus_connection_call(m_connection.get(), nullptr, "/org/webkitgtk/Inspector", "org.webkitgtk.Inspector",
+            "Setup", g_variant_new("(tt)", m_connectionID, m_target.id), nullptr, G_DBUS_CALL_FLAGS_NO_AUTO_START, -1, nullptr, nullptr, nullptr);
+        g_main_loop_run(m_mainLoop.get());
+        g_assert(m_target.isPaired);
+    }
+
+    bool createTopLevelBrowsingContext(WebKitWebView* webView)
+    {
+        setupIfNeeded();
+        m_webViewForAutomation = webView;
+        m_createWebViewWasCalled = false;
+        m_message = CString();
+        auto signalID = g_signal_connect(m_session, "create-web-view", G_CALLBACK(createWebViewCallback), this);
+        sendCommandToBackend("createBrowsingContext");
+        g_main_loop_run(m_mainLoop.get());
+        g_signal_handler_disconnect(m_session, signalID);
+        g_assert(m_createWebViewWasCalled);
+        g_assert(!m_message.isNull());
+        m_webViewForAutomation = nullptr;
+
+        if (strstr(m_message.data(), "The remote session failed to create a new browsing context"))
+            return false;
+        if (strstr(m_message.data(), "handle"))
+            return true;
+        return false;
+    }
+
+    GRefPtr<GMainLoop> m_mainLoop;
+    GRefPtr<GDBusConnection> m_connection;
+    WebKitAutomationSession* m_session;
+    guint64 m_connectionID { 0 };
+    Target m_target;
+
+    WebKitWebView* m_webViewForAutomation { nullptr };
+    bool m_createWebViewWasCalled { false };
+    CString m_message;
+};
+
+static void testAutomationSessionRequestSession(AutomationTest* test, gconstpointer)
+{
+    String sessionID = createCanonicalUUIDString();
+    // WebKitAutomationSession::automation-started is never emitted if automation is not enabled.
+    g_assert(!webkit_web_context_is_automation_allowed(test->m_webContext.get()));
+    auto* session = test->requestSession(sessionID.utf8().data());
+    g_assert(!session);
+
+    webkit_web_context_set_automation_allowed(test->m_webContext.get(), TRUE);
+    g_assert(webkit_web_context_is_automation_allowed(test->m_webContext.get()));
+    session = test->requestSession(sessionID.utf8().data());
+    g_assert_cmpstr(webkit_automation_session_get_id(session), ==, sessionID.utf8().data());
+    g_assert_cmpuint(test->m_target.id, >, 0);
+    ASSERT_CMP_CSTRING(test->m_target.name, ==, sessionID.utf8());
+    g_assert(!test->m_target.isPaired);
+
+    // Will fail to create a browsing context when not creating a web view (or not handling the signal).
+    g_assert(!test->createTopLevelBrowsingContext(nullptr));
+
+    // Will also fail if the web view is not controlled by automation.
+    GRefPtr<WebKitWebView> webView = WEBKIT_WEB_VIEW(webkit_web_view_new_with_context(test->m_webContext.get()));
+    g_assert(!webkit_web_view_is_controlled_by_automation(webView.get()));
+    g_assert(!test->createTopLevelBrowsingContext(webView.get()));
+
+    // And will work with a proper web view.
+    webView = WEBKIT_WEB_VIEW(g_object_new(WEBKIT_TYPE_WEB_VIEW, "web-context", test->m_webContext.get(), "is-controlled-by-automation", TRUE, nullptr));
+    g_assert(webkit_web_view_is_controlled_by_automation(webView.get()));
+    g_assert(test->createTopLevelBrowsingContext(webView.get()));
+
+    webkit_web_context_set_automation_allowed(test->m_webContext.get(), FALSE);
+}
+
+void beforeAll()
+{
+    g_setenv("WEBKIT_INSPECTOR_SERVER", "127.0.0.1:2229", TRUE);
+
+    AutomationTest::add("WebKitAutomationSession", "request-session", testAutomationSessionRequestSession);
+}
+
+void afterAll()
+{
+    g_unsetenv("WEBKIT_INSPECTOR_SERVER");
+}
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to