Add WTF::move()
[WebKit-https.git] / Source / WebKit2 / WebProcess / InjectedBundle / API / gtk / WebKitWebPage.cpp
1 /*
2  * Copyright (C) 2012 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 "ImageOptions.h"
24 #include "ImmutableDictionary.h"
25 #include "InjectedBundle.h"
26 #include "WKBundleAPICast.h"
27 #include "WKBundleFrame.h"
28 #include "WebImage.h"
29 #include "WebKitDOMDocumentPrivate.h"
30 #include "WebKitFramePrivate.h"
31 #include "WebKitMarshal.h"
32 #include "WebKitPrivate.h"
33 #include "WebKitScriptWorldPrivate.h"
34 #include "WebKitURIRequestPrivate.h"
35 #include "WebKitURIResponsePrivate.h"
36 #include "WebKitWebPagePrivate.h"
37 #include "WebProcess.h"
38 #include <WebCore/Document.h>
39 #include <WebCore/DocumentLoader.h>
40 #include <WebCore/Frame.h>
41 #include <WebCore/FrameView.h>
42 #include <glib/gi18n-lib.h>
43 #include <wtf/NeverDestroyed.h>
44 #include <wtf/text/CString.h>
45
46 using namespace WebKit;
47 using namespace WebCore;
48
49 enum {
50     DOCUMENT_LOADED,
51     SEND_REQUEST,
52
53     LAST_SIGNAL
54 };
55
56 enum {
57     PROP_0,
58
59     PROP_URI
60 };
61
62 struct _WebKitWebPagePrivate {
63     WebPage* webPage;
64
65     CString uri;
66 };
67
68 static guint signals[LAST_SIGNAL] = { 0, };
69
70 WEBKIT_DEFINE_TYPE(WebKitWebPage, webkit_web_page, G_TYPE_OBJECT)
71
72 typedef HashMap<WebFrame*, GRefPtr<WebKitFrame>> WebFrameMap;
73
74 static WebFrameMap& webFrameMap()
75 {
76     static NeverDestroyed<WebFrameMap> map;
77     return map;
78 }
79
80 static WebKitFrame* webkitFrameGetOrCreate(WebFrame* webFrame)
81 {
82     GRefPtr<WebKitFrame> frame = webFrameMap().get(webFrame);
83     if (frame)
84         return frame.get();
85
86     frame = adoptGRef(webkitFrameCreate(webFrame));
87     webFrameMap().set(webFrame, frame);
88
89     return frame.get();
90 }
91
92 static CString getProvisionalURLForFrame(WebFrame* webFrame)
93 {
94     DocumentLoader* documentLoader = webFrame->coreFrame()->loader().provisionalDocumentLoader();
95     if (!documentLoader->unreachableURL().isEmpty())
96         return documentLoader->unreachableURL().string().utf8();
97
98     return documentLoader->url().string().utf8();
99 }
100
101 static void webkitWebPageSetURI(WebKitWebPage* webPage, const CString& uri)
102 {
103     if (webPage->priv->uri == uri)
104         return;
105
106     webPage->priv->uri = uri;
107     g_object_notify(G_OBJECT(webPage), "uri");
108 }
109
110 static void didStartProvisionalLoadForFrame(WKBundlePageRef, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
111 {
112     if (!WKBundleFrameIsMainFrame(frame))
113         return;
114
115     webkitWebPageSetURI(WEBKIT_WEB_PAGE(clientInfo), getProvisionalURLForFrame(toImpl(frame)));
116 }
117
118 static void didReceiveServerRedirectForProvisionalLoadForFrame(WKBundlePageRef, WKBundleFrameRef frame, WKTypeRef* /* userData */, const void *clientInfo)
119 {
120     if (!WKBundleFrameIsMainFrame(frame))
121         return;
122
123     webkitWebPageSetURI(WEBKIT_WEB_PAGE(clientInfo), getProvisionalURLForFrame(toImpl(frame)));
124 }
125
126 static void didSameDocumentNavigationForFrame(WKBundlePageRef, WKBundleFrameRef frame, WKSameDocumentNavigationType, WKTypeRef* /* userData */, const void *clientInfo)
127 {
128     if (!WKBundleFrameIsMainFrame(frame))
129         return;
130
131     webkitWebPageSetURI(WEBKIT_WEB_PAGE(clientInfo), toImpl(frame)->coreFrame()->document()->url().string().utf8());
132 }
133
134 static void didFinishDocumentLoadForFrame(WKBundlePageRef, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
135 {
136     if (!WKBundleFrameIsMainFrame(frame))
137         return;
138
139     g_signal_emit(WEBKIT_WEB_PAGE(clientInfo), signals[DOCUMENT_LOADED], 0);
140 }
141
142 static void willDestroyFrame(WKBundlePageRef, WKBundleFrameRef frame, const void* /* clientInfo */)
143 {
144     webFrameMap().remove(toImpl(frame));
145 }
146
147 static void didClearWindowObjectForFrame(WKBundlePageRef, WKBundleFrameRef frame, WKBundleScriptWorldRef wkWorld, const void* clientInfo)
148 {
149     if (WebKitScriptWorld* world = webkitScriptWorldGet(toImpl(wkWorld)))
150         webkitScriptWorldWindowObjectCleared(world, WEBKIT_WEB_PAGE(clientInfo), webkitFrameGetOrCreate(toImpl(frame)));
151 }
152
153 static void didInitiateLoadForResource(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, WKURLRequestRef request, bool /* pageLoadIsProvisional */, const void*)
154 {
155     ImmutableDictionary::MapType message;
156     message.set(String::fromUTF8("Page"), toImpl(page));
157     message.set(String::fromUTF8("Frame"), toImpl(frame));
158     message.set(String::fromUTF8("Identifier"), API::UInt64::create(identifier));
159     message.set(String::fromUTF8("Request"), toImpl(request));
160     WebProcess::shared().injectedBundle()->postMessage(String::fromUTF8("WebPage.DidInitiateLoadForResource"), ImmutableDictionary::create(WTF::move(message)).get());
161 }
162
163 static WKURLRequestRef willSendRequestForFrame(WKBundlePageRef page, WKBundleFrameRef, uint64_t identifier, WKURLRequestRef wkRequest, WKURLResponseRef wkRedirectResponse, const void* clientInfo)
164 {
165     GRefPtr<WebKitURIRequest> request = adoptGRef(webkitURIRequestCreateForResourceRequest(toImpl(wkRequest)->resourceRequest()));
166     const ResourceResponse& redirectResourceResponse = toImpl(wkRedirectResponse)->resourceResponse();
167     GRefPtr<WebKitURIResponse> redirectResponse = !redirectResourceResponse.isNull() ? adoptGRef(webkitURIResponseCreateForResourceResponse(redirectResourceResponse)) : nullptr;
168
169     gboolean returnValue;
170     g_signal_emit(WEBKIT_WEB_PAGE(clientInfo), signals[SEND_REQUEST], 0, request.get(), redirectResponse.get(), &returnValue);
171     if (returnValue)
172         return 0;
173
174     ResourceRequest resourceRequest;
175     webkitURIRequestGetResourceRequest(request.get(), resourceRequest);
176     resourceRequest.setInitiatingPageID(toImpl(page)->pageID());
177     RefPtr<API::URLRequest> newRequest = API::URLRequest::create(resourceRequest);
178
179     ImmutableDictionary::MapType message;
180     message.set(String::fromUTF8("Page"), toImpl(page));
181     message.set(String::fromUTF8("Identifier"), API::UInt64::create(identifier));
182     message.set(String::fromUTF8("Request"), newRequest.get());
183     if (!redirectResourceResponse.isNull())
184         message.set(String::fromUTF8("RedirectResponse"), toImpl(wkRedirectResponse));
185     WebProcess::shared().injectedBundle()->postMessage(String::fromUTF8("WebPage.DidSendRequestForResource"), ImmutableDictionary::create(WTF::move(message)).get());
186
187     return toAPI(newRequest.release().leakRef());
188 }
189
190 static void didReceiveResponseForResource(WKBundlePageRef page, WKBundleFrameRef, uint64_t identifier, WKURLResponseRef response, const void*)
191 {
192     ImmutableDictionary::MapType message;
193     message.set(String::fromUTF8("Page"), toImpl(page));
194     message.set(String::fromUTF8("Identifier"), API::UInt64::create(identifier));
195     message.set(String::fromUTF8("Response"), toImpl(response));
196     WebProcess::shared().injectedBundle()->postMessage(String::fromUTF8("WebPage.DidReceiveResponseForResource"), ImmutableDictionary::create(WTF::move(message)).get());
197 }
198
199 static void didReceiveContentLengthForResource(WKBundlePageRef page, WKBundleFrameRef, uint64_t identifier, uint64_t length, const void*)
200 {
201     ImmutableDictionary::MapType message;
202     message.set(String::fromUTF8("Page"), toImpl(page));
203     message.set(String::fromUTF8("Identifier"), API::UInt64::create(identifier));
204     message.set(String::fromUTF8("ContentLength"), API::UInt64::create(length));
205     WebProcess::shared().injectedBundle()->postMessage(String::fromUTF8("WebPage.DidReceiveContentLengthForResource"), ImmutableDictionary::create(WTF::move(message)).get());
206 }
207
208 static void didFinishLoadForResource(WKBundlePageRef page, WKBundleFrameRef, uint64_t identifier, const void*)
209 {
210     ImmutableDictionary::MapType message;
211     message.set(String::fromUTF8("Page"), toImpl(page));
212     message.set(String::fromUTF8("Identifier"), API::UInt64::create(identifier));
213     WebProcess::shared().injectedBundle()->postMessage(String::fromUTF8("WebPage.DidFinishLoadForResource"), ImmutableDictionary::create(WTF::move(message)).get());
214 }
215
216 static void didFailLoadForResource(WKBundlePageRef page, WKBundleFrameRef, uint64_t identifier, WKErrorRef error, const void*)
217 {
218     ImmutableDictionary::MapType message;
219     message.set(String::fromUTF8("Page"), toImpl(page));
220     message.set(String::fromUTF8("Identifier"), API::UInt64::create(identifier));
221     message.set(String::fromUTF8("Error"), toImpl(error));
222     WebProcess::shared().injectedBundle()->postMessage(String::fromUTF8("WebPage.DidFailLoadForResource"), ImmutableDictionary::create(WTF::move(message)).get());
223 }
224
225 static void webkitWebPageGetProperty(GObject* object, guint propId, GValue* value, GParamSpec* paramSpec)
226 {
227     WebKitWebPage* webPage = WEBKIT_WEB_PAGE(object);
228
229     switch (propId) {
230     case PROP_URI:
231         g_value_set_string(value, webkit_web_page_get_uri(webPage));
232         break;
233     default:
234         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, paramSpec);
235     }
236 }
237
238 static void webkit_web_page_class_init(WebKitWebPageClass* klass)
239 {
240     GObjectClass* gObjectClass = G_OBJECT_CLASS(klass);
241
242     gObjectClass->get_property = webkitWebPageGetProperty;
243
244     /**
245      * WebKitWebPage:uri:
246      *
247      * The current active URI of the #WebKitWebPage.
248      */
249     g_object_class_install_property(
250         gObjectClass,
251         PROP_URI,
252         g_param_spec_string(
253             "uri",
254             _("URI"),
255             _("The current active URI of the web page"),
256             0,
257             WEBKIT_PARAM_READABLE));
258
259     /**
260      * WebKitWebPage::document-loaded:
261      * @web_page: the #WebKitWebPage on which the signal is emitted
262      *
263      * This signal is emitted when the DOM document of a #WebKitWebPage has been
264      * loaded.
265      *
266      * You can wait for this signal to get the DOM document with
267      * webkit_web_page_get_dom_document().
268      */
269     signals[DOCUMENT_LOADED] = g_signal_new(
270         "document-loaded",
271         G_TYPE_FROM_CLASS(klass),
272         G_SIGNAL_RUN_LAST,
273         0, 0, 0,
274         g_cclosure_marshal_VOID__VOID,
275         G_TYPE_NONE, 0);
276
277     /**
278      * WebKitWebPage::send-request:
279      * @web_page: the #WebKitWebPage on which the signal is emitted
280      * @request: a #WebKitURIRequest
281      * @redirected_response: a #WebKitURIResponse, or %NULL
282      *
283      * This signal is emitted when @request is about to be sent to
284      * the server. This signal can be used to modify the #WebKitURIRequest
285      * that will be sent to the server. You can also cancel the resource load
286      * operation by connecting to this signal and returning %TRUE.
287      *
288      * In case of a server redirection this signal is
289      * emitted again with the @request argument containing the new
290      * request to be sent to the server due to the redirection and the
291      * @redirected_response parameter containing the response
292      * received by the server for the initial request.
293      *
294      * Returns: %TRUE to stop other handlers from being invoked for the event.
295      *    %FALSE to continue emission of the event.
296      */
297     signals[SEND_REQUEST] = g_signal_new(
298         "send-request",
299         G_TYPE_FROM_CLASS(klass),
300         G_SIGNAL_RUN_LAST,
301         0,
302         g_signal_accumulator_true_handled, 0,
303         webkit_marshal_BOOLEAN__OBJECT_OBJECT,
304         G_TYPE_BOOLEAN, 2,
305         WEBKIT_TYPE_URI_REQUEST,
306         WEBKIT_TYPE_URI_RESPONSE);
307 }
308
309 WebKitWebPage* webkitWebPageCreate(WebPage* webPage)
310 {
311     WebKitWebPage* page = WEBKIT_WEB_PAGE(g_object_new(WEBKIT_TYPE_WEB_PAGE, NULL));
312     page->priv->webPage = webPage;
313
314     WKBundlePageLoaderClientV7 loaderClient = {
315         {
316             7, // version
317             page, // clientInfo
318         },
319         didStartProvisionalLoadForFrame,
320         didReceiveServerRedirectForProvisionalLoadForFrame,
321         0, // didFailProvisionalLoadWithErrorForFrame
322         0, // didCommitLoadForFrame
323         didFinishDocumentLoadForFrame,
324         0, // didFinishLoadForFrame
325         0, // didFailLoadWithErrorForFrame
326         didSameDocumentNavigationForFrame,
327         0, // didReceiveTitleForFrame
328         0, // didFirstLayoutForFrame
329         0, // didFirstVisuallyNonEmptyLayoutForFrame
330         0, // didRemoveFrameFromHierarchy
331         0, // didDisplayInsecureContentForFrame
332         0, // didRunInsecureContentForFrame
333         didClearWindowObjectForFrame,
334         0, // didCancelClientRedirectForFrame
335         0, // willPerformClientRedirectForFrame
336         0, // didHandleOnloadEventsForFrame
337         0, // didLayoutForFrame
338         0, // didNewFirstVisuallyNonEmptyLayout
339         0, // didDetectXSSForFrame
340         0, // shouldGoToBackForwardListItem
341         0, // globalObjectIsAvailableForFrame
342         0, // willDisconnectDOMWindowExtensionFromGlobalObject
343         0, // didReconnectDOMWindowExtensionToGlobalObject
344         0, // willDestroyGlobalObjectForDOMWindowExtension
345         0, // didFinishProgress
346         0, // shouldForceUniversalAccessFromLocalURL
347         0, // didReceiveIntentForFrame_unavailable
348         0, // registerIntentServiceForFrame_unavailable
349         0, // didLayout
350         0, // featuresUsedInPage
351         0, // willLoadURLRequest
352         0, // willLoadDataRequest
353         willDestroyFrame
354     };
355     WKBundlePageSetPageLoaderClient(toAPI(webPage), &loaderClient.base);
356
357     WKBundlePageResourceLoadClientV1 resourceLoadClient = {
358         {
359             1, // version
360             page, // clientInfo
361         },
362         didInitiateLoadForResource,
363         willSendRequestForFrame,
364         didReceiveResponseForResource,
365         didReceiveContentLengthForResource,
366         didFinishLoadForResource,
367         didFailLoadForResource,
368         0, // shouldCacheResponse
369         0 // shouldUseCredentialStorage
370     };
371     WKBundlePageSetResourceLoadClient(toAPI(webPage), &resourceLoadClient.base);
372
373     return page;
374 }
375
376 void webkitWebPageDidReceiveMessage(WebKitWebPage* page, const String& messageName, ImmutableDictionary& message)
377 {
378     if (messageName == String("GetSnapshot")) {
379         SnapshotOptions snapshotOptions = static_cast<SnapshotOptions>(static_cast<API::UInt64*>(message.get("SnapshotOptions"))->value());
380         uint64_t callbackID = static_cast<API::UInt64*>(message.get("CallbackID"))->value();
381         SnapshotRegion region = static_cast<SnapshotRegion>(static_cast<API::UInt64*>(message.get("SnapshotRegion"))->value());
382
383         RefPtr<WebImage> snapshotImage;
384         WebPage* webPage = page->priv->webPage;
385         if (WebCore::FrameView* frameView = webPage->mainFrameView()) {
386             WebCore::IntRect snapshotRect;
387             switch (region) {
388             case SnapshotRegionVisible:
389                 snapshotRect = frameView->visibleContentRect();
390                 break;
391             case SnapshotRegionFullDocument:
392                 snapshotRect = WebCore::IntRect(WebCore::IntPoint(0, 0), frameView->contentsSize());
393                 break;
394             default:
395                 ASSERT_NOT_REACHED();
396             }
397             if (!snapshotRect.isEmpty())
398                 snapshotImage = webPage->scaledSnapshotWithOptions(snapshotRect, 1, snapshotOptions | SnapshotOptionsShareable);
399         }
400
401         ImmutableDictionary::MapType messageReply;
402         messageReply.set("Page", webPage);
403         messageReply.set("CallbackID", API::UInt64::create(callbackID));
404         messageReply.set("Snapshot", snapshotImage);
405         WebProcess::shared().injectedBundle()->postMessage("WebPage.DidGetSnapshot", ImmutableDictionary::create(WTF::move(messageReply)).get());
406     } else
407         ASSERT_NOT_REACHED();
408 }
409
410 /**
411  * webkit_web_page_get_dom_document:
412  * @web_page: a #WebKitWebPage
413  *
414  * Get the #WebKitDOMDocument currently loaded in @web_page
415  *
416  * Returns: (transfer none): the #WebKitDOMDocument currently loaded, or %NULL
417  *    if no document is currently loaded.
418  */
419 WebKitDOMDocument* webkit_web_page_get_dom_document(WebKitWebPage* webPage)
420 {
421     g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(webPage), 0);
422
423     Frame* coreFrame = webPage->priv->webPage->mainFrame();
424     if (!coreFrame)
425         return 0;
426
427     return kit(coreFrame->document());
428 }
429
430 /**
431  * webkit_web_page_get_id:
432  * @web_page: a #WebKitWebPage
433  *
434  * Get the identifier of the #WebKitWebPage
435  *
436  * Returns: the identifier of @web_page
437  */
438 guint64 webkit_web_page_get_id(WebKitWebPage* webPage)
439 {
440     g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(webPage), 0);
441
442     return webPage->priv->webPage->pageID();
443 }
444
445 /**
446  * webkit_web_page_get_uri:
447  * @web_page: a #WebKitWebPage
448  *
449  * Returns the current active URI of @web_page.
450  *
451  * You can monitor the active URI by connecting to the notify::uri
452  * signal of @web_page.
453  *
454  * Returns: the current active URI of @web_view or %NULL if nothing has been
455  *    loaded yet.
456  */
457 const gchar* webkit_web_page_get_uri(WebKitWebPage* webPage)
458 {
459     g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(webPage), 0);
460
461     return webPage->priv->uri.data();
462 }
463
464 /**
465  * webkit_web_page_get_main_frame:
466  * @web_page: a #WebKitWebPage
467  *
468  * Returns the main frame of a #WebKitWebPage.
469  *
470  * Returns: (transfer none): the #WebKitFrame that is the main frame of @web_page
471  *
472  * Since: 2.2
473  */
474 WebKitFrame* webkit_web_page_get_main_frame(WebKitWebPage* webPage)
475 {
476     g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(webPage), 0);
477
478     return webkitFrameGetOrCreate(webPage->priv->webPage->mainWebFrame());
479 }