WebDriver: implement maximize, minimize and fullscreen window commands
[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     void requestMaximizeWindowOfPage(WebAutomationSession&, WebPageProxy& page, CompletionHandler<void()>&& completionHandler) override
101     {
102         if (auto* webView = webkitWebContextGetWebViewForPage(m_session->priv->webContext, &page))
103             webkitWebViewMaximizeWindow(webView, WTFMove(completionHandler));
104         else
105             completionHandler();
106     }
107
108     void requestHideWindowOfPage(WebAutomationSession&, WebPageProxy& page, CompletionHandler<void()>&& completionHandler) override
109     {
110         if (auto* webView = webkitWebContextGetWebViewForPage(m_session->priv->webContext, &page))
111             webkitWebViewMinimizeWindow(webView, WTFMove(completionHandler));
112         else
113             completionHandler();
114     }
115
116     void requestRestoreWindowOfPage(WebAutomationSession&, WebPageProxy& page, CompletionHandler<void()>&& completionHandler) override
117     {
118         if (auto* webView = webkitWebContextGetWebViewForPage(m_session->priv->webContext, &page))
119             webkitWebViewRestoreWindow(webView, WTFMove(completionHandler));
120         else
121             completionHandler();
122     }
123
124     bool isShowingJavaScriptDialogOnPage(WebAutomationSession&, WebPageProxy& page) override
125     {
126         auto* webView = webkitWebContextGetWebViewForPage(m_session->priv->webContext, &page);
127         if (!webView)
128             return false;
129         return webkitWebViewIsShowingScriptDialog(webView);
130     }
131
132     void dismissCurrentJavaScriptDialogOnPage(WebAutomationSession&, WebPageProxy& page) override
133     {
134         auto* webView = webkitWebContextGetWebViewForPage(m_session->priv->webContext, &page);
135         if (!webView)
136             return;
137         webkitWebViewDismissCurrentScriptDialog(webView);
138     }
139
140     void acceptCurrentJavaScriptDialogOnPage(WebAutomationSession&, WebPageProxy& page) override
141     {
142         auto* webView = webkitWebContextGetWebViewForPage(m_session->priv->webContext, &page);
143         if (!webView)
144             return;
145         webkitWebViewAcceptCurrentScriptDialog(webView);
146     }
147
148     String messageOfCurrentJavaScriptDialogOnPage(WebAutomationSession&, WebPageProxy& page) override
149     {
150         auto* webView = webkitWebContextGetWebViewForPage(m_session->priv->webContext, &page);
151         if (!webView)
152             return { };
153         return webkitWebViewGetCurrentScriptDialogMessage(webView);
154     }
155
156     void setUserInputForCurrentJavaScriptPromptOnPage(WebAutomationSession&, WebPageProxy& page, const String& userInput) override
157     {
158         auto* webView = webkitWebContextGetWebViewForPage(m_session->priv->webContext, &page);
159         if (!webView)
160             return;
161         webkitWebViewSetCurrentScriptDialogUserInput(webView, userInput);
162     }
163
164     std::optional<API::AutomationSessionClient::JavaScriptDialogType> typeOfCurrentJavaScriptDialogOnPage(WebAutomationSession&, WebPageProxy& page) override
165     {
166         auto* webView = webkitWebContextGetWebViewForPage(m_session->priv->webContext, &page);
167         if (!webView)
168             return std::nullopt;
169         auto dialogType = webkitWebViewGetCurrentScriptDialogType(webView);
170         if (!dialogType)
171             return std::nullopt;
172         switch (dialogType.value()) {
173         case WEBKIT_SCRIPT_DIALOG_ALERT:
174             return API::AutomationSessionClient::JavaScriptDialogType::Alert;
175         case WEBKIT_SCRIPT_DIALOG_CONFIRM:
176             return API::AutomationSessionClient::JavaScriptDialogType::Confirm;
177         case WEBKIT_SCRIPT_DIALOG_PROMPT:
178             return API::AutomationSessionClient::JavaScriptDialogType::Prompt;
179         case WEBKIT_SCRIPT_DIALOG_BEFORE_UNLOAD_CONFIRM:
180             return API::AutomationSessionClient::JavaScriptDialogType::BeforeUnloadConfirm;
181         }
182
183         ASSERT_NOT_REACHED();
184         return std::nullopt;
185     }
186
187     WebKitAutomationSession* m_session;
188 };
189
190 static void webkitAutomationSessionGetProperty(GObject* object, guint propID, GValue* value, GParamSpec* paramSpec)
191 {
192     WebKitAutomationSession* session = WEBKIT_AUTOMATION_SESSION(object);
193
194     switch (propID) {
195     case PROP_ID:
196         g_value_set_string(value, session->priv->id.data());
197         break;
198     default:
199         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, paramSpec);
200     }
201 }
202
203 static void webkitAutomationSessionSetProperty(GObject* object, guint propID, const GValue* value, GParamSpec* paramSpec)
204 {
205     WebKitAutomationSession* session = WEBKIT_AUTOMATION_SESSION(object);
206
207     switch (propID) {
208     case PROP_ID:
209         session->priv->id = g_value_get_string(value);
210         break;
211     default:
212         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, paramSpec);
213     }
214 }
215
216 static void webkitAutomationSessionConstructed(GObject* object)
217 {
218     WebKitAutomationSession* session = WEBKIT_AUTOMATION_SESSION(object);
219
220     G_OBJECT_CLASS(webkit_automation_session_parent_class)->constructed(object);
221
222     session->priv->session = adoptRef(new WebAutomationSession());
223     session->priv->session->setSessionIdentifier(String::fromUTF8(session->priv->id.data()));
224     session->priv->session->setClient(std::make_unique<AutomationSessionClient>(session));
225 }
226
227 static void webkitAutomationSessionDispose(GObject* object)
228 {
229     WebKitAutomationSession* session = WEBKIT_AUTOMATION_SESSION(object);
230
231     session->priv->session->setClient(nullptr);
232
233     if (session->priv->applicationInfo) {
234         webkit_application_info_unref(session->priv->applicationInfo);
235         session->priv->applicationInfo = nullptr;
236     }
237
238     G_OBJECT_CLASS(webkit_automation_session_parent_class)->dispose(object);
239 }
240
241 static void webkit_automation_session_class_init(WebKitAutomationSessionClass* sessionClass)
242 {
243     GObjectClass* gObjectClass = G_OBJECT_CLASS(sessionClass);
244     gObjectClass->get_property = webkitAutomationSessionGetProperty;
245     gObjectClass->set_property = webkitAutomationSessionSetProperty;
246     gObjectClass->constructed = webkitAutomationSessionConstructed;
247     gObjectClass->dispose = webkitAutomationSessionDispose;
248
249     /**
250      * WebKitAutomationSession:id:
251      *
252      * The session unique identifier.
253      *
254      * Since: 2.18
255      */
256     g_object_class_install_property(
257         gObjectClass,
258         PROP_ID,
259         g_param_spec_string(
260             "id",
261             _("Identifier"),
262             _("The automation session identifier"),
263             nullptr,
264             static_cast<GParamFlags>(WEBKIT_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)));
265
266     /**
267      * WebKitAutomationSession::create-web-view:
268      * @session: a #WebKitAutomationSession
269      *
270      * This signal is emitted when the automation client requests a new
271      * browsing context to interact with it. The callback handler should
272      * return a #WebKitWebView created with #WebKitWebView:is-controlled-by-automation
273      * construct property enabled. The returned #WebKitWebView could be an existing
274      * web view or a new one created and added to a new tab or window.
275      *
276      * Returns: (transfer none): a #WebKitWebView widget.
277      *
278      * Since: 2.18
279      */
280     signals[CREATE_WEB_VIEW] = g_signal_new(
281         "create-web-view",
282         G_TYPE_FROM_CLASS(sessionClass),
283         G_SIGNAL_RUN_LAST,
284         0,
285         nullptr, nullptr,
286         g_cclosure_marshal_generic,
287         WEBKIT_TYPE_WEB_VIEW, 0,
288         G_TYPE_NONE);
289 }
290
291 WebKitAutomationSession* webkitAutomationSessionCreate(WebKitWebContext* webContext, const char* sessionID)
292 {
293     auto* session = WEBKIT_AUTOMATION_SESSION(g_object_new(WEBKIT_TYPE_AUTOMATION_SESSION, "id", sessionID, nullptr));
294     session->priv->webContext = webContext;
295     return session;
296 }
297
298 WebAutomationSession& webkitAutomationSessionGetSession(WebKitAutomationSession* session)
299 {
300     return *session->priv->session;
301 }
302
303 String webkitAutomationSessionGetBrowserName(WebKitAutomationSession* session)
304 {
305     if (session->priv->applicationInfo)
306         return String::fromUTF8(webkit_application_info_get_name(session->priv->applicationInfo));
307
308     return g_get_prgname();
309 }
310
311 String webkitAutomationSessionGetBrowserVersion(WebKitAutomationSession* session)
312 {
313     if (!session->priv->applicationInfo)
314         return { };
315
316     guint64 major, minor, micro;
317     webkit_application_info_get_version(session->priv->applicationInfo, &major, &minor, &micro);
318
319     if (!micro && !minor)
320         return String::number(major);
321
322     if (!micro)
323         return makeString(String::number(major), ".", String::number(minor));
324
325     return makeString(String::number(major), ".", String::number(minor), ".", String::number(micro));
326 }
327
328 /**
329  * webkit_automation_session_get_id:
330  * @session: a #WebKitAutomationSession
331  *
332  * Get the unique identifier of a #WebKitAutomationSession
333  *
334  * Returns: the unique identifier of @session
335  *
336  * Since: 2.18
337  */
338 const char* webkit_automation_session_get_id(WebKitAutomationSession* session)
339 {
340     g_return_val_if_fail(WEBKIT_IS_AUTOMATION_SESSION(session), nullptr);
341     return session->priv->id.data();
342 }
343
344 /**
345  * webkit_automation_session_set_application_info:
346  * @session: a #WebKitAutomationSession
347  * @info: a #WebKitApplicationInfo
348  *
349  * Set the application information to @session. This information will be used by the driver service
350  * to match the requested capabilities with the actual application information. If this information
351  * is not provided to the session when a new automation session is requested, the creation might fail
352  * if the client requested a specific browser name or version. This will not have any effect when called
353  * after the automation session has been fully created, so this must be called in the callback of
354  * #WebKitWebContext::automation-started signal.
355  *
356  * Since: 2.18
357  */
358 void webkit_automation_session_set_application_info(WebKitAutomationSession* session, WebKitApplicationInfo* info)
359 {
360     g_return_if_fail(WEBKIT_IS_AUTOMATION_SESSION(session));
361     g_return_if_fail(info);
362
363     if (session->priv->applicationInfo == info)
364         return;
365
366     if (session->priv->applicationInfo)
367         webkit_application_info_unref(session->priv->applicationInfo);
368     session->priv->applicationInfo = webkit_application_info_ref(info);
369 }
370
371 /**
372  * webkit_automation_session_get_application_info:
373  * @session: a #WebKitAutomationSession
374  *
375  * Get the #WebKitAutomationSession previously set with webkit_automation_session_set_application_info().
376  *
377  * Returns: (transfer none): the #WebKitAutomationSession of @session, or %NULL if no one has been set.
378  *
379  * Since: 2.18
380  */
381 WebKitApplicationInfo* webkit_automation_session_get_application_info(WebKitAutomationSession* session)
382 {
383     g_return_val_if_fail(WEBKIT_IS_AUTOMATION_SESSION(session), nullptr);
384
385     return session->priv->applicationInfo;
386 }