2008-01-09 Luca Bruno <lethalman88@gmail.com>
[WebKit-https.git] / WebKit / gtk / WebView / webkitwebframe.cpp
1 /*
2  * Copyright (C) 2007 Holger Hans Peter Freyther
3  * Copyright (C) 2007 Alp Toker <alp@atoker.com>
4  * Copyright (C) 2007 Apple Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public License
17  * along with this library; see the file COPYING.LIB.  If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21
22 #include "config.h"
23
24 #include "webkitwebframe.h"
25 #include "webkitwebview.h"
26 #include "webkit-marshal.h"
27 #include "webkitprivate.h"
28
29 #include "CString.h"
30 #include "FrameLoader.h"
31 #include "FrameLoaderClientGtk.h"
32 #include "FrameTree.h"
33 #include "FrameView.h"
34 #include "GraphicsContext.h"
35 #include "HTMLFrameOwnerElement.h"
36 #include "RenderView.h"
37 #include "kjs_binding.h"
38 #include "kjs_proxy.h"
39 #include "kjs_window.h"
40
41 #include <JavaScriptCore/APICast.h>
42
43 using namespace WebKit;
44 using namespace WebCore;
45
46 extern "C" {
47
48 enum {
49     CLEARED,
50     LOAD_COMMITTED,
51     LOAD_DONE,
52     TITLE_CHANGED,
53     HOVERING_OVER_LINK,
54     LAST_SIGNAL
55 };
56
57 enum {
58     PROP_0,
59
60     PROP_NAME,
61     PROP_TITLE,
62     PROP_URI
63 };
64
65 static guint webkit_web_frame_signals[LAST_SIGNAL] = { 0, };
66
67 G_DEFINE_TYPE(WebKitWebFrame, webkit_web_frame, G_TYPE_OBJECT)
68
69 static void webkit_web_frame_get_property(GObject* object, guint prop_id, GValue* value, GParamSpec* pspec)
70 {
71     WebKitWebFrame* frame = WEBKIT_WEB_FRAME(object);
72
73     switch(prop_id) {
74     case PROP_NAME:
75         g_value_set_string(value, webkit_web_frame_get_name(frame));
76         break;
77     case PROP_TITLE:
78         g_value_set_string(value, webkit_web_frame_get_title(frame));
79         break;
80     case PROP_URI:
81         g_value_set_string(value, webkit_web_frame_get_uri(frame));
82         break;
83     default:
84         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
85         break;
86     }
87 }
88
89 static void webkit_web_frame_finalize(GObject* object)
90 {
91     WebKitWebFramePrivate* privateData = WEBKIT_WEB_FRAME_GET_PRIVATE(WEBKIT_WEB_FRAME(object));
92     privateData->frame->loader()->cancelAndClear();
93     g_free(privateData->name);
94     g_free(privateData->title);
95     g_free(privateData->uri);
96     delete privateData->frame;
97
98     G_OBJECT_CLASS(webkit_web_frame_parent_class)->finalize(object);
99 }
100
101 static void webkit_web_frame_class_init(WebKitWebFrameClass* frameClass)
102 {
103     webkit_init();
104
105     g_type_class_add_private(frameClass, sizeof(WebKitWebFramePrivate));
106
107     /*
108      * signals
109      */
110     webkit_web_frame_signals[CLEARED] = g_signal_new("cleared",
111             G_TYPE_FROM_CLASS(frameClass),
112             (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
113             0,
114             NULL,
115             NULL,
116             g_cclosure_marshal_VOID__VOID,
117             G_TYPE_NONE, 0);
118
119     webkit_web_frame_signals[LOAD_COMMITTED] = g_signal_new("load-committed",
120             G_TYPE_FROM_CLASS(frameClass),
121             (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
122             0,
123             NULL,
124             NULL,
125             g_cclosure_marshal_VOID__VOID,
126             G_TYPE_NONE, 0);
127
128     webkit_web_frame_signals[LOAD_DONE] = g_signal_new("load-done",
129             G_TYPE_FROM_CLASS(frameClass),
130             (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
131             0,
132             NULL,
133             NULL,
134             g_cclosure_marshal_VOID__BOOLEAN,
135             G_TYPE_NONE, 1,
136             G_TYPE_BOOLEAN);
137
138     webkit_web_frame_signals[TITLE_CHANGED] = g_signal_new("title-changed",
139             G_TYPE_FROM_CLASS(frameClass),
140             (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
141             0,
142             NULL,
143             NULL,
144             webkit_marshal_VOID__STRING,
145             G_TYPE_NONE, 1,
146             G_TYPE_STRING);
147
148     webkit_web_frame_signals[HOVERING_OVER_LINK] = g_signal_new("hovering-over-link",
149             G_TYPE_FROM_CLASS(frameClass),
150             (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
151             0,
152             NULL,
153             NULL,
154             webkit_marshal_VOID__STRING_STRING,
155             G_TYPE_NONE, 2,
156             G_TYPE_STRING, G_TYPE_STRING);
157
158     /*
159      * implementations of virtual methods
160      */
161     GObjectClass* objectClass = G_OBJECT_CLASS(frameClass);
162     objectClass->finalize = webkit_web_frame_finalize;
163     objectClass->get_property = webkit_web_frame_get_property;
164
165     /*
166      * properties
167      */
168     g_object_class_install_property(objectClass, PROP_NAME,
169                                     g_param_spec_string("name",
170                                                         "Name",
171                                                         "The name of the frame",
172                                                         NULL,
173                                                         WEBKIT_PARAM_READABLE));
174
175     g_object_class_install_property(objectClass, PROP_TITLE,
176                                     g_param_spec_string("title",
177                                                         "Title",
178                                                         "The document title of the frame",
179                                                         NULL,
180                                                         WEBKIT_PARAM_READABLE));
181
182     g_object_class_install_property(objectClass, PROP_URI,
183                                     g_param_spec_string("uri",
184                                                         "URI",
185                                                         "The current URI of the contents displayed by the frame",
186                                                         NULL,
187                                                         WEBKIT_PARAM_READABLE));
188 }
189
190 static void webkit_web_frame_init(WebKitWebFrame* frame)
191 {
192     // TODO: Move constructor code here.
193 }
194
195 /**
196  * webkit_web_frame_new:
197  * @web_view: the controlling #WebKitWebView
198  *
199  * Creates a new #WebKitWebFrame initialized with a controlling #WebKitWebView.
200  *
201  * Returns: a new #WebKitWebFrame
202  **/
203 WebKitWebFrame* webkit_web_frame_new(WebKitWebView* webView)
204 {
205     g_return_val_if_fail(WEBKIT_IS_WEB_VIEW(webView), NULL);
206
207     WebKitWebFrame* frame = WEBKIT_WEB_FRAME(g_object_new(WEBKIT_TYPE_WEB_FRAME, NULL));
208     WebKitWebFramePrivate* frameData = WEBKIT_WEB_FRAME_GET_PRIVATE(frame);
209     WebKitWebViewPrivate* webViewData = WEBKIT_WEB_VIEW_GET_PRIVATE(webView);
210
211     frameData->client = new WebKit::FrameLoaderClient(frame);
212     frameData->frame = new Frame(webViewData->corePage, 0, frameData->client);
213
214     FrameView* frameView = new FrameView(frameData->frame);
215     frameView->setContainingWindow(GTK_CONTAINER(webView));
216     frameView->setGtkAdjustments(GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0)),
217                                  GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0)));
218     frameData->frame->setView(frameView);
219     frameData->frame->init();
220     frameData->webView = webView;
221     frameData->name = 0;
222     frameData->title = 0;
223     frameData->uri = 0;
224
225     return frame;
226 }
227
228 WebKitWebFrame* webkit_web_frame_init_with_web_view(WebKitWebView* webView, HTMLFrameOwnerElement* element)
229 {
230     WebKitWebFrame* frame = WEBKIT_WEB_FRAME(g_object_new(WEBKIT_TYPE_WEB_FRAME, NULL));
231     WebKitWebFramePrivate* frameData = WEBKIT_WEB_FRAME_GET_PRIVATE(frame);
232     WebKitWebViewPrivate* webViewData = WEBKIT_WEB_VIEW_GET_PRIVATE(webView);
233
234     frameData->client = new WebKit::FrameLoaderClient(frame);
235     frameData->frame = new Frame(webViewData->corePage, element, frameData->client);
236
237     FrameView* frameView = new FrameView(frameData->frame);
238     frameView->setContainingWindow(GTK_CONTAINER(webView));
239     frameData->frame->setView(frameView);
240     frameView->deref();
241     frameData->frame->init();
242     frameData->webView = webView;
243
244     return frame;
245 }
246
247 /**
248  * webkit_web_frame_get_title:
249  * @frame: a #WebKitWebFrame
250  *
251  * Returns the @frame's document title
252  *
253  * Return value: the title of @frame
254  */
255 const gchar* webkit_web_frame_get_title(WebKitWebFrame* frame)
256 {
257     g_return_val_if_fail(WEBKIT_IS_WEB_FRAME(frame), NULL);
258
259     WebKitWebFramePrivate* frameData = WEBKIT_WEB_FRAME_GET_PRIVATE(frame);
260     return frameData->title;
261 }
262
263 /**
264  * webkit_web_frame_get_uri:
265  * @frame: a #WebKitWebFrame
266  *
267  * Returns the current URI of the contents displayed by the @frame
268  *
269  * Return value: the URI of @frame
270  */
271 const gchar* webkit_web_frame_get_uri(WebKitWebFrame* frame)
272 {
273     g_return_val_if_fail(WEBKIT_IS_WEB_FRAME(frame), NULL);
274
275     WebKitWebFramePrivate* frameData = WEBKIT_WEB_FRAME_GET_PRIVATE(frame);
276     return frameData->uri;
277 }
278
279 /**
280  * webkit_web_frame_get_web_view:
281  * @frame: a #WebKitWebFrame
282  *
283  * Returns the #WebKitWebView that manages this #WebKitWebFrame.
284  *
285  * The #WebKitWebView returned manages the entire hierarchy of #WebKitWebFrame
286  * objects that contains @frame.
287  *
288  * Return value: the #WebKitWebView that manages @frame
289  */
290 WebKitWebView* webkit_web_frame_get_web_view(WebKitWebFrame* frame)
291 {
292     g_return_val_if_fail(WEBKIT_IS_WEB_FRAME(frame), NULL);
293
294     WebKitWebFramePrivate* frameData = WEBKIT_WEB_FRAME_GET_PRIVATE(frame);
295     return frameData->webView;
296 }
297
298 /**
299  * webkit_web_frame_get_name:
300  * @frame: a #WebKitWebFrame
301  *
302  * Returns the @frame's name
303  *
304  * Return value: the name of @frame
305  */
306 const gchar* webkit_web_frame_get_name(WebKitWebFrame* frame)
307 {
308     g_return_val_if_fail(WEBKIT_IS_WEB_FRAME(frame), NULL);
309
310     WebKitWebFramePrivate* frameData = WEBKIT_WEB_FRAME_GET_PRIVATE(frame);
311
312     if (frameData->name)
313         return frameData->name;
314
315     Frame* coreFrame = core(frame);
316     g_return_val_if_fail(coreFrame, NULL);
317
318     String string = coreFrame->tree()->name();
319     frameData->name = g_strdup(string.utf8().data());
320
321     return frameData->name;
322 }
323
324 /**
325  * webkit_web_frame_get_parent:
326  * @frame: a #WebKitWebFrame
327  *
328  * Returns the @frame's parent frame, or %NULL if it has none.
329  *
330  * Return value: the parent #WebKitWebFrame or %NULL in case there is none
331  */
332 WebKitWebFrame* webkit_web_frame_get_parent(WebKitWebFrame* frame)
333 {
334     g_return_val_if_fail(WEBKIT_IS_WEB_FRAME(frame), NULL);
335
336     Frame* coreFrame = core(frame);
337     g_return_val_if_fail(coreFrame, NULL);
338
339     return kit(coreFrame->tree()->parent());
340 }
341
342 /**
343  * webkit_web_frame_load_request:
344  * @frame: a #WebKitWebFrame
345  * @request: a #WebKitNetworkRequest
346  *
347  * Connects to a given URI by initiating an asynchronous client request.
348  *
349  * Creates a provisional data source that will transition to a committed data
350  * source once any data has been received. Use webkit_web_frame_stop_loading() to
351  * stop the load. This function is typically invoked on the main frame.
352  */
353 void webkit_web_frame_load_request(WebKitWebFrame* frame, WebKitNetworkRequest* request)
354 {
355     g_return_if_fail(WEBKIT_IS_WEB_FRAME(frame));
356     g_return_if_fail(WEBKIT_IS_NETWORK_REQUEST(request));
357
358     Frame* coreFrame = core(frame);
359     g_return_if_fail(coreFrame);
360
361     // TODO: Use the ResourceRequest carried by WebKitNetworkRequest when it gets implemented.
362     DeprecatedString string = DeprecatedString::fromUtf8(webkit_network_request_get_uri(request));
363     coreFrame->loader()->load(ResourceRequest(KURL(string)));
364 }
365
366 /**
367  * webkit_web_frame_stop_loading:
368  * @frame: a #WebKitWebFrame
369  *
370  * Stops any pending loads on @frame's data source, and those of its children.
371  */
372 void webkit_web_frame_stop_loading(WebKitWebFrame* frame)
373 {
374     g_return_if_fail(WEBKIT_IS_WEB_FRAME(frame));
375
376     Frame* coreFrame = core(frame);
377     g_return_if_fail(coreFrame);
378
379     coreFrame->loader()->stopAllLoaders();
380 }
381
382 /**
383  * webkit_web_frame_reload:
384  * @frame: a #WebKitWebFrame
385  *
386  * Reloads the initial request.
387  */
388 void webkit_web_frame_reload(WebKitWebFrame* frame)
389 {
390     g_return_if_fail(WEBKIT_IS_WEB_FRAME(frame));
391
392     Frame* coreFrame = core(frame);
393     g_return_if_fail(coreFrame);
394
395     coreFrame->loader()->reload();
396 }
397
398 /**
399  * webkit_web_frame_find_frame:
400  * @frame: a #WebKitWebFrame
401  * @name: the name of the frame to be found
402  *
403  * For pre-defined names, returns @frame if @name is "_self" or "_current",
404  * returns @frame's parent frame if @name is "_parent", and returns the main
405  * frame if @name is "_top". Also returns @frame if it is the main frame and
406  * @name is either "_parent" or "_top". For other names, this function returns
407  * the first frame that matches @name. This function searches @frame and its
408  * descendents first, then @frame's parent and its children moving up the
409  * hierarchy until a match is found. If no match is found in @frame's
410  * hierarchy, this function will search for a matching frame in other main
411  * frame hierarchies. Returns %NULL if no match is found.
412  *
413  * Return value: the found #WebKitWebFrame or %NULL in case none is found
414  */
415 WebKitWebFrame* webkit_web_frame_find_frame(WebKitWebFrame* frame, const gchar* name)
416 {
417     g_return_val_if_fail(WEBKIT_IS_WEB_FRAME(frame), NULL);
418     g_return_val_if_fail(name, NULL);
419
420     Frame* coreFrame = core(frame);
421     g_return_val_if_fail(coreFrame, NULL);
422
423     String nameString = String::fromUTF8(name);
424     return kit(coreFrame->tree()->find(AtomicString(nameString)));
425 }
426
427 /**
428  * webkit_web_frame_get_global_context:
429  * @frame: a #WebKitWebFrame
430  *
431  * Gets the global JavaScript execution context. Use this function to bridge
432  * between the WebKit and JavaScriptCore APIs.
433  *
434  * Return value: the global JavaScript context
435  */
436 JSGlobalContextRef webkit_web_frame_get_global_context(WebKitWebFrame* frame)
437 {
438     g_return_val_if_fail(WEBKIT_IS_WEB_FRAME(frame), NULL);
439
440     Frame* coreFrame = core(frame);
441     g_return_val_if_fail(coreFrame, NULL);
442
443     return toGlobalRef(coreFrame->scriptProxy()->globalObject()->globalExec());
444 }
445
446 /**
447  * webkit_web_frame_get_children:
448  * @frame: a #WebKitWebFrame
449  *
450  * Return value: child frames of @frame
451  */
452 GSList* webkit_web_frame_get_children(WebKitWebFrame* frame)
453 {
454     g_return_val_if_fail(WEBKIT_IS_WEB_FRAME(frame), NULL);
455
456     GSList* children = NULL;
457     Frame* coreFrame = core(frame);
458
459     for (Frame* child = coreFrame->tree()->firstChild(); child; child = child->tree()->nextSibling()) {
460         FrameLoader* loader = child->loader();
461         WebKit::FrameLoaderClient* client = static_cast<WebKit::FrameLoaderClient*>(loader->client());
462         if (client)
463           children = g_slist_append(children, client->webFrame());
464     }
465
466     return children;
467 }
468
469 /**
470  * webkit_web_frame_get_inner_text:
471  * @frame: a #WebKitWebFrame
472  *
473  * Return value: inner text of @frame
474  */
475 gchar* webkit_web_frame_get_inner_text(WebKitWebFrame* frame)
476 {
477     g_return_val_if_fail(WEBKIT_IS_WEB_FRAME(frame), NULL);
478
479     Frame* coreFrame = core(frame);
480     FrameView* view = coreFrame->view();
481
482     if (view->layoutPending())
483         view->layout();
484
485     Element* documentElement = coreFrame->document()->documentElement();
486     String string =  documentElement->innerText();
487     return g_strdup(string.utf8().data());
488 }
489
490 #if GTK_CHECK_VERSION(2,10,0)
491
492 // This could be shared between ports once it's complete
493 class PrintContext
494 {
495 public:
496     PrintContext(Frame* frame)
497         : m_frame(frame)
498     {
499     }
500
501     ~PrintContext()
502     {
503         m_pageRects.clear();
504     }
505
506     int pageCount()
507     {
508         return m_pageRects.size();
509     }
510
511     void computePageRects(const FloatRect& printRect, float headerHeight, float footerHeight, float userScaleFactor, float& outPageHeight)
512     {
513         m_pageRects.clear();
514         outPageHeight = 0;
515
516         if (!m_frame->document() || !m_frame->view() || !m_frame->document()->renderer())
517             return;
518
519         RenderView* root = static_cast<RenderView*>(m_frame->document()->renderer());
520
521         if (!root) {
522             LOG_ERROR("document to be printed has no renderer");
523             return;
524         }
525
526         if (userScaleFactor <= 0) {
527             LOG_ERROR("userScaleFactor has bad value %.2f", userScaleFactor);
528             return;
529         }
530
531         float ratio = printRect.height() / printRect.width();
532
533         float pageWidth  = (float)root->docWidth();
534         float pageHeight = pageWidth * ratio;
535         outPageHeight = pageHeight;   // this is the height of the page adjusted by margins
536         pageHeight -= headerHeight + footerHeight;
537
538         if (pageHeight <= 0) {
539             LOG_ERROR("pageHeight has bad value %.2f", pageHeight);
540             return;
541         }
542
543         float currPageHeight = pageHeight / userScaleFactor;
544         float docHeight = root->layer()->height();
545         float currPageWidth = pageWidth / userScaleFactor;
546
547         // always return at least one page, since empty files should print a blank page
548         float printedPagesHeight = 0.0;
549         do {
550             float proposedBottom = min(docHeight, printedPagesHeight + pageHeight);
551             m_frame->adjustPageHeight(&proposedBottom, printedPagesHeight, proposedBottom, printedPagesHeight);
552             currPageHeight = max(1.0f, proposedBottom - printedPagesHeight);
553
554             m_pageRects.append(IntRect(0, (int)printedPagesHeight, (int)currPageWidth, (int)currPageHeight));
555             printedPagesHeight += currPageHeight;
556         } while (printedPagesHeight < docHeight);
557     }
558
559     // TODO: eliminate width param
560     void begin(float width)
561     {
562         // By imaging to a width a little wider than the available pixels,
563         // thin pages will be scaled down a little, matching the way they
564         // print in IE and Camino. This lets them use fewer sheets than they
565         // would otherwise, which is presumably why other browsers do this.
566         // Wide pages will be scaled down more than this.
567         const float PrintingMinimumShrinkFactor = 1.25f;
568
569         // This number determines how small we are willing to reduce the page content
570         // in order to accommodate the widest line. If the page would have to be
571         // reduced smaller to make the widest line fit, we just clip instead (this
572         // behavior matches MacIE and Mozilla, at least)
573         const float PrintingMaximumShrinkFactor = 2.0f;
574
575         float minLayoutWidth = width * PrintingMinimumShrinkFactor;
576         float maxLayoutWidth = width * PrintingMaximumShrinkFactor;
577
578         // FIXME: This will modify the rendering of the on-screen frame.
579         // Could lead to flicker during printing.
580         m_frame->setPrinting(true, minLayoutWidth, maxLayoutWidth, true);
581     }
582
583     // TODO: eliminate width param
584     void spoolPage(GraphicsContext& ctx, int pageNumber, float width)
585     {
586         IntRect pageRect = m_pageRects[pageNumber];
587         float scale = width / pageRect.width();
588
589         ctx.save();
590         ctx.scale(FloatSize(scale, scale));
591         ctx.translate(-pageRect.x(), -pageRect.y());
592         ctx.clip(pageRect);
593         m_frame->paint(&ctx, pageRect);
594         ctx.restore();
595     }
596
597     void end()
598     {
599         m_frame->setPrinting(false, 0, 0, true);
600     }
601
602 protected:
603     Frame* m_frame;
604     Vector<IntRect> m_pageRects;
605 };
606
607 static void begin_print(GtkPrintOperation* op, GtkPrintContext* context, gpointer user_data)
608 {
609     PrintContext* printContext = reinterpret_cast<PrintContext*>(user_data);
610
611     float width = gtk_print_context_get_width(context);
612     float height = gtk_print_context_get_height(context);
613     FloatRect printRect = FloatRect(0, 0, width, height);
614
615     printContext->begin(width);
616
617     // TODO: Margin adjustments and header/footer support
618     float headerHeight = 0;
619     float footerHeight = 0;
620     float pageHeight; // height of the page adjusted by margins
621     printContext->computePageRects(printRect, headerHeight, footerHeight, 1.0, pageHeight);
622     gtk_print_operation_set_n_pages(op, printContext->pageCount());
623 }
624
625 static void draw_page(GtkPrintOperation* op, GtkPrintContext* context, gint page_nr, gpointer user_data)
626 {
627     PrintContext* printContext = reinterpret_cast<PrintContext*>(user_data);
628
629     cairo_t* cr = gtk_print_context_get_cairo_context(context);
630     GraphicsContext ctx(cr);
631     float width = gtk_print_context_get_width(context);
632     printContext->spoolPage(ctx, page_nr, width);
633 }
634
635 static void end_print(GtkPrintOperation* op, GtkPrintContext* context, gpointer user_data)
636 {
637     PrintContext* printContext = reinterpret_cast<PrintContext*>(user_data);
638     printContext->end();
639 }
640
641 void webkit_web_frame_print(WebKitWebFrame* frame)
642 {
643     GtkWidget* topLevel = gtk_widget_get_toplevel(GTK_WIDGET(webkit_web_frame_get_web_view(frame)));
644     if (!GTK_WIDGET_TOPLEVEL(topLevel))
645         topLevel = NULL;
646
647     Frame* coreFrame = core(frame);
648     PrintContext printContext(coreFrame);
649
650     GtkPrintOperation* op = gtk_print_operation_new();
651     g_signal_connect(G_OBJECT(op), "begin-print", G_CALLBACK(begin_print), &printContext);
652     g_signal_connect(G_OBJECT(op), "draw-page", G_CALLBACK(draw_page), &printContext);
653     g_signal_connect(G_OBJECT(op), "end-print", G_CALLBACK(end_print), &printContext);
654     GError *error = NULL;
655     gtk_print_operation_run(op, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, GTK_WINDOW(topLevel), &error);
656     g_object_unref(op);
657
658     if (error) {
659         GtkWidget* dialog = gtk_message_dialog_new(GTK_WINDOW(topLevel),
660                                                    GTK_DIALOG_DESTROY_WITH_PARENT,
661                                                    GTK_MESSAGE_ERROR,
662                                                    GTK_BUTTONS_CLOSE,
663                                                    "%s", error->message);
664         g_error_free(error);
665
666         g_signal_connect(dialog, "response", G_CALLBACK(gtk_widget_destroy), NULL);
667         gtk_widget_show(dialog);
668     }
669 }
670
671 #else
672
673 void webkit_web_frame_print(WebKitWebFrame*)
674 {
675     g_warning("Printing support is not available in older versions of GTK+");
676 }
677
678 #endif
679
680 }