a12249f61478d52e749aa3b0a92f475074ed5bb5
[WebKit-https.git] / Source / WebKit / UIProcess / API / glib / WebKitAutomationSession.cpp
1 /*
2  * Copyright (C) 2017 Igalia S.L.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public License
15  * along with this library; see the file COPYING.LIB.  If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 #include "config.h"
21 #include "WebKitAutomationSession.h"
22
23 #include "APIAutomationSessionClient.h"
24 #include "WebKitApplicationInfo.h"
25 #include "WebKitAutomationSessionPrivate.h"
26 #include "WebKitWebContextPrivate.h"
27 #include "WebKitWebViewPrivate.h"
28 #include <glib/gi18n-lib.h>
29 #include <wtf/glib/WTFGType.h>
30 #include <wtf/text/CString.h>
31
32 using namespace WebKit;
33
34 /**
35  * SECTION: WebKitAutomationSession
36  * @Short_description: Automation Session
37  * @Title: WebKitAutomationSession
38  *
39  * WebKitAutomationSession represents an automation session of a WebKitWebContext.
40  * When a new session is requested, a WebKitAutomationSession is created and the signal
41  * WebKitWebContext::automation-started is emitted with the WebKitAutomationSession as
42  * argument. Then, the automation client can request the session to create a new
43  * #WebKitWebView to interact with it. When this happens the signal #WebKitAutomationSession::create-web-view
44  * is emitted.
45  *
46  * Since: 2.18
47  */
48
49 enum {
50     PROP_0,
51
52     PROP_ID
53 };
54
55 enum {
56     CREATE_WEB_VIEW,
57
58     LAST_SIGNAL
59 };
60
61 struct _WebKitAutomationSessionPrivate {
62     RefPtr<WebAutomationSession> session;
63     WebKitApplicationInfo* applicationInfo;
64     WebKitWebContext* webContext;
65     CString id;
66 };
67
68 static guint signals[LAST_SIGNAL] = { 0, };
69
70 WEBKIT_DEFINE_TYPE(WebKitAutomationSession, webkit_automation_session, G_TYPE_OBJECT)
71
72 class AutomationSessionClient final : public API::AutomationSessionClient {
73 public:
74     explicit AutomationSessionClient(WebKitAutomationSession* session)
75         : m_session(session)
76     {
77     }
78
79 private:
80     String sessionIdentifier() const override
81     {
82         return String::fromUTF8(m_session->priv->id.data());
83     }
84
85     void didDisconnectFromRemote(WebAutomationSession&) override
86     {
87         webkitWebContextWillCloseAutomationSession(m_session->priv->webContext);
88     }
89
90     void requestNewPageWithOptions(WebAutomationSession&, API::AutomationSessionBrowsingContextOptions, CompletionHandler<void(WebPageProxy*)>&& completionHandler) override
91     {
92         WebKitWebView* webView = nullptr;
93         g_signal_emit(m_session, signals[CREATE_WEB_VIEW], 0, &webView);
94         if (!webView || !webkit_web_view_is_controlled_by_automation(webView))
95             completionHandler(nullptr);
96         else
97             completionHandler(&webkitWebViewGetPage(webView));
98     }
99
100     bool isShowingJavaScriptDialogOnPage(WebAutomationSession&, WebPageProxy& page) override
101     {
102         auto* webView = webkitWebContextGetWebViewForPage(m_session->priv->webContext, &page);
103         if (!webView)
104             return false;
105         return webkitWebViewIsShowingScriptDialog(webView);
106     }
107
108     void dismissCurrentJavaScriptDialogOnPage(WebAutomationSession&, WebPageProxy& page) override
109     {
110         auto* webView = webkitWebContextGetWebViewForPage(m_session->priv->webContext, &page);
111         if (!webView)
112             return;
113         webkitWebViewDismissCurrentScriptDialog(webView);
114     }
115
116     void acceptCurrentJavaScriptDialogOnPage(WebAutomationSession&, WebPageProxy& page) override
117     {
118         auto* webView = webkitWebContextGetWebViewForPage(m_session->priv->webContext, &page);
119         if (!webView)
120             return;
121         webkitWebViewAcceptCurrentScriptDialog(webView);
122     }
123
124     String messageOfCurrentJavaScriptDialogOnPage(WebAutomationSession&, WebPageProxy& page) override
125     {
126         auto* webView = webkitWebContextGetWebViewForPage(m_session->priv->webContext, &page);
127         if (!webView)
128             return { };
129         return webkitWebViewGetCurrentScriptDialogMessage(webView);
130     }
131
132     void setUserInputForCurrentJavaScriptPromptOnPage(WebAutomationSession&, WebPageProxy& page, const String& userInput) override
133     {
134         auto* webView = webkitWebContextGetWebViewForPage(m_session->priv->webContext, &page);
135         if (!webView)
136             return;
137         webkitWebViewSetCurrentScriptDialogUserInput(webView, userInput);
138     }
139
140     std::optional<API::AutomationSessionClient::JavaScriptDialogType> typeOfCurrentJavaScriptDialogOnPage(WebAutomationSession&, WebPageProxy& page) override
141     {
142         auto* webView = webkitWebContextGetWebViewForPage(m_session->priv->webContext, &page);
143         if (!webView)
144             return std::nullopt;
145         auto dialogType = webkitWebViewGetCurrentScriptDialogType(webView);
146         if (!dialogType)
147             return std::nullopt;
148         switch (dialogType.value()) {
149         case WEBKIT_SCRIPT_DIALOG_ALERT:
150             return API::AutomationSessionClient::JavaScriptDialogType::Alert;
151         case WEBKIT_SCRIPT_DIALOG_CONFIRM:
152             return API::AutomationSessionClient::JavaScriptDialogType::Confirm;
153         case WEBKIT_SCRIPT_DIALOG_PROMPT:
154             return API::AutomationSessionClient::JavaScriptDialogType::Prompt;
155         case WEBKIT_SCRIPT_DIALOG_BEFORE_UNLOAD_CONFIRM:
156             return API::AutomationSessionClient::JavaScriptDialogType::BeforeUnloadConfirm;
157         }
158
159         ASSERT_NOT_REACHED();
160         return std::nullopt;
161     }
162
163     WebKitAutomationSession* m_session;
164 };
165
166 static void webkitAutomationSessionGetProperty(GObject* object, guint propID, GValue* value, GParamSpec* paramSpec)
167 {
168     WebKitAutomationSession* session = WEBKIT_AUTOMATION_SESSION(object);
169
170     switch (propID) {
171     case PROP_ID:
172         g_value_set_string(value, session->priv->id.data());
173         break;
174     default:
175         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, paramSpec);
176     }
177 }
178
179 static void webkitAutomationSessionSetProperty(GObject* object, guint propID, const GValue* value, GParamSpec* paramSpec)
180 {
181     WebKitAutomationSession* session = WEBKIT_AUTOMATION_SESSION(object);
182
183     switch (propID) {
184     case PROP_ID:
185         session->priv->id = g_value_get_string(value);
186         break;
187     default:
188         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, paramSpec);
189     }
190 }
191
192 static void webkitAutomationSessionConstructed(GObject* object)
193 {
194     WebKitAutomationSession* session = WEBKIT_AUTOMATION_SESSION(object);
195
196     G_OBJECT_CLASS(webkit_automation_session_parent_class)->constructed(object);
197
198     session->priv->session = adoptRef(new WebAutomationSession());
199     session->priv->session->setSessionIdentifier(String::fromUTF8(session->priv->id.data()));
200     session->priv->session->setClient(std::make_unique<AutomationSessionClient>(session));
201 }
202
203 static void webkitAutomationSessionDispose(GObject* object)
204 {
205     WebKitAutomationSession* session = WEBKIT_AUTOMATION_SESSION(object);
206
207     session->priv->session->setClient(nullptr);
208
209     if (session->priv->applicationInfo) {
210         webkit_application_info_unref(session->priv->applicationInfo);
211         session->priv->applicationInfo = nullptr;
212     }
213
214     G_OBJECT_CLASS(webkit_automation_session_parent_class)->dispose(object);
215 }
216
217 static void webkit_automation_session_class_init(WebKitAutomationSessionClass* sessionClass)
218 {
219     GObjectClass* gObjectClass = G_OBJECT_CLASS(sessionClass);
220     gObjectClass->get_property = webkitAutomationSessionGetProperty;
221     gObjectClass->set_property = webkitAutomationSessionSetProperty;
222     gObjectClass->constructed = webkitAutomationSessionConstructed;
223     gObjectClass->dispose = webkitAutomationSessionDispose;
224
225     /**
226      * WebKitAutomationSession:id:
227      *
228      * The session unique identifier.
229      *
230      * Since: 2.18
231      */
232     g_object_class_install_property(
233         gObjectClass,
234         PROP_ID,
235         g_param_spec_string(
236             "id",
237             _("Identifier"),
238             _("The automation session identifier"),
239             nullptr,
240             static_cast<GParamFlags>(WEBKIT_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)));
241
242     /**
243      * WebKitAutomationSession::create-web-view:
244      * @session: a #WebKitAutomationSession
245      *
246      * This signal is emitted when the automation client requests a new
247      * browsing context to interact with it. The callback handler should
248      * return a #WebKitWebView created with #WebKitWebView:is-controlled-by-automation
249      * construct property enabled. The returned #WebKitWebView could be an existing
250      * web view or a new one created and added to a new tab or window.
251      *
252      * Returns: (transfer none): a #WebKitWebView widget.
253      *
254      * Since: 2.18
255      */
256     signals[CREATE_WEB_VIEW] = g_signal_new(
257         "create-web-view",
258         G_TYPE_FROM_CLASS(sessionClass),
259         G_SIGNAL_RUN_LAST,
260         0,
261         nullptr, nullptr,
262         g_cclosure_marshal_generic,
263         WEBKIT_TYPE_WEB_VIEW, 0,
264         G_TYPE_NONE);
265 }
266
267 WebKitAutomationSession* webkitAutomationSessionCreate(WebKitWebContext* webContext, const char* sessionID)
268 {
269     auto* session = WEBKIT_AUTOMATION_SESSION(g_object_new(WEBKIT_TYPE_AUTOMATION_SESSION, "id", sessionID, nullptr));
270     session->priv->webContext = webContext;
271     return session;
272 }
273
274 WebAutomationSession& webkitAutomationSessionGetSession(WebKitAutomationSession* session)
275 {
276     return *session->priv->session;
277 }
278
279 String webkitAutomationSessionGetBrowserName(WebKitAutomationSession* session)
280 {
281     if (session->priv->applicationInfo)
282         return String::fromUTF8(webkit_application_info_get_name(session->priv->applicationInfo));
283
284     return g_get_prgname();
285 }
286
287 String webkitAutomationSessionGetBrowserVersion(WebKitAutomationSession* session)
288 {
289     if (!session->priv->applicationInfo)
290         return { };
291
292     guint64 major, minor, micro;
293     webkit_application_info_get_version(session->priv->applicationInfo, &major, &minor, &micro);
294
295     if (!micro && !minor)
296         return String::number(major);
297
298     if (!micro)
299         return makeString(String::number(major), ".", String::number(minor));
300
301     return makeString(String::number(major), ".", String::number(minor), ".", String::number(micro));
302 }
303
304 /**
305  * webkit_automation_session_get_id:
306  * @session: a #WebKitAutomationSession
307  *
308  * Get the unique identifier of a #WebKitAutomationSession
309  *
310  * Returns: the unique identifier of @session
311  *
312  * Since: 2.18
313  */
314 const char* webkit_automation_session_get_id(WebKitAutomationSession* session)
315 {
316     g_return_val_if_fail(WEBKIT_IS_AUTOMATION_SESSION(session), nullptr);
317     return session->priv->id.data();
318 }
319
320 /**
321  * webkit_automation_session_set_application_info:
322  * @session: a #WebKitAutomationSession
323  * @info: a #WebKitApplicationInfo
324  *
325  * Set the application information to @session. This information will be used by the driver service
326  * to match the requested capabilities with the actual application information. If this information
327  * is not provided to the session when a new automation session is requested, the creation might fail
328  * if the client requested a specific browser name or version. This will not have any effect when called
329  * after the automation session has been fully created, so this must be called in the callback of
330  * #WebKitWebContext::automation-started signal.
331  *
332  * Since: 2.18
333  */
334 void webkit_automation_session_set_application_info(WebKitAutomationSession* session, WebKitApplicationInfo* info)
335 {
336     g_return_if_fail(WEBKIT_IS_AUTOMATION_SESSION(session));
337     g_return_if_fail(info);
338
339     if (session->priv->applicationInfo == info)
340         return;
341
342     if (session->priv->applicationInfo)
343         webkit_application_info_unref(session->priv->applicationInfo);
344     session->priv->applicationInfo = webkit_application_info_ref(info);
345 }
346
347 /**
348  * webkit_automation_session_get_application_info:
349  * @session: a #WebKitAutomationSession
350  *
351  * Get the #WebKitAutomationSession previously set with webkit_automation_session_set_application_info().
352  *
353  * Returns: (transfer none): the #WebKitAutomationSession of @session, or %NULL if no one has been set.
354  *
355  * Since: 2.18
356  */
357 WebKitApplicationInfo* webkit_automation_session_get_application_info(WebKitAutomationSession* session)
358 {
359     g_return_val_if_fail(WEBKIT_IS_AUTOMATION_SESSION(session), nullptr);
360
361     return session->priv->applicationInfo;
362 }