6b6f9c44d1b7e0d3fcd47ce28a445fbacc73cf62
[WebKit-https.git] / WebKit / gtk / Api / webkitgtkpage.cpp
1 /*
2  * Copyright (C) 2007 Holger Hans Peter Freyther
3  * Copyright (C) 2007 Christian Dywan <christian@twotoasts.de>
4  * Copyright (C) 2007 Xan Lopez <xan@gnome.org>
5  * Copyright (C) 2007 Alp Toker <alp@atoker.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1.  Redistributions of source code must retain the above copyright
12  *     notice, this list of conditions and the following disclaimer. 
13  * 2.  Redistributions in binary form must reproduce the above copyright
14  *     notice, this list of conditions and the following disclaimer in the
15  *     documentation and/or other materials provided with the distribution. 
16  * 3.  Neither the name of Apple, Inc. ("Apple") nor the names of
17  *     its contributors may be used to endorse or promote products derived
18  *     from this software without specific prior written permission. 
19  *
20  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
21  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
24  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 #include "config.h"
33
34 #include "webkitgtkpage.h"
35 #include "webkitgtk-marshal.h"
36 #include "webkitgtkprivate.h"
37
38 #include "NotImplemented.h"
39 #include "ChromeClientGtk.h"
40 #include "ContextMenuClientGtk.h"
41 #include "DragClientGtk.h"
42 #include "EditorClientGtk.h"
43 #include "EventHandler.h"
44 #include "HitTestRequest.h"
45 #include "HitTestResult.h"
46 #include "GraphicsContext.h"
47 #include "InspectorClientGtk.h"
48 #include "FrameLoader.h"
49 #include "FrameView.h"
50 #include "PlatformKeyboardEvent.h"
51 #include "PlatformWheelEvent.h"
52 #include "SubstituteData.h"
53
54 #include <gdk/gdkkeysyms.h>
55
56 using namespace WebKit;
57 using namespace WebCore;
58
59 extern "C" {
60
61 enum {
62     /* normal signals */
63     NAVIGATION_REQUESTED,
64     WINDOW_OBJECT_CLEARED,
65     LOAD_STARTED,
66     LOAD_PROGRESS_CHANGED,
67     LOAD_FINISHED,
68     TITLE_CHANGED,
69     HOVERING_OVER_LINK,
70     STATUS_BAR_TEXT_CHANGED,
71     ICOND_LOADED,
72     SELECTION_CHANGED,
73     CONSOLE_MESSAGE,
74     SCRIPT_ALERT,
75     SCRIPT_CONFIRM,
76     SCRIPT_PROMPT,
77     LAST_SIGNAL
78 };
79
80 static guint webkit_page_signals[LAST_SIGNAL] = { 0, };
81
82 G_DEFINE_TYPE(WebKitPage, webkit_page, GTK_TYPE_CONTAINER)
83
84 static gboolean webkit_page_expose_event(GtkWidget* widget, GdkEventExpose* event)
85 {
86     Frame* frame = core(getFrameFromPage(WEBKIT_PAGE(widget)));
87     GdkRectangle clip;
88     gdk_region_get_clipbox(event->region, &clip);
89     cairo_t* cr = gdk_cairo_create(event->window);
90     GraphicsContext ctx(cr);
91     ctx.setGdkExposeEvent(event);
92     if (frame->renderer()) {
93         frame->view()->layoutIfNeededRecursive();
94         frame->view()->paint(&ctx, clip);
95     }
96     cairo_destroy(cr);
97
98     return FALSE;
99 }
100
101 static gboolean webkit_page_key_event(GtkWidget* widget, GdkEventKey* event)
102 {
103     Frame* frame = core(getFrameFromPage(WEBKIT_PAGE(widget)));
104     PlatformKeyboardEvent keyboardEvent(event);
105
106     if (frame->eventHandler()->keyEvent(keyboardEvent))
107         return TRUE;
108
109     if (event->type != GDK_KEY_PRESS)
110         return FALSE;
111
112     FrameView* view = frame->view();
113
114     /* FIXME: at the very least we should be using the same code than the
115        Windows port here, but our ScrollView file diverges enough to make
116        that impossible. A short term solution would be to unify ScrollViewWin
117        and ScrollViewGtk. Long-term ScrollView and FrameView should be
118        unified and used everywhere for scrollbars */
119
120     switch (event->keyval) {
121     case (GDK_Down):
122         view->scrollBy(0, LINE_STEP);
123         return TRUE;
124     case (GDK_Up):
125         view->scrollBy(0, -LINE_STEP);
126         return TRUE;
127     case (GDK_Right):
128         view->scrollBy(LINE_STEP, 0);
129         return TRUE;
130     case (GDK_Left):
131         view->scrollBy(-LINE_STEP, 0);
132         return TRUE;
133     }
134
135     return FALSE;
136 }
137
138 static gboolean webkit_page_button_event(GtkWidget* widget, GdkEventButton* event)
139 {
140     Frame* frame = core(getFrameFromPage(WEBKIT_PAGE(widget)));
141
142     if (event->type == GDK_BUTTON_RELEASE)
143         return frame->eventHandler()->handleMouseReleaseEvent(PlatformMouseEvent(event));
144
145     // FIXME: need to keep track of subframe focus for key events
146     gtk_widget_grab_focus(GTK_WIDGET(widget));
147     return frame->eventHandler()->handleMousePressEvent(PlatformMouseEvent(event));
148 }
149
150 static gboolean webkit_page_motion_event(GtkWidget* widget, GdkEventMotion* event)
151 {
152     Frame* frame = core(getFrameFromPage(WEBKIT_PAGE(widget)));
153     return frame->eventHandler()->mouseMoved(PlatformMouseEvent(event));
154 }
155
156 static gboolean webkit_page_scroll_event(GtkWidget* widget, GdkEventScroll* event)
157 {
158     Frame* frame = core(getFrameFromPage(WEBKIT_PAGE(widget)));
159     PlatformWheelEvent wheelEvent(event);
160     return frame->eventHandler()->handleWheelEvent(wheelEvent);
161 }
162
163 static void webkit_page_size_allocate(GtkWidget* widget, GtkAllocation* allocation)
164 {
165     GTK_WIDGET_CLASS(webkit_page_parent_class)->size_allocate(widget,allocation);
166
167     Frame* frame = core(getFrameFromPage(WEBKIT_PAGE(widget)));
168     frame->view()->resize(allocation->width, allocation->height);
169     frame->forceLayout();
170     frame->view()->adjustViewSize();
171     frame->sendResizeEvent();
172 }
173
174 static void webkit_page_realize(GtkWidget* widget)
175 {
176     GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
177
178     GdkWindowAttr attributes;
179     attributes.window_type = GDK_WINDOW_CHILD;
180     attributes.x = widget->allocation.x;
181     attributes.y = widget->allocation.y;
182     attributes.width = widget->allocation.width;
183     attributes.height = widget->allocation.height;
184     attributes.wclass = GDK_INPUT_OUTPUT;
185     attributes.visual = gtk_widget_get_visual (widget);
186     attributes.colormap = gtk_widget_get_colormap (widget);
187     attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK
188                             | GDK_EXPOSURE_MASK
189                             | GDK_BUTTON_PRESS_MASK
190                             | GDK_BUTTON_RELEASE_MASK
191                             | GDK_POINTER_MOTION_MASK
192                             | GDK_KEY_PRESS_MASK
193                             | GDK_KEY_RELEASE_MASK
194                             | GDK_BUTTON_MOTION_MASK
195                             | GDK_BUTTON1_MOTION_MASK
196                             | GDK_BUTTON2_MOTION_MASK
197                             | GDK_BUTTON3_MOTION_MASK;
198
199     gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
200     widget->window = gdk_window_new(gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
201     gdk_window_set_user_data(widget->window, widget);
202 }
203
204 static void webkit_page_set_scroll_adjustments(WebKitPage* page, GtkAdjustment* hadj, GtkAdjustment* vadj)
205 {
206     FrameView* view = core(getFrameFromPage(page))->view();
207     view->setGtkAdjustments(hadj, vadj);
208 }
209
210 static void webkit_page_container_add(GtkContainer* container, GtkWidget* widget)
211 {
212     WebKitPage* page = WEBKIT_PAGE(container);
213     WebKitPagePrivate* private_data = WEBKIT_PAGE_GET_PRIVATE(page);
214
215     private_data->children.add(widget);
216     if (GTK_WIDGET_REALIZED(container))
217         gtk_widget_set_parent_window(widget, GTK_WIDGET(page)->window);
218     gtk_widget_set_parent(widget, GTK_WIDGET(container));
219 }
220
221 static void webkit_page_container_remove(GtkContainer* container, GtkWidget* widget)
222 {
223     WebKitPagePrivate* private_data = WEBKIT_PAGE_GET_PRIVATE(WEBKIT_PAGE(container));
224
225     if (private_data->children.contains(widget)) {
226         gtk_widget_unparent(widget);
227         private_data->children.remove(widget);
228     }
229 }
230
231 static void webkit_page_container_forall(GtkContainer* container, gboolean, GtkCallback callback, gpointer callbackData)
232 {
233     WebKitPagePrivate* privateData = WEBKIT_PAGE_GET_PRIVATE(WEBKIT_PAGE(container));
234
235     HashSet<GtkWidget*> children = privateData->children;
236     HashSet<GtkWidget*>::const_iterator end = children.end();
237     for (HashSet<GtkWidget*>::const_iterator current = children.begin(); current != end; ++current)
238         (*callback)(*current, callbackData);
239 }
240
241 static WebKitPage* webkit_page_real_create_page(WebKitPage*)
242 {
243     notImplemented();
244     return 0;
245 }
246
247 static WebKitNavigationResponse webkit_page_real_navigation_requested(WebKitPage*, WebKitFrame* frame, WebKitNetworkRequest*)
248 {
249     notImplemented();
250     return WEBKIT_NAVIGATION_RESPONSE_ACCEPT;
251 }
252
253 static void webkit_page_real_window_object_cleared(WebKitPage*, WebKitFrame*, JSGlobalContextRef context, JSObjectRef window_object)
254 {
255     notImplemented();
256 }
257
258 static gchar* webkit_page_real_choose_file(WebKitPage*, WebKitFrame*, const gchar* old_name)
259 {
260     notImplemented();
261     return g_strdup(old_name);
262 }
263
264 typedef enum {
265     WEBKIT_SCRIPT_DIALOG_ALERT,
266     WEBKIT_SCRIPT_DIALOG_CONFIRM,
267     WEBKIT_SCRIPT_DIALOG_PROMPT
268  } WebKitScriptDialogType;
269
270 static gboolean webkit_page_script_dialog(WebKitPage* page, WebKitFrame* frame, const gchar* message, WebKitScriptDialogType type, const gchar* defaultValue, gchar** value)
271 {
272     GtkMessageType messageType;
273     GtkButtonsType buttons;
274     gint defaultResponse;
275     GtkWidget* window;
276     GtkWidget* dialog;
277     GtkWidget* entry = 0;
278     gboolean didConfirm = FALSE;
279
280     switch (type) {
281     case WEBKIT_SCRIPT_DIALOG_ALERT:
282         messageType = GTK_MESSAGE_WARNING;
283         buttons = GTK_BUTTONS_CLOSE;
284         defaultResponse = GTK_RESPONSE_CLOSE;
285         break;
286     case WEBKIT_SCRIPT_DIALOG_CONFIRM:
287         messageType = GTK_MESSAGE_QUESTION;
288         buttons = GTK_BUTTONS_YES_NO;
289         defaultResponse = GTK_RESPONSE_YES;
290         break;
291     case WEBKIT_SCRIPT_DIALOG_PROMPT:
292         messageType = GTK_MESSAGE_QUESTION;
293         buttons = GTK_BUTTONS_OK_CANCEL;
294         defaultResponse = GTK_RESPONSE_OK;
295         break;
296     default:
297         g_warning("Unknown value for WebKitScriptDialogType.");
298         return FALSE;
299     }
300
301     window = gtk_widget_get_toplevel(GTK_WIDGET(page));
302     dialog = gtk_message_dialog_new(GTK_WIDGET_TOPLEVEL(window) ? GTK_WINDOW(window) : 0, GTK_DIALOG_DESTROY_WITH_PARENT, messageType, buttons, "%s", message);
303     gchar* title = g_strconcat("JavaScript - ", webkit_frame_get_location(frame), NULL);
304     gtk_window_set_title(GTK_WINDOW(dialog), title);
305     g_free(title);
306
307     if (type == WEBKIT_SCRIPT_DIALOG_PROMPT) {
308         entry = gtk_entry_new();
309         gtk_entry_set_text(GTK_ENTRY(entry), defaultValue);
310         gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), entry);
311         gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
312         gtk_widget_show(entry);
313     }
314
315     gtk_dialog_set_default_response(GTK_DIALOG(dialog), defaultResponse);
316     gint response = gtk_dialog_run(GTK_DIALOG(dialog));
317
318     switch (response) {
319     case GTK_RESPONSE_YES:
320         didConfirm = TRUE;
321         break;
322     case GTK_RESPONSE_OK:
323         didConfirm = TRUE;
324         if (entry)
325             *value = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
326         else
327             *value = 0;
328         break;
329     case GTK_RESPONSE_NO:
330     case GTK_RESPONSE_CANCEL:
331         didConfirm = FALSE;
332         break;
333
334     }
335     gtk_widget_destroy(GTK_WIDGET(dialog));
336     return didConfirm;
337 }
338
339 static gboolean webkit_page_real_script_alert(WebKitPage* page, WebKitFrame* frame, const gchar* message)
340 {
341     webkit_page_script_dialog(page, frame, message, WEBKIT_SCRIPT_DIALOG_ALERT, 0, 0);
342     return TRUE;
343 }
344
345 static gboolean webkit_page_real_script_confirm(WebKitPage* page, WebKitFrame* frame, const gchar* message, gboolean* didConfirm)
346 {
347     *didConfirm = webkit_page_script_dialog(page, frame, message, WEBKIT_SCRIPT_DIALOG_CONFIRM, 0, 0);
348     return TRUE;
349 }
350
351 static gboolean webkit_page_real_script_prompt(WebKitPage* page, WebKitFrame* frame, const gchar* message, const gchar* defaultValue, gchar** value)
352 {
353     if (!webkit_page_script_dialog(page, frame, message, WEBKIT_SCRIPT_DIALOG_PROMPT, defaultValue, value))
354         *value = NULL;
355     return TRUE;
356 }
357
358 static gboolean webkit_page_real_console_message(WebKitPage* page, const gchar* message, unsigned int line, const gchar* sourceId)
359 {
360     g_print("console message: %s @%d: %s\n", sourceId, line, message);
361     return TRUE;
362 }
363
364 static void webkit_page_finalize(GObject* object)
365 {
366     webkit_page_stop_loading(WEBKIT_PAGE(object));
367
368     WebKitPagePrivate* pageData = WEBKIT_PAGE_GET_PRIVATE(WEBKIT_PAGE(object));
369     delete pageData->page;
370     delete pageData->settings;
371     g_object_unref(pageData->mainFrame);
372     delete pageData->userAgent;
373
374     G_OBJECT_CLASS(webkit_page_parent_class)->finalize(object);
375 }
376
377 static void webkit_page_class_init(WebKitPageClass* pageClass)
378 {
379     g_type_class_add_private(pageClass, sizeof(WebKitPagePrivate));
380
381     /*
382      * Signals
383      */
384
385     webkit_page_signals[NAVIGATION_REQUESTED] = g_signal_new("navigation_requested",
386             G_TYPE_FROM_CLASS(pageClass),
387             (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
388             G_STRUCT_OFFSET (WebKitPageClass, navigation_requested),
389             NULL,
390             NULL,
391             webkit_marshal_INT__OBJECT_OBJECT,
392             G_TYPE_INT, 2,
393             G_TYPE_OBJECT,
394             G_TYPE_OBJECT);
395
396     /**
397      * WebKitPage::window-object-cleared:
398      * @page: the object on which the signal is emitted
399      * @frame: the #WebKitFrame to which @window_object belongs
400      * @context: the #JSGlobalContextRef holding the global object and other
401      * execution state; equivalent to the return value of
402      * webkit_frame_get_global_context(@frame)
403      *
404      * @window_object: the #JSObjectRef representing the frame's JavaScript
405      * window object
406      *
407      * Emitted when the JavaScript window object in a #WebKitFrame has been
408      * cleared in preparation for a new load. This is the preferred place to
409      * set custom properties on the window object using the JavaScriptCore API.
410      */
411     webkit_page_signals[WINDOW_OBJECT_CLEARED] = g_signal_new("window_object_cleared",
412             G_TYPE_FROM_CLASS(pageClass),
413             (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
414             G_STRUCT_OFFSET (WebKitPageClass, window_object_cleared),
415             NULL,
416             NULL,
417             webkit_marshal_VOID__OBJECT_POINTER_POINTER,
418             G_TYPE_NONE, 3,
419             WEBKIT_TYPE_FRAME,
420             G_TYPE_POINTER,
421             G_TYPE_POINTER);
422
423     /**
424      * WebKitPage::load-started:
425      *
426      * @page: the object on which the signal is emitted
427      * @frame: the frame going to do the load
428      *
429      * When a #WebKitFrame begins to load this signal is emitted.
430      */
431     webkit_page_signals[LOAD_STARTED] = g_signal_new("load_started",
432             G_TYPE_FROM_CLASS(pageClass),
433             (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
434             0,
435             NULL,
436             NULL,
437             g_cclosure_marshal_VOID__OBJECT,
438             G_TYPE_NONE, 1,
439             WEBKIT_TYPE_FRAME);
440
441     /**
442      * WebKitPage::load-progress-changed:
443      *
444      * @page: the #WebKitPage
445      * @progress: the global progress
446      */
447     webkit_page_signals[LOAD_PROGRESS_CHANGED] = g_signal_new("load_progress_changed",
448             G_TYPE_FROM_CLASS(pageClass),
449             (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
450             0,
451             NULL,
452             NULL,
453             g_cclosure_marshal_VOID__INT,
454             G_TYPE_NONE, 1,
455             G_TYPE_INT);
456
457     webkit_page_signals[LOAD_FINISHED] = g_signal_new("load_finished",
458             G_TYPE_FROM_CLASS(pageClass),
459             (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
460             0,
461             NULL,
462             NULL,
463             g_cclosure_marshal_VOID__OBJECT,
464             G_TYPE_NONE, 1,
465             WEBKIT_TYPE_FRAME);
466
467     webkit_page_signals[TITLE_CHANGED] = g_signal_new("title_changed",
468             G_TYPE_FROM_CLASS(pageClass),
469             (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
470             0,
471             NULL,
472             NULL,
473             webkit_marshal_VOID__STRING_STRING,
474             G_TYPE_NONE, 2,
475             G_TYPE_STRING, G_TYPE_STRING);
476
477     webkit_page_signals[HOVERING_OVER_LINK] = g_signal_new("hovering_over_link",
478             G_TYPE_FROM_CLASS(pageClass),
479             (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
480             0,
481             NULL,
482             NULL,
483             webkit_marshal_VOID__STRING_STRING,
484             G_TYPE_NONE, 2,
485             G_TYPE_STRING,
486             G_TYPE_STRING);
487
488     webkit_page_signals[STATUS_BAR_TEXT_CHANGED] = g_signal_new("status_bar_text_changed",
489             G_TYPE_FROM_CLASS(pageClass),
490             (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
491             0,
492             NULL,
493             NULL,
494             g_cclosure_marshal_VOID__STRING,
495             G_TYPE_NONE, 1,
496             G_TYPE_STRING);
497
498     webkit_page_signals[ICOND_LOADED] = g_signal_new("icon_loaded",
499             G_TYPE_FROM_CLASS(pageClass),
500             (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
501             0,
502             NULL,
503             NULL,
504             g_cclosure_marshal_VOID__VOID,
505             G_TYPE_NONE, 0);
506
507     webkit_page_signals[SELECTION_CHANGED] = g_signal_new("selection_changed",
508             G_TYPE_FROM_CLASS(pageClass),
509             (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
510             0,
511             NULL,
512             NULL,
513             g_cclosure_marshal_VOID__VOID,
514             G_TYPE_NONE, 0);
515
516     /**
517      * WebKitPage::console-message:
518      *
519      * @page: the object on which the signal is emitted
520      * @message: the message text
521      * @line: the line where the error occured
522      * @source_id: the source id
523      * @return: TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
524      *
525      * A JavaScript console message was created.
526      */
527     webkit_page_signals[CONSOLE_MESSAGE] = g_signal_new("console_message",
528             G_TYPE_FROM_CLASS(pageClass),
529             (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
530             G_STRUCT_OFFSET(WebKitPageClass, console_message),
531             g_signal_accumulator_true_handled,
532             NULL,
533             webkit_marshal_BOOLEAN__STRING_INT_STRING,
534             G_TYPE_BOOLEAN, 3,
535             G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
536
537     /**
538      * WebKitPage::script-alert:
539      *
540      * @page: the object on which the signal is emitted
541      * @frame: the relevant frame
542      * @message: the message text
543      * @return: TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
544      *
545      * A JavaScript alert dialog was created.
546      */
547     webkit_page_signals[SCRIPT_ALERT] = g_signal_new("script-alert",
548             G_TYPE_FROM_CLASS(pageClass),
549             (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
550             G_STRUCT_OFFSET(WebKitPageClass, script_alert),
551             g_signal_accumulator_true_handled,
552             NULL,
553             webkit_marshal_BOOLEAN__OBJECT_STRING,
554             G_TYPE_BOOLEAN, 2,
555             G_TYPE_OBJECT, G_TYPE_STRING);
556
557     /**
558      * WebKitPage::script-confirm:
559      *
560      * @page: the object on which the signal is emitted
561      * @frame: the relevant frame
562      * @message: the message text
563      * @confirmed: whether the dialog has been confirmed
564      * @return: TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
565      *
566      * A JavaScript confirm dialog was created, providing Yes and No buttons.
567      */
568     webkit_page_signals[SCRIPT_CONFIRM] = g_signal_new("script_confirm",
569             G_TYPE_FROM_CLASS(pageClass),
570             (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
571             G_STRUCT_OFFSET(WebKitPageClass, script_confirm),
572             g_signal_accumulator_true_handled,
573             NULL,
574             webkit_marshal_BOOLEAN__OBJECT_STRING_BOOLEAN,
575             G_TYPE_BOOLEAN, 3,
576             G_TYPE_OBJECT, G_TYPE_STRING, G_TYPE_BOOLEAN);
577
578     /**
579      * WebKitPage::script-prompt:
580      *
581      * @page: the object on which the signal is emitted
582      * @frame: the relevant frame
583      * @message: the message text
584      * @default: the default value
585      * @text: To be filled with the return value or NULL if the dialog was cancelled.
586      * @return: TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
587      *
588      * A JavaScript prompt dialog was created, providing an entry to input text.
589      */
590     webkit_page_signals[SCRIPT_PROMPT] = g_signal_new("script_prompt",
591             G_TYPE_FROM_CLASS(pageClass),
592             (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
593             G_STRUCT_OFFSET(WebKitPageClass, script_prompt),
594             g_signal_accumulator_true_handled,
595             NULL,
596             webkit_marshal_BOOLEAN__OBJECT_STRING_STRING_STRING,
597             G_TYPE_BOOLEAN, 4,
598             G_TYPE_OBJECT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER);
599
600
601     /*
602      * implementations of virtual methods
603      */
604     pageClass->create_page = webkit_page_real_create_page;
605     pageClass->navigation_requested = webkit_page_real_navigation_requested;
606     pageClass->window_object_cleared = webkit_page_real_window_object_cleared;
607     pageClass->choose_file = webkit_page_real_choose_file;
608     pageClass->script_alert = webkit_page_real_script_alert;
609     pageClass->script_confirm = webkit_page_real_script_confirm;
610     pageClass->script_prompt = webkit_page_real_script_prompt;
611     pageClass->console_message = webkit_page_real_console_message;
612
613     G_OBJECT_CLASS(pageClass)->finalize = webkit_page_finalize;
614
615     GtkWidgetClass* widgetClass = GTK_WIDGET_CLASS(pageClass);
616     widgetClass->realize = webkit_page_realize;
617     widgetClass->expose_event = webkit_page_expose_event;
618     widgetClass->key_press_event = webkit_page_key_event;
619     widgetClass->key_release_event = webkit_page_key_event;
620     widgetClass->button_press_event = webkit_page_button_event;
621     widgetClass->button_release_event = webkit_page_button_event;
622     widgetClass->motion_notify_event = webkit_page_motion_event;
623     widgetClass->scroll_event = webkit_page_scroll_event;
624     widgetClass->size_allocate = webkit_page_size_allocate;
625
626     GtkContainerClass* containerClass = GTK_CONTAINER_CLASS(pageClass);
627     containerClass->add = webkit_page_container_add;
628     containerClass->remove = webkit_page_container_remove;
629     containerClass->forall = webkit_page_container_forall;
630
631     /*
632      * make us scrollable (e.g. addable to a GtkScrolledWindow)
633      */
634     pageClass->set_scroll_adjustments = webkit_page_set_scroll_adjustments;
635     GTK_WIDGET_CLASS(pageClass)->set_scroll_adjustments_signal = g_signal_new("set_scroll_adjustments",
636             G_TYPE_FROM_CLASS(pageClass),
637             (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
638             G_STRUCT_OFFSET(WebKitPageClass, set_scroll_adjustments),
639             NULL, NULL,
640             webkit_marshal_VOID__OBJECT_OBJECT,
641             G_TYPE_NONE, 2,
642             GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
643 }
644
645 static void webkit_page_init(WebKitPage* page)
646 {
647     WebKitPagePrivate* pageData = WEBKIT_PAGE_GET_PRIVATE(WEBKIT_PAGE(page));
648     pageData->page = new Page(new WebKit::ChromeClient(page), new WebKit::ContextMenuClient, new WebKit::EditorClient(page), new WebKit::DragClient, new WebKit::InspectorClient);
649
650     Settings* settings = pageData->page->settings();
651     settings->setLoadsImagesAutomatically(true);
652     settings->setMinimumFontSize(5);
653     settings->setMinimumLogicalFontSize(5);
654     settings->setShouldPrintBackgrounds(true);
655     settings->setJavaScriptEnabled(true);
656     settings->setDefaultFixedFontSize(14);
657     settings->setDefaultFontSize(14);
658     settings->setSerifFontFamily("Times New Roman");
659     settings->setSansSerifFontFamily("Arial");
660     settings->setFixedFontFamily("Courier New");
661     settings->setStandardFontFamily("Arial");
662
663     GTK_WIDGET_SET_FLAGS(page, GTK_CAN_FOCUS);
664     pageData->mainFrame = WEBKIT_FRAME(webkit_frame_new(page));
665     pageData->editable = false;
666 }
667
668 GtkWidget* webkit_page_new(void)
669 {
670     WebKitPage* page = WEBKIT_PAGE(g_object_new(WEBKIT_TYPE_PAGE, NULL));
671
672     return GTK_WIDGET(page);
673 }
674
675 void webkit_page_set_settings(WebKitPage* page, WebKitSettings* settings)
676 {
677     g_return_if_fail(WEBKIT_IS_PAGE(page));
678     g_return_if_fail(settings);
679
680     notImplemented();
681 }
682
683 WebKitSettings* webkit_page_get_settings(WebKitPage* page)
684 {
685     g_return_val_if_fail(WEBKIT_IS_PAGE(page), NULL);
686
687     notImplemented();
688     return NULL;
689 }
690
691 void webkit_page_go_backward(WebKitPage* page)
692 {
693     g_return_if_fail(WEBKIT_IS_PAGE(page));
694
695     WebKitPagePrivate* pageData = WEBKIT_PAGE_GET_PRIVATE(page);
696     WebKitFramePrivate* frameData = WEBKIT_FRAME_GET_PRIVATE(pageData->mainFrame);
697     frameData->frame->loader()->goBackOrForward(-1);
698 }
699
700 void webkit_page_go_forward(WebKitPage* page)
701 {
702     g_return_if_fail(WEBKIT_IS_PAGE(page));
703
704     WebKitPagePrivate* pageData = WEBKIT_PAGE_GET_PRIVATE(page);
705     WebKitFramePrivate* frameData = WEBKIT_FRAME_GET_PRIVATE(pageData->mainFrame);
706     frameData->frame->loader()->goBackOrForward(1);
707 }
708
709 gboolean webkit_page_can_go_backward(WebKitPage* page)
710 {
711     g_return_val_if_fail(WEBKIT_IS_PAGE(page), FALSE);
712
713     WebKitPagePrivate* pageData = WEBKIT_PAGE_GET_PRIVATE(page);
714     WebKitFramePrivate* frameData = WEBKIT_FRAME_GET_PRIVATE(pageData->mainFrame);
715     return frameData->frame->loader()->canGoBackOrForward(-1);
716 }
717
718 gboolean webkit_page_can_go_forward(WebKitPage* page)
719 {
720     g_return_val_if_fail(WEBKIT_IS_PAGE(page), FALSE);
721
722     WebKitPagePrivate* pageData = WEBKIT_PAGE_GET_PRIVATE(page);
723     WebKitFramePrivate* frameData = WEBKIT_FRAME_GET_PRIVATE(pageData->mainFrame);
724     return frameData->frame->loader()->canGoBackOrForward(1);
725 }
726
727 void webkit_page_open(WebKitPage* page, const gchar* uri)
728 {
729     g_return_if_fail(WEBKIT_IS_PAGE(page));
730
731     WebKitPagePrivate* pageData = WEBKIT_PAGE_GET_PRIVATE(page);
732     WebKitFramePrivate* frameData = WEBKIT_FRAME_GET_PRIVATE(pageData->mainFrame);
733
734     DeprecatedString string = DeprecatedString::fromUtf8(uri);
735     frameData->frame->loader()->load(ResourceRequest(KURL(string)));
736 }
737
738 void webkit_page_reload(WebKitPage* page)
739 {
740     g_return_if_fail(WEBKIT_IS_PAGE(page));
741
742     WebKitPagePrivate* pageData = WEBKIT_PAGE_GET_PRIVATE(page);
743     WebKitFramePrivate* frameData = WEBKIT_FRAME_GET_PRIVATE(pageData->mainFrame);
744     frameData->frame->loader()->reload();
745 }
746
747 void webkit_page_load_string(WebKitPage* page, const gchar* content, const gchar* contentMimeType, const gchar* contentEncoding, const gchar* baseUri)
748 {
749     g_return_if_fail(WEBKIT_IS_PAGE(page));
750
751     WebKitPagePrivate* pageData = WEBKIT_PAGE_GET_PRIVATE(page);
752     WebKitFramePrivate* frameData = WEBKIT_FRAME_GET_PRIVATE(pageData->mainFrame);
753
754     KURL url(DeprecatedString::fromUtf8(baseUri));
755     RefPtr<SharedBuffer> sharedBuffer = new SharedBuffer(strdup(content), strlen(content));
756     SubstituteData substituteData(sharedBuffer.release(), String(contentMimeType), String(contentEncoding), KURL("about:blank"), url);
757
758     frameData->frame->loader()->load(ResourceRequest(url), substituteData);
759 }
760
761 void webkit_page_load_html_string(WebKitPage* page, const gchar* content, const gchar* baseUrl)
762 {
763     g_return_if_fail(WEBKIT_IS_PAGE(page));
764
765     webkit_page_load_string(page, content, "text/html", "UTF-8", baseUrl);
766 }
767
768 void webkit_page_stop_loading(WebKitPage* page)
769 {
770     g_return_if_fail(WEBKIT_IS_PAGE(page));
771
772     WebKitPagePrivate* pageData = WEBKIT_PAGE_GET_PRIVATE(page);
773     WebKitFramePrivate* frameData = WEBKIT_FRAME_GET_PRIVATE(pageData->mainFrame);
774
775     if (FrameLoader* loader = frameData->frame->loader())
776         loader->stopAllLoaders();
777 }
778
779 WebKitFrame* webkit_page_get_main_frame(WebKitPage* page)
780 {
781     g_return_val_if_fail(WEBKIT_IS_PAGE(page), NULL);
782
783     WebKitPagePrivate* pageData = WEBKIT_PAGE_GET_PRIVATE(page);
784     return pageData->mainFrame;
785 }
786
787 void webkit_page_execute_script(WebKitPage* page, const gchar* script)
788 {
789     g_return_if_fail(WEBKIT_IS_PAGE(page));
790     g_return_if_fail(script);
791
792     WebKitPagePrivate* pageData = WEBKIT_PAGE_GET_PRIVATE(page);
793     WebKitFramePrivate* frameData = WEBKIT_FRAME_GET_PRIVATE(pageData->mainFrame);
794
795     if (FrameLoader* loader = frameData->frame->loader())
796         loader->executeScript(String::fromUTF8(script), true);
797 }
798
799 /**
800  * webkit_page_get_editable:
801  * @page: a #WebKitPage
802  *
803  * Returns whether the user is allowed to edit the document.
804  *
805  * Returns %TRUE if @page allows the user to edit the HTML document, %FALSE if
806  * it doesn't. You can change @page's document programmatically regardless of
807  * this setting.
808  *
809  * Return value: a #gboolean indicating the editable state
810  */
811 gboolean webkit_page_get_editable(WebKitPage* page)
812 {
813     g_return_val_if_fail(WEBKIT_IS_PAGE(page), FALSE);
814
815     WebKitPagePrivate* pageData = WEBKIT_PAGE_GET_PRIVATE(page);
816     ASSERT(pageData);
817
818     return pageData->editable;
819 }
820
821 /**
822  * webkit_page_set_editable:
823  * @page: a #WebKitPage
824  * @flag: a #gboolean indicating the editable state
825  *
826  * Sets whether @page allows the user to edit its HTML document.
827  *
828  * If @flag is %TRUE, @page allows the user to edit the document. If @flag is
829  * %FALSE, an element in @page's document can only be edited if the
830  * CONTENTEDITABLE attribute has been set on the element or one of its parent
831  * elements. You can change @page's document programmatically regardless of
832  * this setting. By default a #WebKitPage is not editable.
833
834  * Normally, an HTML document is not editable unless the elements within the
835  * document are editable. This function provides a low-level way to make the
836  * contents of a #WebKitPage editable without altering the document or DOM
837  * structure.
838  */
839 void webkit_page_set_editable(WebKitPage* page, gboolean flag)
840 {
841     g_return_if_fail(WEBKIT_IS_PAGE(page));
842     flag = flag != FALSE;
843
844     WebKitPagePrivate* pageData = WEBKIT_PAGE_GET_PRIVATE(page);
845     ASSERT(pageData);
846
847     Frame* mainFrame = core(pageData->mainFrame);
848     g_return_if_fail(mainFrame);
849
850     // TODO: What happens when the frame is replaced?
851     if (flag == pageData->editable)
852         return;
853
854     pageData->editable = flag;
855
856     if (flag) {
857         mainFrame->applyEditingStyleToBodyElement();
858         // TODO: If the WebKitPage is made editable and the selection is empty, set it to something.
859         //if (!webkit_page_get_selected_dom_range(page))
860         //    mainFrame->setSelectionFromNone();
861     } else
862         mainFrame->removeEditingStyleFromBodyElement();
863 }
864
865 }