| /* |
| * Copyright (C) 2012 Igalia S.L. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2,1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public License |
| * along with this library; see the file COPYING.LIB. If not, write to |
| * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| */ |
| |
| #include "config.h" |
| #include "WebKitWebPage.h" |
| |
| #include "ImageOptions.h" |
| #include "ImmutableDictionary.h" |
| #include "InjectedBundle.h" |
| #include "WKBundleAPICast.h" |
| #include "WKBundleFrame.h" |
| #include "WebImage.h" |
| #include "WebKitDOMDocumentPrivate.h" |
| #include "WebKitFramePrivate.h" |
| #include "WebKitMarshal.h" |
| #include "WebKitPrivate.h" |
| #include "WebKitURIRequestPrivate.h" |
| #include "WebKitURIResponsePrivate.h" |
| #include "WebKitWebPagePrivate.h" |
| #include "WebProcess.h" |
| #include <WebCore/Document.h> |
| #include <WebCore/DocumentLoader.h> |
| #include <WebCore/Frame.h> |
| #include <WebCore/FrameView.h> |
| #include <glib/gi18n-lib.h> |
| #include <wtf/NeverDestroyed.h> |
| #include <wtf/text/CString.h> |
| |
| using namespace WebKit; |
| using namespace WebCore; |
| |
| enum { |
| DOCUMENT_LOADED, |
| SEND_REQUEST, |
| |
| LAST_SIGNAL |
| }; |
| |
| enum { |
| PROP_0, |
| |
| PROP_URI |
| }; |
| |
| struct _WebKitWebPagePrivate { |
| WebPage* webPage; |
| |
| CString uri; |
| }; |
| |
| static guint signals[LAST_SIGNAL] = { 0, }; |
| |
| WEBKIT_DEFINE_TYPE(WebKitWebPage, webkit_web_page, G_TYPE_OBJECT) |
| |
| typedef HashMap<WebFrame*, GRefPtr<WebKitFrame>> WebFrameMap; |
| |
| static WebFrameMap& webFrameMap() |
| { |
| static NeverDestroyed<WebFrameMap> map; |
| return map; |
| } |
| |
| static WebKitFrame* webkitFrameGetOrCreate(WebFrame* webFrame) |
| { |
| GRefPtr<WebKitFrame> frame = webFrameMap().get(webFrame); |
| if (frame) |
| return frame.get(); |
| |
| frame = adoptGRef(webkitFrameCreate(webFrame)); |
| webFrameMap().set(webFrame, frame); |
| |
| return frame.get(); |
| } |
| |
| static CString getProvisionalURLForFrame(WebFrame* webFrame) |
| { |
| DocumentLoader* documentLoader = webFrame->coreFrame()->loader().provisionalDocumentLoader(); |
| if (!documentLoader->unreachableURL().isEmpty()) |
| return documentLoader->unreachableURL().string().utf8(); |
| |
| return documentLoader->url().string().utf8(); |
| } |
| |
| static void webkitWebPageSetURI(WebKitWebPage* webPage, const CString& uri) |
| { |
| if (webPage->priv->uri == uri) |
| return; |
| |
| webPage->priv->uri = uri; |
| g_object_notify(G_OBJECT(webPage), "uri"); |
| } |
| |
| static void didStartProvisionalLoadForFrame(WKBundlePageRef, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo) |
| { |
| if (!WKBundleFrameIsMainFrame(frame)) |
| return; |
| |
| webkitWebPageSetURI(WEBKIT_WEB_PAGE(clientInfo), getProvisionalURLForFrame(toImpl(frame))); |
| } |
| |
| static void didReceiveServerRedirectForProvisionalLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef* userData, const void *clientInfo) |
| { |
| if (!WKBundleFrameIsMainFrame(frame)) |
| return; |
| |
| webkitWebPageSetURI(WEBKIT_WEB_PAGE(clientInfo), getProvisionalURLForFrame(toImpl(frame))); |
| } |
| |
| static void didSameDocumentNavigationForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKSameDocumentNavigationType type, WKTypeRef* userData, const void *clientInfo) |
| { |
| if (!WKBundleFrameIsMainFrame(frame)) |
| return; |
| |
| webkitWebPageSetURI(WEBKIT_WEB_PAGE(clientInfo), toImpl(frame)->coreFrame()->document()->url().string().utf8()); |
| } |
| |
| static void didFinishDocumentLoadForFrame(WKBundlePageRef, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo) |
| { |
| if (!WKBundleFrameIsMainFrame(frame)) |
| return; |
| |
| g_signal_emit(WEBKIT_WEB_PAGE(clientInfo), signals[DOCUMENT_LOADED], 0); |
| } |
| |
| static void willDestroyFrame(WKBundlePageRef, WKBundleFrameRef frame, const void *clientInfo) |
| { |
| webFrameMap().remove(toImpl(frame)); |
| } |
| |
| static void didInitiateLoadForResource(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, WKURLRequestRef request, bool pageLoadIsProvisional, const void*) |
| { |
| ImmutableDictionary::MapType message; |
| message.set(String::fromUTF8("Page"), toImpl(page)); |
| message.set(String::fromUTF8("Frame"), toImpl(frame)); |
| message.set(String::fromUTF8("Identifier"), WebUInt64::create(identifier)); |
| message.set(String::fromUTF8("Request"), toImpl(request)); |
| WebProcess::shared().injectedBundle()->postMessage(String::fromUTF8("WebPage.DidInitiateLoadForResource"), ImmutableDictionary::adopt(message).get()); |
| } |
| |
| static WKURLRequestRef willSendRequestForFrame(WKBundlePageRef page, WKBundleFrameRef, uint64_t identifier, WKURLRequestRef wkRequest, WKURLResponseRef wkRedirectResponse, const void* clientInfo) |
| { |
| GRefPtr<WebKitURIRequest> request = adoptGRef(webkitURIRequestCreateForResourceRequest(toImpl(wkRequest)->resourceRequest())); |
| GRefPtr<WebKitURIResponse> redirectResponse = wkRedirectResponse ? adoptGRef(webkitURIResponseCreateForResourceResponse(toImpl(wkRedirectResponse)->resourceResponse())) : 0; |
| |
| gboolean returnValue; |
| g_signal_emit(WEBKIT_WEB_PAGE(clientInfo), signals[SEND_REQUEST], 0, request.get(), redirectResponse.get(), &returnValue); |
| if (returnValue) |
| return 0; |
| |
| ResourceRequest resourceRequest; |
| webkitURIRequestGetResourceRequest(request.get(), resourceRequest); |
| RefPtr<WebURLRequest> newRequest = WebURLRequest::create(resourceRequest); |
| |
| ImmutableDictionary::MapType message; |
| message.set(String::fromUTF8("Page"), toImpl(page)); |
| message.set(String::fromUTF8("Identifier"), WebUInt64::create(identifier)); |
| message.set(String::fromUTF8("Request"), newRequest.get()); |
| if (!toImpl(wkRedirectResponse)->resourceResponse().isNull()) |
| message.set(String::fromUTF8("RedirectResponse"), toImpl(wkRedirectResponse)); |
| WebProcess::shared().injectedBundle()->postMessage(String::fromUTF8("WebPage.DidSendRequestForResource"), ImmutableDictionary::adopt(message).get()); |
| |
| return toAPI(newRequest.release().leakRef()); |
| } |
| |
| static void didReceiveResponseForResource(WKBundlePageRef page, WKBundleFrameRef, uint64_t identifier, WKURLResponseRef response, const void*) |
| { |
| ImmutableDictionary::MapType message; |
| message.set(String::fromUTF8("Page"), toImpl(page)); |
| message.set(String::fromUTF8("Identifier"), WebUInt64::create(identifier)); |
| message.set(String::fromUTF8("Response"), toImpl(response)); |
| WebProcess::shared().injectedBundle()->postMessage(String::fromUTF8("WebPage.DidReceiveResponseForResource"), ImmutableDictionary::adopt(message).get()); |
| } |
| |
| static void didReceiveContentLengthForResource(WKBundlePageRef page, WKBundleFrameRef, uint64_t identifier, uint64_t length, const void*) |
| { |
| ImmutableDictionary::MapType message; |
| message.set(String::fromUTF8("Page"), toImpl(page)); |
| message.set(String::fromUTF8("Identifier"), WebUInt64::create(identifier)); |
| message.set(String::fromUTF8("ContentLength"), WebUInt64::create(length)); |
| WebProcess::shared().injectedBundle()->postMessage(String::fromUTF8("WebPage.DidReceiveContentLengthForResource"), ImmutableDictionary::adopt(message).get()); |
| } |
| |
| static void didFinishLoadForResource(WKBundlePageRef page, WKBundleFrameRef, uint64_t identifier, const void*) |
| { |
| ImmutableDictionary::MapType message; |
| message.set(String::fromUTF8("Page"), toImpl(page)); |
| message.set(String::fromUTF8("Identifier"), WebUInt64::create(identifier)); |
| WebProcess::shared().injectedBundle()->postMessage(String::fromUTF8("WebPage.DidFinishLoadForResource"), ImmutableDictionary::adopt(message).get()); |
| } |
| |
| static void didFailLoadForResource(WKBundlePageRef page, WKBundleFrameRef, uint64_t identifier, WKErrorRef error, const void*) |
| { |
| ImmutableDictionary::MapType message; |
| message.set(String::fromUTF8("Page"), toImpl(page)); |
| message.set(String::fromUTF8("Identifier"), WebUInt64::create(identifier)); |
| message.set(String::fromUTF8("Error"), toImpl(error)); |
| WebProcess::shared().injectedBundle()->postMessage(String::fromUTF8("WebPage.DidFailLoadForResource"), ImmutableDictionary::adopt(message).get()); |
| } |
| |
| static void webkitWebPageGetProperty(GObject* object, guint propId, GValue* value, GParamSpec* paramSpec) |
| { |
| WebKitWebPage* webPage = WEBKIT_WEB_PAGE(object); |
| |
| switch (propId) { |
| case PROP_URI: |
| g_value_set_string(value, webkit_web_page_get_uri(webPage)); |
| break; |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, paramSpec); |
| } |
| } |
| |
| static void webkit_web_page_class_init(WebKitWebPageClass* klass) |
| { |
| GObjectClass* gObjectClass = G_OBJECT_CLASS(klass); |
| |
| gObjectClass->get_property = webkitWebPageGetProperty; |
| |
| /** |
| * WebKitWebPage:uri: |
| * |
| * The current active URI of the #WebKitWebPage. |
| */ |
| g_object_class_install_property( |
| gObjectClass, |
| PROP_URI, |
| g_param_spec_string( |
| "uri", |
| _("URI"), |
| _("The current active URI of the web page"), |
| 0, |
| WEBKIT_PARAM_READABLE)); |
| |
| /** |
| * WebKitWebPage::document-loaded: |
| * @web_page: the #WebKitWebPage on which the signal is emitted |
| * |
| * This signal is emitted when the DOM document of a #WebKitWebPage has been |
| * loaded. |
| * |
| * You can wait for this signal to get the DOM document with |
| * webkit_web_page_get_dom_document(). |
| */ |
| signals[DOCUMENT_LOADED] = g_signal_new( |
| "document-loaded", |
| G_TYPE_FROM_CLASS(klass), |
| G_SIGNAL_RUN_LAST, |
| 0, 0, 0, |
| g_cclosure_marshal_VOID__OBJECT, |
| G_TYPE_NONE, 0); |
| |
| /** |
| * WebKitWebPage::send-request: |
| * @web_page: the #WebKitWebPage on which the signal is emitted |
| * @request: a #WebKitURIRequest |
| * @redirected_response: a #WebKitURIResponse, or %NULL |
| * |
| * This signal is emitted when @request is about to be sent to |
| * the server. This signal can be used to modify the #WebKitURIRequest |
| * that will be sent to the server. You can also cancel the resource load |
| * operation by connecting to this signal and returning %TRUE. |
| * |
| * In case of a server redirection this signal is |
| * emitted again with the @request argument containing the new |
| * request to be sent to the server due to the redirection and the |
| * @redirected_response parameter containing the response |
| * received by the server for the initial request. |
| * |
| * Returns: %TRUE to stop other handlers from being invoked for the event. |
| * %FALSE to continue emission of the event. |
| */ |
| signals[SEND_REQUEST] = g_signal_new( |
| "send-request", |
| G_TYPE_FROM_CLASS(klass), |
| G_SIGNAL_RUN_LAST, |
| 0, |
| g_signal_accumulator_true_handled, 0, |
| webkit_marshal_BOOLEAN__OBJECT_OBJECT, |
| G_TYPE_BOOLEAN, 2, |
| WEBKIT_TYPE_URI_REQUEST, |
| WEBKIT_TYPE_URI_RESPONSE); |
| } |
| |
| WebKitWebPage* webkitWebPageCreate(WebPage* webPage) |
| { |
| WebKitWebPage* page = WEBKIT_WEB_PAGE(g_object_new(WEBKIT_TYPE_WEB_PAGE, NULL)); |
| page->priv->webPage = webPage; |
| |
| WKBundlePageLoaderClient loaderClient = { |
| kWKBundlePageLoaderClientCurrentVersion, |
| page, |
| didStartProvisionalLoadForFrame, |
| didReceiveServerRedirectForProvisionalLoadForFrame, |
| 0, // didFailProvisionalLoadWithErrorForFrame |
| 0, // didCommitLoadForFrame |
| didFinishDocumentLoadForFrame, |
| 0, // didFinishLoadForFrame |
| 0, // didFailLoadWithErrorForFrame |
| didSameDocumentNavigationForFrame, |
| 0, // didReceiveTitleForFrame |
| 0, // didFirstLayoutForFrame |
| 0, // didFirstVisuallyNonEmptyLayoutForFrame |
| 0, // didRemoveFrameFromHierarchy |
| 0, // didDisplayInsecureContentForFrame |
| 0, // didRunInsecureContentForFrame |
| 0, // didClearWindowObjectForFrame |
| 0, // didCancelClientRedirectForFrame |
| 0, // willPerformClientRedirectForFrame |
| 0, // didHandleOnloadEventsForFrame |
| 0, // didLayoutForFrame |
| 0, // didNewFirstVisuallyNonEmptyLayout |
| 0, // didDetectXSSForFrame |
| 0, // shouldGoToBackForwardListItem |
| 0, // globalObjectIsAvailableForFrame |
| 0, // willDisconnectDOMWindowExtensionFromGlobalObject |
| 0, // didReconnectDOMWindowExtensionToGlobalObject |
| 0, // willDestroyGlobalObjectForDOMWindowExtension |
| 0, // didFinishProgress |
| 0, // shouldForceUniversalAccessFromLocalURL |
| 0, // didReceiveIntentForFrame_unavailable |
| 0, // registerIntentServiceForFrame_unavailable |
| 0, // didLayout |
| 0, // featuresUsedInPage |
| 0, // willLoadURLRequest |
| 0, // willLoadDataRequest |
| willDestroyFrame |
| }; |
| WKBundlePageSetPageLoaderClient(toAPI(webPage), &loaderClient); |
| |
| WKBundlePageResourceLoadClient resourceLoadClient = { |
| kWKBundlePageResourceLoadClientCurrentVersion, |
| page, |
| didInitiateLoadForResource, |
| willSendRequestForFrame, |
| didReceiveResponseForResource, |
| didReceiveContentLengthForResource, |
| didFinishLoadForResource, |
| didFailLoadForResource, |
| 0, // shouldCacheResponse |
| 0 // shouldUseCredentialStorage |
| }; |
| WKBundlePageSetResourceLoadClient(toAPI(webPage), &resourceLoadClient); |
| |
| return page; |
| } |
| |
| void webkitWebPageDidReceiveMessage(WebKitWebPage* page, const String& messageName, ImmutableDictionary& message) |
| { |
| if (messageName == String("GetSnapshot")) { |
| SnapshotOptions snapshotOptions = static_cast<SnapshotOptions>(static_cast<WebUInt64*>(message.get("SnapshotOptions"))->value()); |
| uint64_t callbackID = static_cast<WebUInt64*>(message.get("CallbackID"))->value(); |
| SnapshotRegion region = static_cast<SnapshotRegion>(static_cast<WebUInt64*>(message.get("SnapshotRegion"))->value()); |
| |
| RefPtr<WebImage> snapshotImage; |
| WebPage* webPage = page->priv->webPage; |
| if (WebCore::FrameView* frameView = webPage->mainFrameView()) { |
| WebCore::IntRect snapshotRect; |
| switch (region) { |
| case SnapshotRegionVisible: |
| snapshotRect = frameView->visibleContentRect(WebCore::ScrollableArea::ExcludeScrollbars); |
| break; |
| case SnapshotRegionFullDocument: |
| snapshotRect = WebCore::IntRect(WebCore::IntPoint(0, 0), frameView->contentsSize()); |
| break; |
| default: |
| ASSERT_NOT_REACHED(); |
| } |
| if (!snapshotRect.isEmpty()) |
| snapshotImage = webPage->scaledSnapshotWithOptions(snapshotRect, 1, snapshotOptions | SnapshotOptionsShareable); |
| } |
| |
| ImmutableDictionary::MapType messageReply; |
| messageReply.set("Page", webPage); |
| messageReply.set("CallbackID", WebUInt64::create(callbackID)); |
| messageReply.set("Snapshot", snapshotImage); |
| WebProcess::shared().injectedBundle()->postMessage("WebPage.DidGetSnapshot", ImmutableDictionary::adopt(messageReply).get()); |
| } else |
| ASSERT_NOT_REACHED(); |
| } |
| |
| /** |
| * webkit_web_page_get_dom_document: |
| * @web_page: a #WebKitWebPage |
| * |
| * Get the #WebKitDOMDocument currently loaded in @web_page |
| * |
| * Returns: the #WebKitDOMDocument currently loaded, or %NULL |
| * if no document is currently loaded. |
| */ |
| WebKitDOMDocument* webkit_web_page_get_dom_document(WebKitWebPage* webPage) |
| { |
| g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(webPage), 0); |
| |
| Frame* coreFrame = webPage->priv->webPage->mainFrame(); |
| if (!coreFrame) |
| return 0; |
| |
| return kit(coreFrame->document()); |
| } |
| |
| /** |
| * webkit_web_page_get_id: |
| * @web_page: a #WebKitWebPage |
| * |
| * Get the identifier of the #WebKitWebPage |
| * |
| * Returns: the identifier of @web_page |
| */ |
| guint64 webkit_web_page_get_id(WebKitWebPage* webPage) |
| { |
| g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(webPage), 0); |
| |
| return webPage->priv->webPage->pageID(); |
| } |
| |
| /** |
| * webkit_web_page_get_uri: |
| * @web_page: a #WebKitWebPage |
| * |
| * Returns the current active URI of @web_page. |
| * |
| * You can monitor the active URI by connecting to the notify::uri |
| * signal of @web_page. |
| * |
| * Returns: the current active URI of @web_view or %NULL if nothing has been |
| * loaded yet. |
| */ |
| const gchar* webkit_web_page_get_uri(WebKitWebPage* webPage) |
| { |
| g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(webPage), 0); |
| |
| return webPage->priv->uri.data(); |
| } |
| |
| WebKitFrame* webkit_web_page_get_main_frame(WebKitWebPage* webPage) |
| { |
| g_return_val_if_fail(WEBKIT_IS_WEB_PAGE(webPage), 0); |
| |
| return webkitFrameGetOrCreate(webPage->priv->webPage->mainWebFrame()); |
| } |