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