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