667cd81aff76df8034518757b1a2b1819f160a8b
[WebKit-https.git] / Source / WebKit / WebProcess / InjectedBundle / API / glib / WebKitWebPage.cpp
1 /*
2  * Copyright (C) 2012, 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 Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2,1 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 "WebKitWebPage.h"
22
23 #include "APIArray.h"
24 #include "APIDictionary.h"
25 #include "APIError.h"
26 #include "APINumber.h"
27 #include "APIString.h"
28 #include "APIURLRequest.h"
29 #include "APIURLResponse.h"
30 #include "ImageOptions.h"
31 #include "InjectedBundle.h"
32 #include "WebContextMenuItem.h"
33 #include "WebImage.h"
34 #include "WebKitConsoleMessagePrivate.h"
35 #include "WebKitContextMenuPrivate.h"
36 #include "WebKitDOMDocumentPrivate.h"
37 #include "WebKitDOMElementPrivate.h"
38 #include "WebKitDOMNodePrivate.h"
39 #include "WebKitFramePrivate.h"
40 #include "WebKitPrivate.h"
41 #include "WebKitScriptWorldPrivate.h"
42 #include "WebKitURIRequestPrivate.h"
43 #include "WebKitURIResponsePrivate.h"
44 #include "WebKitWebEditorPrivate.h"
45 #include "WebKitWebHitTestResultPrivate.h"
46 #include "WebKitWebPagePrivate.h"
47 #include "WebKitWebProcessEnumTypes.h"
48 #include "WebProcess.h"
49 #include <WebCore/Document.h>
50 #include <WebCore/DocumentLoader.h>
51 #include <WebCore/Frame.h>
52 #include <WebCore/FrameDestructionObserver.h>
53 #include <WebCore/FrameLoader.h>
54 #include <WebCore/FrameView.h>
55 #include <WebCore/HTMLFormElement.h>
56 #include <glib/gi18n-lib.h>
57 #include <wtf/NeverDestroyed.h>
58 #include <wtf/glib/WTFGType.h>
59 #include <wtf/text/CString.h>
60 #include <wtf/text/StringBuilder.h>
61
62 using namespace WebKit;
63 using namespace WebCore;
64
65 enum {
66     DOCUMENT_LOADED,
67     SEND_REQUEST,
68     CONTEXT_MENU,
69     CONSOLE_MESSAGE_SENT,
70     FORM_CONTROLS_ASSOCIATED,
71     WILL_SUBMIT_FORM,
72
73     LAST_SIGNAL
74 };
75
76 enum {
77     PROP_0,
78
79     PROP_URI
80 };
81
82 struct _WebKitWebPagePrivate {
83     WebPage* webPage;
84
85     CString uri;
86
87     GRefPtr<WebKitWebEditor> webEditor;
88 };
89
90 static guint signals[LAST_SIGNAL] = { 0, };
91
92 WEBKIT_DEFINE_TYPE(WebKitWebPage, webkit_web_page, G_TYPE_OBJECT)
93
94 static void webFrameDestroyed(WebFrame*);
95
96 class WebKitFrameWrapper final: public FrameDestructionObserver {
97 public:
98     WebKitFrameWrapper(WebFrame& webFrame)
99         : FrameDestructionObserver(webFrame.coreFrame())
100         , m_webkitFrame(adoptGRef(webkitFrameCreate(&webFrame)))
101     {
102     }
103
104     WebKitFrame* webkitFrame() const { return m_webkitFrame.get(); }
105
106 private:
107     void frameDestroyed() override
108     {
109         FrameDestructionObserver::frameDestroyed();
110         webFrameDestroyed(webkitFrameGetWebFrame(m_webkitFrame.get()));
111     }
112
113     GRefPtr<WebKitFrame> m_webkitFrame;
114 };
115
116 typedef HashMap<WebFrame*, std::unique_ptr<WebKitFrameWrapper>> WebFrameMap;
117
118 static WebFrameMap& webFrameMap()
119 {
120     static NeverDestroyed<WebFrameMap> map;
121     return map;
122 }
123
124 static WebKitFrame* webkitFrameGetOrCreate(WebFrame* webFrame)
125 {
126     auto wrapperPtr = webFrameMap().get(webFrame);
127     if (wrapperPtr)
128         return wrapperPtr->webkitFrame();
129
130     std::unique_ptr<WebKitFrameWrapper> wrapper = std::make_unique<WebKitFrameWrapper>(*webFrame);
131     wrapperPtr = wrapper.get();
132     webFrameMap().set(webFrame, WTFMove(wrapper));
133     return wrapperPtr->webkitFrame();
134 }
135
136 static void webFrameDestroyed(WebFrame* webFrame)
137 {
138     webFrameMap().remove(webFrame);
139 }
140
141 static void webkitWebPageSetURI(WebKitWebPage* webPage, const CString& uri)
142 {
143     if (webPage->priv->uri == uri)
144         return;
145
146     webPage->priv->uri = uri;
147     g_object_notify(G_OBJECT(webPage), "uri");
148 }
149
150 static void webkitWebPageDidSendConsoleMessage(WebKitWebPage* webPage, MessageSource source, MessageLevel level, const String& message, unsigned lineNumber, const String& sourceID)
151 {
152     WebKitConsoleMessage consoleMessage(source, level, message, lineNumber, sourceID);
153     g_signal_emit(webPage, signals[CONSOLE_MESSAGE_SENT], 0, &consoleMessage);
154 }
155
156 class PageLoaderClient final : public API::InjectedBundle::PageLoaderClient {
157 public:
158     explicit PageLoaderClient(WebKitWebPage* webPage)
159         : m_webPage(webPage)
160     {
161     }
162
163 private:
164     static CString getDocumentLoaderURL(DocumentLoader* documentLoader)
165     {
166         ASSERT(documentLoader);
167         if (!documentLoader->unreachableURL().isEmpty())
168             return documentLoader->unreachableURL().string().utf8();
169
170         return documentLoader->url().string().utf8();
171     }
172
173     void didStartProvisionalLoadForFrame(WebPage&, WebFrame& frame, RefPtr<API::Object>&) override
174     {
175         if (!frame.isMainFrame())
176             return;
177         webkitWebPageSetURI(m_webPage, getDocumentLoaderURL(frame.coreFrame()->loader().provisionalDocumentLoader()));
178     }
179
180     void didReceiveServerRedirectForProvisionalLoadForFrame(WebPage&, WebFrame& frame, RefPtr<API::Object>&) override
181     {
182         if (!frame.isMainFrame())
183             return;
184         webkitWebPageSetURI(m_webPage, getDocumentLoaderURL(frame.coreFrame()->loader().provisionalDocumentLoader()));
185     }
186
187     void didSameDocumentNavigationForFrame(WebPage&, WebFrame& frame, SameDocumentNavigationType, RefPtr<API::Object>&) override
188     {
189         if (!frame.isMainFrame())
190             return;
191         webkitWebPageSetURI(m_webPage, frame.coreFrame()->document()->url().string().utf8());
192     }
193
194     void didCommitLoadForFrame(WebPage&, WebFrame& frame, RefPtr<API::Object>&) override
195     {
196         if (!frame.isMainFrame())
197             return;
198         webkitWebPageSetURI(m_webPage, getDocumentLoaderURL(frame.coreFrame()->loader().documentLoader()));
199     }
200
201     void didFinishDocumentLoadForFrame(WebPage&, WebFrame& frame, RefPtr<API::Object>&) override
202     {
203         if (!frame.isMainFrame())
204             return;
205         g_signal_emit(m_webPage, signals[DOCUMENT_LOADED], 0);
206     }
207
208     void didClearWindowObjectForFrame(WebPage&, WebFrame& frame, DOMWrapperWorld& world) override
209     {
210         auto injectedWorld = InjectedBundleScriptWorld::getOrCreate(world);
211         if (auto* wkWorld = webkitScriptWorldGet(injectedWorld.ptr()))
212             webkitScriptWorldWindowObjectCleared(wkWorld, m_webPage, webkitFrameGetOrCreate(&frame));
213     }
214
215     WebKitWebPage* m_webPage;
216 };
217
218
219 class PageResourceLoadClient final : public API::InjectedBundle::ResourceLoadClient {
220 public:
221     explicit PageResourceLoadClient(WebKitWebPage* webPage)
222         : m_webPage(webPage)
223     {
224     }
225
226 private:
227     void didInitiateLoadForResource(WebPage& page, WebFrame& frame, uint64_t identifier, const ResourceRequest& request, bool) override
228     {
229         API::Dictionary::MapType message;
230         message.set(String::fromUTF8("Page"), &page);
231         message.set(String::fromUTF8("Frame"), &frame);
232         message.set(String::fromUTF8("Identifier"), API::UInt64::create(identifier));
233         message.set(String::fromUTF8("Request"), API::URLRequest::create(request));
234         WebProcess::singleton().injectedBundle()->postMessage(String::fromUTF8("WebPage.DidInitiateLoadForResource"), API::Dictionary::create(WTFMove(message)).ptr());
235     }
236
237     void willSendRequestForFrame(WebPage& page, WebFrame&, uint64_t identifier, ResourceRequest& resourceRequest, const ResourceResponse& redirectResourceResponse) override
238     {
239         GRefPtr<WebKitURIRequest> request = adoptGRef(webkitURIRequestCreateForResourceRequest(resourceRequest));
240         GRefPtr<WebKitURIResponse> redirectResponse = !redirectResourceResponse.isNull() ? adoptGRef(webkitURIResponseCreateForResourceResponse(redirectResourceResponse)) : nullptr;
241
242         gboolean returnValue;
243         g_signal_emit(m_webPage, signals[SEND_REQUEST], 0, request.get(), redirectResponse.get(), &returnValue);
244         if (returnValue) {
245             resourceRequest = { };
246             return;
247         }
248
249         webkitURIRequestGetResourceRequest(request.get(), resourceRequest);
250         resourceRequest.setInitiatingPageID(page.pageID());
251
252         API::Dictionary::MapType message;
253         message.set(String::fromUTF8("Page"), &page);
254         message.set(String::fromUTF8("Identifier"), API::UInt64::create(identifier));
255         message.set(String::fromUTF8("Request"), API::URLRequest::create(resourceRequest));
256         if (!redirectResourceResponse.isNull())
257             message.set(String::fromUTF8("RedirectResponse"), API::URLResponse::create(redirectResourceResponse));
258         WebProcess::singleton().injectedBundle()->postMessage(String::fromUTF8("WebPage.DidSendRequestForResource"), API::Dictionary::create(WTFMove(message)).ptr());
259     }
260
261     void didReceiveResponseForResource(WebPage& page, WebFrame&, uint64_t identifier, const ResourceResponse& response) override
262     {
263         API::Dictionary::MapType message;
264         message.set(String::fromUTF8("Page"), &page);
265         message.set(String::fromUTF8("Identifier"), API::UInt64::create(identifier));
266         message.set(String::fromUTF8("Response"), API::URLResponse::create(response));
267         WebProcess::singleton().injectedBundle()->postMessage(String::fromUTF8("WebPage.DidReceiveResponseForResource"), API::Dictionary::create(WTFMove(message)).ptr());
268
269         // Post on the console as well to be consistent with the inspector.
270         if (response.httpStatusCode() >= 400) {
271             StringBuilder errorMessage;
272             errorMessage.appendLiteral("Failed to load resource: the server responded with a status of ");
273             errorMessage.appendNumber(response.httpStatusCode());
274             errorMessage.appendLiteral(" (");
275             errorMessage.append(response.httpStatusText());
276             errorMessage.append(')');
277             webkitWebPageDidSendConsoleMessage(m_webPage, MessageSource::Network, MessageLevel::Error, errorMessage.toString(), 0, response.url().string());
278         }
279     }
280
281     void didReceiveContentLengthForResource(WebPage& page, WebFrame&, uint64_t identifier, uint64_t contentLength) override
282     {
283         API::Dictionary::MapType message;
284         message.set(String::fromUTF8("Page"), &page);
285         message.set(String::fromUTF8("Identifier"), API::UInt64::create(identifier));
286         message.set(String::fromUTF8("ContentLength"), API::UInt64::create(contentLength));
287         WebProcess::singleton().injectedBundle()->postMessage(String::fromUTF8("WebPage.DidReceiveContentLengthForResource"), API::Dictionary::create(WTFMove(message)).ptr());
288     }
289
290     void didFinishLoadForResource(WebPage& page, WebFrame&, uint64_t identifier) override
291     {
292         API::Dictionary::MapType message;
293         message.set(String::fromUTF8("Page"), &page);
294         message.set(String::fromUTF8("Identifier"), API::UInt64::create(identifier));
295         WebProcess::singleton().injectedBundle()->postMessage(String::fromUTF8("WebPage.DidFinishLoadForResource"), API::Dictionary::create(WTFMove(message)).ptr());
296     }
297
298     void didFailLoadForResource(WebPage& page, WebFrame&, uint64_t identifier, const ResourceError& error) override
299     {
300         API::Dictionary::MapType message;
301         message.set(String::fromUTF8("Page"), &page);
302         message.set(String::fromUTF8("Identifier"), API::UInt64::create(identifier));
303         message.set(String::fromUTF8("Error"), API::Error::create(error));
304         WebProcess::singleton().injectedBundle()->postMessage(String::fromUTF8("WebPage.DidFailLoadForResource"), API::Dictionary::create(WTFMove(message)).ptr());
305
306         // Post on the console as well to be consistent with the inspector.
307         if (!error.isCancellation()) {
308             StringBuilder errorMessage;
309             errorMessage.appendLiteral("Failed to load resource");
310             if (!error.localizedDescription().isEmpty()) {
311                 errorMessage.appendLiteral(": ");
312                 errorMessage.append(error.localizedDescription());
313             }
314             webkitWebPageDidSendConsoleMessage(m_webPage, MessageSource::Network, MessageLevel::Error, errorMessage.toString(), 0, error.failingURL());
315         }
316     }
317
318     WebKitWebPage* m_webPage;
319 };
320
321 class PageContextMenuClient final : public API::InjectedBundle::PageContextMenuClient {
322 public:
323     explicit PageContextMenuClient(WebKitWebPage* webPage)
324         : m_webPage(webPage)
325     {
326     }
327
328 private:
329     bool getCustomMenuFromDefaultItems(WebPage&, const WebCore::HitTestResult& hitTestResult, const Vector<WebCore::ContextMenuItem>& defaultMenu, Vector<WebContextMenuItemData>& newMenu, RefPtr<API::Object>& userData) override
330     {
331         GRefPtr<WebKitContextMenu> contextMenu = adoptGRef(webkitContextMenuCreate(kitItems(defaultMenu)));
332         GRefPtr<WebKitWebHitTestResult> webHitTestResult = adoptGRef(webkitWebHitTestResultCreate(hitTestResult));
333         gboolean returnValue;
334         g_signal_emit(m_webPage, signals[CONTEXT_MENU], 0, contextMenu.get(), webHitTestResult.get(), &returnValue);
335         if (GVariant* variant = webkit_context_menu_get_user_data(contextMenu.get())) {
336             GUniquePtr<gchar> dataString(g_variant_print(variant, TRUE));
337             userData = API::String::create(String::fromUTF8(dataString.get()));
338         }
339
340         if (!returnValue)
341             return false;
342
343         webkitContextMenuPopulate(contextMenu.get(), newMenu);
344         return true;
345     }
346
347     WebKitWebPage* m_webPage;
348 };
349
350 class PageUIClient final : public API::InjectedBundle::PageUIClient {
351 public:
352     explicit PageUIClient(WebKitWebPage* webPage)
353         : m_webPage(webPage)
354     {
355     }
356
357 private:
358     void willAddMessageToConsole(WebPage*, MessageSource source, MessageLevel level, const String& message, unsigned lineNumber, unsigned /*columnNumber*/, const String& sourceID) override
359     {
360         webkitWebPageDidSendConsoleMessage(m_webPage, source, level, message, lineNumber, sourceID);
361     }
362
363     WebKitWebPage* m_webPage;
364 };
365
366 class PageFormClient final : public API::InjectedBundle::FormClient {
367 public:
368     explicit PageFormClient(WebKitWebPage* webPage)
369         : m_webPage(webPage)
370     {
371     }
372
373     void willSubmitForm(WebPage*, HTMLFormElement* formElement, WebFrame* frame, WebFrame* sourceFrame, const Vector<std::pair<String, String>>& values, RefPtr<API::Object>&) override
374     {
375         fireFormSubmissionEvent(WEBKIT_FORM_SUBMISSION_WILL_COMPLETE, formElement, frame, sourceFrame, values);
376     }
377
378     void willSendSubmitEvent(WebPage*, HTMLFormElement* formElement, WebFrame* frame, WebFrame* sourceFrame, const Vector<std::pair<String, String>>& values) override
379     {
380         fireFormSubmissionEvent(WEBKIT_FORM_SUBMISSION_WILL_SEND_DOM_EVENT, formElement, frame, sourceFrame, values);
381     }
382
383     void didAssociateFormControls(WebPage*, const Vector<RefPtr<Element>>& elements) override
384     {
385         GRefPtr<GPtrArray> formElements = adoptGRef(g_ptr_array_sized_new(elements.size()));
386         for (size_t i = 0; i < elements.size(); ++i)
387             g_ptr_array_add(formElements.get(), WebKit::kit(elements[i].get()));
388
389         g_signal_emit(m_webPage, signals[FORM_CONTROLS_ASSOCIATED], 0, formElements.get());
390     }
391
392     bool shouldNotifyOnFormChanges(WebPage*) override { return true; }
393
394 private:
395     void fireFormSubmissionEvent(WebKitFormSubmissionStep step, HTMLFormElement* formElement, WebFrame* frame, WebFrame* sourceFrame, const Vector<std::pair<String, String>>& values)
396     {
397         WebKitFrame* webkitTargetFrame = webkitFrameGetOrCreate(frame);
398         WebKitFrame* webkitSourceFrame = webkitFrameGetOrCreate(sourceFrame);
399
400         GRefPtr<GPtrArray> textFieldNames = adoptGRef(g_ptr_array_new_full(values.size(), g_free));
401         GRefPtr<GPtrArray> textFieldValues = adoptGRef(g_ptr_array_new_full(values.size(), g_free));
402         for (auto& pair : values) {
403             g_ptr_array_add(textFieldNames.get(), g_strdup(pair.first.utf8().data()));
404             g_ptr_array_add(textFieldValues.get(), g_strdup(pair.second.utf8().data()));
405         }
406
407         g_signal_emit(m_webPage, signals[WILL_SUBMIT_FORM], 0, WEBKIT_DOM_ELEMENT(WebKit::kit(static_cast<Node*>(formElement))), step, webkitSourceFrame, webkitTargetFrame, textFieldNames.get(), textFieldValues.get());
408     }
409
410     WebKitWebPage* m_webPage;
411 };
412
413 static void webkitWebPageGetProperty(GObject* object, guint propId, GValue* value, GParamSpec* paramSpec)
414 {
415     WebKitWebPage* webPage = WEBKIT_WEB_PAGE(object);
416
417     switch (propId) {
418     case PROP_URI:
419         g_value_set_string(value, webkit_web_page_get_uri(webPage));
420         break;
421     default:
422         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, paramSpec);
423     }
424 }
425
426 static void webkit_web_page_class_init(WebKitWebPageClass* klass)
427 {
428     GObjectClass* gObjectClass = G_OBJECT_CLASS(klass);
429
430     gObjectClass->get_property = webkitWebPageGetProperty;
431
432     /**
433      * WebKitWebPage:uri:
434      *
435      * The current active URI of the #WebKitWebPage.
436      */
437     g_object_class_install_property(
438         gObjectClass,
439         PROP_URI,
440         g_param_spec_string(
441             "uri",
442             _("URI"),
443             _("The current active URI of the web page"),
444             0,
445             WEBKIT_PARAM_READABLE));
446
447     /**
448      * WebKitWebPage::document-loaded:
449      * @web_page: the #WebKitWebPage on which the signal is emitted
450      *
451      * This signal is emitted when the DOM document of a #WebKitWebPage has been
452      * loaded.
453      *
454      * You can wait for this signal to get the DOM document with
455      * webkit_web_page_get_dom_document().
456      */
457     signals[DOCUMENT_LOADED] = g_signal_new(
458         "document-loaded",
459         G_TYPE_FROM_CLASS(klass),
460         G_SIGNAL_RUN_LAST,
461         0, 0, 0,
462         g_cclosure_marshal_VOID__VOID,
463         G_TYPE_NONE, 0);
464
465     /**
466      * WebKitWebPage::send-request:
467      * @web_page: the #WebKitWebPage on which the signal is emitted
468      * @request: a #WebKitURIRequest
469      * @redirected_response: a #WebKitURIResponse, or %NULL
470      *
471      * This signal is emitted when @request is about to be sent to
472      * the server. This signal can be used to modify the #WebKitURIRequest
473      * that will be sent to the server. You can also cancel the resource load
474      * operation by connecting to this signal and returning %TRUE.
475      *
476      * In case of a server redirection this signal is
477      * emitted again with the @request argument containing the new
478      * request to be sent to the server due to the redirection and the
479      * @redirected_response parameter containing the response
480      * received by the server for the initial request.
481      *
482      * Returns: %TRUE to stop other handlers from being invoked for the event.
483      *    %FALSE to continue emission of the event.
484      */
485     signals[SEND_REQUEST] = g_signal_new(
486         "send-request",
487         G_TYPE_FROM_CLASS(klass),
488         G_SIGNAL_RUN_LAST,
489         0,
490         g_signal_accumulator_true_handled, nullptr,
491         g_cclosure_marshal_generic,
492         G_TYPE_BOOLEAN, 2,
493         WEBKIT_TYPE_URI_REQUEST,
494         WEBKIT_TYPE_URI_RESPONSE);
495
496     /**
497      * WebKitWebPage::context-menu:
498      * @web_page: the #WebKitWebPage on which the signal is emitted
499      * @context_menu: the proposed #WebKitContextMenu
500      * @hit_test_result: a #WebKitWebHitTestResult
501      *
502      * Emitted before a context menu is displayed in the UI Process to
503      * give the application a chance to customize the proposed menu,
504      * build its own context menu or pass user data to the UI Process.
505      * This signal is useful when the information available in the UI Process
506      * is not enough to build or customize the context menu, for example, to
507      * add menu entries depending on the #WebKitDOMNode at the coordinates of the
508      * @hit_test_result. Otherwise, it's recommended to use #WebKitWebView::context-menu
509      * signal instead.
510      *
511      * Returns: %TRUE if the proposed @context_menu has been modified, or %FALSE otherwise.
512      *
513      * Since: 2.8
514      */
515     signals[CONTEXT_MENU] = g_signal_new(
516         "context-menu",
517         G_TYPE_FROM_CLASS(klass),
518         G_SIGNAL_RUN_LAST,
519         0,
520         g_signal_accumulator_true_handled, nullptr,
521         g_cclosure_marshal_generic,
522         G_TYPE_BOOLEAN, 2,
523         WEBKIT_TYPE_CONTEXT_MENU,
524         WEBKIT_TYPE_WEB_HIT_TEST_RESULT);
525
526     /**
527      * WebKitWebPage::console-message-sent:
528      * @web_page: the #WebKitWebPage on which the signal is emitted
529      * @console_message: the #WebKitConsoleMessage
530      *
531      * Emitted when a message is sent to the console. This can be a message
532      * produced by the use of JavaScript console API, a JavaScript exception,
533      * a security error or other errors, warnings, debug or log messages.
534      * The @console_message contains information of the message.
535      *
536      * Since: 2.12
537      */
538     signals[CONSOLE_MESSAGE_SENT] = g_signal_new(
539         "console-message-sent",
540         G_TYPE_FROM_CLASS(klass),
541         G_SIGNAL_RUN_LAST,
542         0, 0, nullptr,
543         g_cclosure_marshal_VOID__BOXED,
544         G_TYPE_NONE, 1,
545         WEBKIT_TYPE_CONSOLE_MESSAGE | G_SIGNAL_TYPE_STATIC_SCOPE);
546
547     /**
548      * WebKitWebPage::form-controls-associated:
549      * @web_page: the #WebKitWebPage on which the signal is emitted
550      * @elements: (element-type WebKitDOMElement) (transfer none): a #GPtrArray of
551      *     #WebKitDOMElement with the list of forms in the page
552      *
553      * Emitted after form elements (or form associated elements) are associated to a particular web
554      * page. This is useful to implement form auto filling for web pages where form fields are added
555      * dynamically. This signal might be emitted multiple times for the same web page.
556      *
557      * Note that this signal could be also emitted when form controls are moved between forms. In
558      * that case, the @elements array carries the list of those elements which have moved.
559      *
560      * Clients should take a reference to the members of the @elements array if it is desired to
561      * keep them alive after the signal handler returns.
562      *
563      * Since: 2.16
564      */
565     signals[FORM_CONTROLS_ASSOCIATED] = g_signal_new(
566         "form-controls-associated",
567         G_TYPE_FROM_CLASS(klass),
568         G_SIGNAL_RUN_LAST,
569         0, 0, nullptr,
570         g_cclosure_marshal_VOID__BOXED,
571         G_TYPE_NONE, 1,
572         G_TYPE_PTR_ARRAY);
573
574     /**
575      * WebKitWebPage::will-submit-form:
576      * @web_page: the #WebKitWebPage on which the signal is emitted
577      * @form: the #WebKitDOMElement to be submitted, which will always correspond to an HTMLFormElement
578      * @step: a #WebKitFormSubmissionEventType indicating the current
579      * stage of form submission
580      * @source_frame: the #WebKitFrame containing the form to be
581      * submitted
582      * @target_frame: the #WebKitFrame containing the form's target,
583      * which may be the same as @source_frame if no target was specified
584      * @text_field_names: (element-type utf8) (transfer none): names of
585      * the form's text fields
586      * @text_field_values: (element-type utf8) (transfer none): values
587      * of the form's text fields
588      *
589      * This signal is emitted to indicate various points during form
590      * submission. @step indicates the current stage of form submission.
591      *
592      * If this signal is emitted with %WEBKIT_FORM_SUBMISSION_WILL_SEND_DOM_EVENT,
593      * then the DOM submit event is about to be emitted. JavaScript code
594      * may rely on the submit event to detect that the user has clicked
595      * on a submit button, and to possibly cancel the form submission
596      * before %WEBKIT_FORM_SUBMISSION_WILL_COMPLETE. However, beware
597      * that, for historical reasons, the submit event is not emitted at
598      * all if the form submission is triggered by JavaScript. For these
599      * reasons, %WEBKIT_FORM_SUBMISSION_WILL_SEND_DOM_EVENT may not
600      * be used to reliably detect whether a form will be submitted.
601      * Instead, use it to detect if a user has clicked on a form's
602      * submit button even if JavaScript later cancels the form
603      * submission, or to read the values of the form's fields even if
604      * JavaScript later clears certain fields before submitting. This
605      * may be needed, for example, to implement a robust browser
606      * password manager, as some misguided websites may use such
607      * techniques to attempt to thwart password managers.
608      *
609      * If this signal is emitted with %WEBKIT_FORM_SUBMISSION_WILL_COMPLETE,
610      * the form will imminently be submitted. It can no longer be
611      * cancelled. This event always occurs immediately before a form is
612      * submitted to its target, so use this event to reliably detect
613      * when a form is submitted. This event occurs after
614      * %WEBKIT_FORM_SUBMISSION_WILL_SEND_DOM_EVENT if that event is
615      * emitted.
616      *
617      * Since: 2.20
618      */
619     signals[WILL_SUBMIT_FORM] = g_signal_new(
620         "will-submit-form",
621         G_TYPE_FROM_CLASS(klass),
622         G_SIGNAL_RUN_LAST,
623         0, 0, nullptr,
624         g_cclosure_marshal_generic,
625         G_TYPE_NONE, 6,
626         WEBKIT_DOM_TYPE_ELEMENT,
627         WEBKIT_TYPE_FORM_SUBMISSION_STEP,
628         WEBKIT_TYPE_FRAME,
629         WEBKIT_TYPE_FRAME,
630         G_TYPE_PTR_ARRAY,
631         G_TYPE_PTR_ARRAY);
632 }
633
634 WebPage* webkitWebPageGetPage(WebKitWebPage *webPage)
635 {
636     return webPage->priv->webPage;
637 }
638
639 WebKitWebPage* webkitWebPageCreate(WebPage* webPage)
640 {
641     WebKitWebPage* page = WEBKIT_WEB_PAGE(g_object_new(WEBKIT_TYPE_WEB_PAGE, NULL));
642     page->priv->webPage = webPage;
643
644     webPage->setInjectedBundleResourceLoadClient(std::make_unique<PageResourceLoadClient>(page));
645     webPage->setInjectedBundlePageLoaderClient(std::make_unique<PageLoaderClient>(page));
646     webPage->setInjectedBundleContextMenuClient(std::make_unique<PageContextMenuClient>(page));
647     webPage->setInjectedBundleUIClient(std::make_unique<PageUIClient>(page));
648     webPage->setInjectedBundleFormClient(std::make_unique<PageFormClient>(page));
649
650     return page;
651 }
652
653 void webkitWebPageDidReceiveMessage(WebKitWebPage* page, const String& messageName, API::Dictionary& message)
654 {
655 #if PLATFORM(GTK)
656     if (messageName == String("GetSnapshot")) {
657         SnapshotOptions snapshotOptions = static_cast<SnapshotOptions>(static_cast<API::UInt64*>(message.get("SnapshotOptions"))->value());
658         uint64_t callbackID = static_cast<API::UInt64*>(message.get("CallbackID"))->value();
659         SnapshotRegion region = static_cast<SnapshotRegion>(static_cast<API::UInt64*>(message.get("SnapshotRegion"))->value());
660         bool transparentBackground = static_cast<API::Boolean*>(message.get("TransparentBackground"))->value();
661
662         RefPtr<WebImage> snapshotImage;
663         WebPage* webPage = page->priv->webPage;
664         if (WebCore::FrameView* frameView = webPage->mainFrameView()) {
665             WebCore::IntRect snapshotRect;
666             switch (region) {
667             case SnapshotRegionVisible:
668                 snapshotRect = frameView->visibleContentRect();
669                 break;
670             case SnapshotRegionFullDocument:
671                 snapshotRect = WebCore::IntRect(WebCore::IntPoint(0, 0), frameView->contentsSize());
672                 break;
673             default:
674                 ASSERT_NOT_REACHED();
675             }
676             if (!snapshotRect.isEmpty()) {
677                 Color savedBackgroundColor;
678                 if (transparentBackground) {
679                     savedBackgroundColor = frameView->baseBackgroundColor();
680                     frameView->setBaseBackgroundColor(Color::transparent);
681                 }
682                 snapshotImage = webPage->scaledSnapshotWithOptions(snapshotRect, 1, snapshotOptions | SnapshotOptionsShareable);
683                 if (transparentBackground)
684                     frameView->setBaseBackgroundColor(savedBackgroundColor);
685             }
686         }
687
688         API::Dictionary::MapType messageReply;
689         messageReply.set("Page", webPage);
690         messageReply.set("CallbackID", API::UInt64::create(callbackID));
691         messageReply.set("Snapshot", snapshotImage);
692         WebProcess::singleton().injectedBundle()->postMessage("WebPage.DidGetSnapshot", API::Dictionary::create(WTFMove(messageReply)).ptr());
693     } else
694 #endif
695         ASSERT_NOT_REACHED();
696 }
697
698 /**
699  * webkit_web_page_get_dom_document:
700  * @web_page: a #WebKitWebPage
701  *
702  * Get the #WebKitDOMDocument currently loaded in @web_page
703  *
704  * Returns: (transfer none): the #WebKitDOMDocument currently loaded, or %NULL
705  *    if no document is currently loaded.
706  */
707 WebKitDOMDocument* webkit_web_page_get_dom_document(WebKitWebPage* webPage)
708 {
709     g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(webPage), nullptr);
710
711     if (auto* coreFrame = webPage->priv->webPage->mainFrame())
712         return kit(coreFrame->document());
713
714     return nullptr;
715 }
716
717 /**
718  * webkit_web_page_get_id:
719  * @web_page: a #WebKitWebPage
720  *
721  * Get the identifier of the #WebKitWebPage
722  *
723  * Returns: the identifier of @web_page
724  */
725 guint64 webkit_web_page_get_id(WebKitWebPage* webPage)
726 {
727     g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(webPage), 0);
728
729     return webPage->priv->webPage->pageID();
730 }
731
732 /**
733  * webkit_web_page_get_uri:
734  * @web_page: a #WebKitWebPage
735  *
736  * Returns the current active URI of @web_page.
737  *
738  * You can monitor the active URI by connecting to the notify::uri
739  * signal of @web_page.
740  *
741  * Returns: the current active URI of @web_view or %NULL if nothing has been
742  *    loaded yet.
743  */
744 const gchar* webkit_web_page_get_uri(WebKitWebPage* webPage)
745 {
746     g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(webPage), 0);
747
748     return webPage->priv->uri.data();
749 }
750
751 /**
752  * webkit_web_page_get_main_frame:
753  * @web_page: a #WebKitWebPage
754  *
755  * Returns the main frame of a #WebKitWebPage.
756  *
757  * Returns: (transfer none): the #WebKitFrame that is the main frame of @web_page
758  *
759  * Since: 2.2
760  */
761 WebKitFrame* webkit_web_page_get_main_frame(WebKitWebPage* webPage)
762 {
763     g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(webPage), 0);
764
765     return webkitFrameGetOrCreate(webPage->priv->webPage->mainWebFrame());
766 }
767
768 /**
769  * webkit_web_page_get_editor:
770  * @web_page: a #WebKitWebPage
771  *
772  * Gets the #WebKitWebEditor of a #WebKitWebPage.
773  *
774  * Returns: (transfer none): the #WebKitWebEditor
775  *
776  * Since: 2.10
777  */
778 WebKitWebEditor* webkit_web_page_get_editor(WebKitWebPage* webPage)
779 {
780     g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(webPage), nullptr);
781
782     if (!webPage->priv->webEditor)
783         webPage->priv->webEditor = adoptGRef(webkitWebEditorCreate(webPage));
784
785     return webPage->priv->webEditor.get();
786 }