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