2007-12-06 Xan Lopez <xan@gnome.org>
[WebKit-https.git] / WebKit / gtk / WebView / webkitwebview.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 "webkitwebview.h"
35 #include "webkit-marshal.h"
36 #include "webkitprivate.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 "Editor.h"
51 #include "PlatformKeyboardEvent.h"
52 #include "PlatformWheelEvent.h"
53 #include "SubstituteData.h"
54
55 #include <gdk/gdkkeysyms.h>
56
57 using namespace WebKit;
58 using namespace WebCore;
59
60 extern "C" {
61
62 enum {
63     /* normal signals */
64     NAVIGATION_REQUESTED,
65     WINDOW_OBJECT_CLEARED,
66     LOAD_STARTED,
67     LOAD_PROGRESS_CHANGED,
68     LOAD_FINISHED,
69     TITLE_CHANGED,
70     HOVERING_OVER_LINK,
71     STATUS_BAR_TEXT_CHANGED,
72     ICOND_LOADED,
73     SELECTION_CHANGED,
74     CONSOLE_MESSAGE,
75     SCRIPT_ALERT,
76     SCRIPT_CONFIRM,
77     SCRIPT_PROMPT,
78     SELECT_ALL,
79     COPY_CLIPBOARD,
80     PASTE_CLIPBOARD,
81     CUT_CLIPBOARD,
82     LAST_SIGNAL
83 };
84
85 static guint webkit_web_view_signals[LAST_SIGNAL] = { 0, };
86
87 G_DEFINE_TYPE(WebKitWebView, webkit_web_view, GTK_TYPE_CONTAINER)
88
89 static gboolean webkit_web_view_expose_event(GtkWidget* widget, GdkEventExpose* event)
90 {
91     Frame* frame = core(getFrameFromView(WEBKIT_WEB_VIEW(widget)));
92     GdkRectangle clip;
93     gdk_region_get_clipbox(event->region, &clip);
94     cairo_t* cr = gdk_cairo_create(event->window);
95     GraphicsContext ctx(cr);
96     ctx.setGdkExposeEvent(event);
97     if (frame->renderer()) {
98         frame->view()->layoutIfNeededRecursive();
99         frame->view()->paint(&ctx, clip);
100     }
101     cairo_destroy(cr);
102
103     return FALSE;
104 }
105
106 static gboolean webkit_web_view_key_press_event(GtkWidget* widget, GdkEventKey* event)
107 {
108     Frame* frame = core(getFrameFromView(WEBKIT_WEB_VIEW(widget)));
109     PlatformKeyboardEvent keyboardEvent(event);
110
111     if (frame->eventHandler()->keyEvent(keyboardEvent))
112         return TRUE;
113
114     FrameView* view = frame->view();
115     SelectionController::EAlteration alteration;
116     if (event->state & GDK_SHIFT_MASK)
117         alteration = SelectionController::EXTEND;
118     else
119         alteration = SelectionController::MOVE;
120
121     // TODO: We probably want to use GTK+ key bindings here and perhaps take an
122     // approach more like the Win and Mac ports for key handling.
123     switch (event->keyval) {
124     case GDK_Down:
125         view->scrollBy(0, LINE_STEP);
126         return TRUE;
127     case GDK_Up:
128         view->scrollBy(0, -LINE_STEP);
129         return TRUE;
130     case GDK_Right:
131         view->scrollBy(LINE_STEP, 0);
132         return TRUE;
133     case GDK_Left:
134         view->scrollBy(-LINE_STEP, 0);
135         return TRUE;
136     case GDK_Home:
137         frame->selectionController()->modify(alteration, SelectionController::BACKWARD, DocumentBoundary, true);
138         return TRUE;
139     case GDK_End:
140         frame->selectionController()->modify(alteration, SelectionController::FORWARD, DocumentBoundary, true);
141         return TRUE;
142     }
143
144     /* Chain up to our parent class for binding activation */
145     return GTK_WIDGET_CLASS(webkit_web_view_parent_class)->key_press_event(widget, event);
146 }
147
148 static gboolean webkit_web_view_key_release_event(GtkWidget* widget, GdkEventKey* event)
149 {
150     Frame* frame = core(getFrameFromView(WEBKIT_WEB_VIEW(widget)));
151     PlatformKeyboardEvent keyboardEvent(event);
152
153     if (frame->eventHandler()->keyEvent(keyboardEvent))
154         return TRUE;
155
156     /* Chain up to our parent class for binding activation */
157     return GTK_WIDGET_CLASS(webkit_web_view_parent_class)->key_release_event(widget, event);
158 }
159
160 static gboolean webkit_web_view_button_press_event(GtkWidget* widget, GdkEventButton* event)
161 {
162     Frame* frame = core(getFrameFromView(WEBKIT_WEB_VIEW(widget)));
163
164     // FIXME: need to keep track of subframe focus for key events
165     gtk_widget_grab_focus(GTK_WIDGET(widget));
166     return frame->eventHandler()->handleMousePressEvent(PlatformMouseEvent(event));
167 }
168
169 static gboolean webkit_web_view_button_release_event(GtkWidget* widget, GdkEventButton* event)
170 {
171     Frame* frame = core(getFrameFromView(WEBKIT_WEB_VIEW(widget)));
172
173     return frame->eventHandler()->handleMouseReleaseEvent(PlatformMouseEvent(event));
174 }
175
176 static gboolean webkit_web_view_motion_event(GtkWidget* widget, GdkEventMotion* event)
177 {
178     Frame* frame = core(getFrameFromView(WEBKIT_WEB_VIEW(widget)));
179     return frame->eventHandler()->mouseMoved(PlatformMouseEvent(event));
180 }
181
182 static gboolean webkit_web_view_scroll_event(GtkWidget* widget, GdkEventScroll* event)
183 {
184     Frame* frame = core(getFrameFromView(WEBKIT_WEB_VIEW(widget)));
185     PlatformWheelEvent wheelEvent(event);
186     return frame->eventHandler()->handleWheelEvent(wheelEvent);
187 }
188
189 static void webkit_web_view_size_allocate(GtkWidget* widget, GtkAllocation* allocation)
190 {
191     GTK_WIDGET_CLASS(webkit_web_view_parent_class)->size_allocate(widget,allocation);
192
193     Frame* frame = core(getFrameFromView(WEBKIT_WEB_VIEW(widget)));
194     frame->view()->resize(allocation->width, allocation->height);
195     frame->forceLayout();
196     frame->view()->adjustViewSize();
197 }
198
199 static void webkit_web_view_realize(GtkWidget* widget)
200 {
201     GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
202
203     GdkWindowAttr attributes;
204     attributes.window_type = GDK_WINDOW_CHILD;
205     attributes.x = widget->allocation.x;
206     attributes.y = widget->allocation.y;
207     attributes.width = widget->allocation.width;
208     attributes.height = widget->allocation.height;
209     attributes.wclass = GDK_INPUT_OUTPUT;
210     attributes.visual = gtk_widget_get_visual (widget);
211     attributes.colormap = gtk_widget_get_colormap (widget);
212     attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK
213                             | GDK_EXPOSURE_MASK
214                             | GDK_BUTTON_PRESS_MASK
215                             | GDK_BUTTON_RELEASE_MASK
216                             | GDK_POINTER_MOTION_MASK
217                             | GDK_KEY_PRESS_MASK
218                             | GDK_KEY_RELEASE_MASK
219                             | GDK_BUTTON_MOTION_MASK
220                             | GDK_BUTTON1_MOTION_MASK
221                             | GDK_BUTTON2_MOTION_MASK
222                             | GDK_BUTTON3_MOTION_MASK;
223
224     gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
225     widget->window = gdk_window_new(gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
226     gdk_window_set_user_data(widget->window, widget);
227 }
228
229 static void webkit_web_view_set_scroll_adjustments(WebKitWebView* webView, GtkAdjustment* hadj, GtkAdjustment* vadj)
230 {
231     FrameView* view = core(getFrameFromView(webView))->view();
232     view->setGtkAdjustments(hadj, vadj);
233 }
234
235 static void webkit_web_view_container_add(GtkContainer* container, GtkWidget* widget)
236 {
237     WebKitWebView* webView = WEBKIT_WEB_VIEW(container);
238     WebKitWebViewPrivate* private_data = WEBKIT_WEB_VIEW_GET_PRIVATE(webView);
239
240     private_data->children.add(widget);
241     if (GTK_WIDGET_REALIZED(container))
242         gtk_widget_set_parent_window(widget, GTK_WIDGET(webView)->window);
243     gtk_widget_set_parent(widget, GTK_WIDGET(container));
244 }
245
246 static void webkit_web_view_container_remove(GtkContainer* container, GtkWidget* widget)
247 {
248     WebKitWebViewPrivate* private_data = WEBKIT_WEB_VIEW_GET_PRIVATE(WEBKIT_WEB_VIEW(container));
249
250     if (private_data->children.contains(widget)) {
251         gtk_widget_unparent(widget);
252         private_data->children.remove(widget);
253     }
254 }
255
256 static void webkit_web_view_container_forall(GtkContainer* container, gboolean, GtkCallback callback, gpointer callbackData)
257 {
258     WebKitWebViewPrivate* privateData = WEBKIT_WEB_VIEW_GET_PRIVATE(WEBKIT_WEB_VIEW(container));
259
260     HashSet<GtkWidget*> children = privateData->children;
261     HashSet<GtkWidget*>::const_iterator end = children.end();
262     for (HashSet<GtkWidget*>::const_iterator current = children.begin(); current != end; ++current)
263         (*callback)(*current, callbackData);
264 }
265
266 static WebKitWebView* webkit_web_view_real_create_web_view(WebKitWebView*)
267 {
268     notImplemented();
269     return 0;
270 }
271
272 static WebKitNavigationResponse webkit_web_view_real_navigation_requested(WebKitWebView*, WebKitWebFrame* frame, WebKitNetworkRequest*)
273 {
274     notImplemented();
275     return WEBKIT_NAVIGATION_RESPONSE_ACCEPT;
276 }
277
278 static void webkit_web_view_real_window_object_cleared(WebKitWebView*, WebKitWebFrame*, JSGlobalContextRef context, JSObjectRef window_object)
279 {
280     notImplemented();
281 }
282
283 static gchar* webkit_web_view_real_choose_file(WebKitWebView*, WebKitWebFrame*, const gchar* old_name)
284 {
285     notImplemented();
286     return g_strdup(old_name);
287 }
288
289 typedef enum {
290     WEBKIT_SCRIPT_DIALOG_ALERT,
291     WEBKIT_SCRIPT_DIALOG_CONFIRM,
292     WEBKIT_SCRIPT_DIALOG_PROMPT
293  } WebKitScriptDialogType;
294
295 static gboolean webkit_web_view_script_dialog(WebKitWebView* webView, WebKitWebFrame* frame, const gchar* message, WebKitScriptDialogType type, const gchar* defaultValue, gchar** value)
296 {
297     GtkMessageType messageType;
298     GtkButtonsType buttons;
299     gint defaultResponse;
300     GtkWidget* window;
301     GtkWidget* dialog;
302     GtkWidget* entry = 0;
303     gboolean didConfirm = FALSE;
304
305     switch (type) {
306     case WEBKIT_SCRIPT_DIALOG_ALERT:
307         messageType = GTK_MESSAGE_WARNING;
308         buttons = GTK_BUTTONS_CLOSE;
309         defaultResponse = GTK_RESPONSE_CLOSE;
310         break;
311     case WEBKIT_SCRIPT_DIALOG_CONFIRM:
312         messageType = GTK_MESSAGE_QUESTION;
313         buttons = GTK_BUTTONS_YES_NO;
314         defaultResponse = GTK_RESPONSE_YES;
315         break;
316     case WEBKIT_SCRIPT_DIALOG_PROMPT:
317         messageType = GTK_MESSAGE_QUESTION;
318         buttons = GTK_BUTTONS_OK_CANCEL;
319         defaultResponse = GTK_RESPONSE_OK;
320         break;
321     default:
322         g_warning("Unknown value for WebKitScriptDialogType.");
323         return FALSE;
324     }
325
326     window = gtk_widget_get_toplevel(GTK_WIDGET(webView));
327     dialog = gtk_message_dialog_new(GTK_WIDGET_TOPLEVEL(window) ? GTK_WINDOW(window) : 0, GTK_DIALOG_DESTROY_WITH_PARENT, messageType, buttons, "%s", message);
328     gchar* title = g_strconcat("JavaScript - ", webkit_web_frame_get_location(frame), NULL);
329     gtk_window_set_title(GTK_WINDOW(dialog), title);
330     g_free(title);
331
332     if (type == WEBKIT_SCRIPT_DIALOG_PROMPT) {
333         entry = gtk_entry_new();
334         gtk_entry_set_text(GTK_ENTRY(entry), defaultValue);
335         gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), entry);
336         gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
337         gtk_widget_show(entry);
338     }
339
340     gtk_dialog_set_default_response(GTK_DIALOG(dialog), defaultResponse);
341     gint response = gtk_dialog_run(GTK_DIALOG(dialog));
342
343     switch (response) {
344     case GTK_RESPONSE_YES:
345         didConfirm = TRUE;
346         break;
347     case GTK_RESPONSE_OK:
348         didConfirm = TRUE;
349         if (entry)
350             *value = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
351         else
352             *value = 0;
353         break;
354     case GTK_RESPONSE_NO:
355     case GTK_RESPONSE_CANCEL:
356         didConfirm = FALSE;
357         break;
358
359     }
360     gtk_widget_destroy(GTK_WIDGET(dialog));
361     return didConfirm;
362 }
363
364 static gboolean webkit_web_view_real_script_alert(WebKitWebView* webView, WebKitWebFrame* frame, const gchar* message)
365 {
366     webkit_web_view_script_dialog(webView, frame, message, WEBKIT_SCRIPT_DIALOG_ALERT, 0, 0);
367     return TRUE;
368 }
369
370 static gboolean webkit_web_view_real_script_confirm(WebKitWebView* webView, WebKitWebFrame* frame, const gchar* message, gboolean* didConfirm)
371 {
372     *didConfirm = webkit_web_view_script_dialog(webView, frame, message, WEBKIT_SCRIPT_DIALOG_CONFIRM, 0, 0);
373     return TRUE;
374 }
375
376 static gboolean webkit_web_view_real_script_prompt(WebKitWebView* webView, WebKitWebFrame* frame, const gchar* message, const gchar* defaultValue, gchar** value)
377 {
378     if (!webkit_web_view_script_dialog(webView, frame, message, WEBKIT_SCRIPT_DIALOG_PROMPT, defaultValue, value))
379         *value = NULL;
380     return TRUE;
381 }
382
383 static gboolean webkit_web_view_real_console_message(WebKitWebView* webView, const gchar* message, unsigned int line, const gchar* sourceId)
384 {
385     g_print("console message: %s @%d: %s\n", sourceId, line, message);
386     return TRUE;
387 }
388
389 static void webkit_web_view_real_select_all(WebKitWebView* webView)
390 {
391     Frame* frame = core(getFrameFromView(webView));
392     frame->editor()->execCommand("SelectAll");
393 }
394
395 static void webkit_web_view_real_cut_clipboard(WebKitWebView* webView)
396 {
397     Frame* frame = core(getFrameFromView(webView));
398     frame->editor()->execCommand("Cut");
399 }
400
401 static void webkit_web_view_real_copy_clipboard(WebKitWebView* webView)
402 {
403     Frame* frame = core(getFrameFromView(webView));
404     frame->editor()->execCommand("Copy");
405 }
406
407 static void webkit_web_view_real_paste_clipboard(WebKitWebView* webView)
408 {
409     Frame* frame = core(getFrameFromView(webView));
410     frame->editor()->execCommand("Paste");
411 }
412
413 static void webkit_web_view_finalize(GObject* object)
414 {
415     webkit_web_view_stop_loading(WEBKIT_WEB_VIEW(object));
416
417     WebKitWebViewPrivate* webViewData = WEBKIT_WEB_VIEW_GET_PRIVATE(WEBKIT_WEB_VIEW(object));
418     delete webViewData->corePage;
419     delete webViewData->settings;
420     g_object_unref(webViewData->mainFrame);
421     delete webViewData->userAgent;
422
423     G_OBJECT_CLASS(webkit_web_view_parent_class)->finalize(object);
424 }
425
426 static void webkit_web_view_class_init(WebKitWebViewClass* webViewClass)
427 {
428     GtkBindingSet* binding_set;
429
430     g_type_class_add_private(webViewClass, sizeof(WebKitWebViewPrivate));
431
432     /*
433      * Signals
434      */
435
436     webkit_web_view_signals[NAVIGATION_REQUESTED] = g_signal_new("navigation-requested",
437             G_TYPE_FROM_CLASS(webViewClass),
438             (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
439             G_STRUCT_OFFSET (WebKitWebViewClass, navigation_requested),
440             NULL,
441             NULL,
442             webkit_marshal_INT__OBJECT_OBJECT,
443             G_TYPE_INT, 2,
444             G_TYPE_OBJECT,
445             G_TYPE_OBJECT);
446
447     /**
448      * WebKitWebView::window-object-cleared:
449      * @web_view: the object on which the signal is emitted
450      * @frame: the #WebKitWebFrame to which @window_object belongs
451      * @context: the #JSGlobalContextRef holding the global object and other
452      * execution state; equivalent to the return value of
453      * webkit_web_frame_get_global_context(@frame)
454      *
455      * @window_object: the #JSObjectRef representing the frame's JavaScript
456      * window object
457      *
458      * Emitted when the JavaScript window object in a #WebKitWebFrame has been
459      * cleared in preparation for a new load. This is the preferred place to
460      * set custom properties on the window object using the JavaScriptCore API.
461      */
462     webkit_web_view_signals[WINDOW_OBJECT_CLEARED] = g_signal_new("window-object-cleared",
463             G_TYPE_FROM_CLASS(webViewClass),
464             (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
465             G_STRUCT_OFFSET (WebKitWebViewClass, window_object_cleared),
466             NULL,
467             NULL,
468             webkit_marshal_VOID__OBJECT_POINTER_POINTER,
469             G_TYPE_NONE, 3,
470             WEBKIT_TYPE_WEB_FRAME,
471             G_TYPE_POINTER,
472             G_TYPE_POINTER);
473
474     /**
475      * WebKitWebView::load-started:
476      * @web_view: the object on which the signal is emitted
477      * @frame: the frame going to do the load
478      *
479      * When a #WebKitWebFrame begins to load this signal is emitted.
480      */
481     webkit_web_view_signals[LOAD_STARTED] = g_signal_new("load-started",
482             G_TYPE_FROM_CLASS(webViewClass),
483             (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
484             0,
485             NULL,
486             NULL,
487             g_cclosure_marshal_VOID__OBJECT,
488             G_TYPE_NONE, 1,
489             WEBKIT_TYPE_WEB_FRAME);
490
491     /**
492      * WebKitWebView::load-progress-changed:
493      * @web_view: the #WebKitWebView
494      * @progress: the global progress
495      */
496     webkit_web_view_signals[LOAD_PROGRESS_CHANGED] = g_signal_new("load-progress-changed",
497             G_TYPE_FROM_CLASS(webViewClass),
498             (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
499             0,
500             NULL,
501             NULL,
502             g_cclosure_marshal_VOID__INT,
503             G_TYPE_NONE, 1,
504             G_TYPE_INT);
505
506     webkit_web_view_signals[LOAD_FINISHED] = g_signal_new("load-finished",
507             G_TYPE_FROM_CLASS(webViewClass),
508             (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
509             0,
510             NULL,
511             NULL,
512             g_cclosure_marshal_VOID__OBJECT,
513             G_TYPE_NONE, 1,
514             WEBKIT_TYPE_WEB_FRAME);
515
516     webkit_web_view_signals[TITLE_CHANGED] = g_signal_new("title-changed",
517             G_TYPE_FROM_CLASS(webViewClass),
518             (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
519             0,
520             NULL,
521             NULL,
522             webkit_marshal_VOID__STRING_STRING,
523             G_TYPE_NONE, 2,
524             G_TYPE_STRING, G_TYPE_STRING);
525
526     webkit_web_view_signals[HOVERING_OVER_LINK] = g_signal_new("hovering-over-link",
527             G_TYPE_FROM_CLASS(webViewClass),
528             (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
529             0,
530             NULL,
531             NULL,
532             webkit_marshal_VOID__STRING_STRING,
533             G_TYPE_NONE, 2,
534             G_TYPE_STRING,
535             G_TYPE_STRING);
536
537     webkit_web_view_signals[STATUS_BAR_TEXT_CHANGED] = g_signal_new("status-bar-text-changed",
538             G_TYPE_FROM_CLASS(webViewClass),
539             (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
540             0,
541             NULL,
542             NULL,
543             g_cclosure_marshal_VOID__STRING,
544             G_TYPE_NONE, 1,
545             G_TYPE_STRING);
546
547     webkit_web_view_signals[ICOND_LOADED] = g_signal_new("icon-loaded",
548             G_TYPE_FROM_CLASS(webViewClass),
549             (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
550             0,
551             NULL,
552             NULL,
553             g_cclosure_marshal_VOID__VOID,
554             G_TYPE_NONE, 0);
555
556     webkit_web_view_signals[SELECTION_CHANGED] = g_signal_new("selection-changed",
557             G_TYPE_FROM_CLASS(webViewClass),
558             (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
559             0,
560             NULL,
561             NULL,
562             g_cclosure_marshal_VOID__VOID,
563             G_TYPE_NONE, 0);
564
565     /**
566      * WebKitWebView::console-message:
567      * @web_view: the object on which the signal is emitted
568      * @message: the message text
569      * @line: the line where the error occured
570      * @source_id: the source id
571      * @return: TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
572      *
573      * A JavaScript console message was created.
574      */
575     webkit_web_view_signals[CONSOLE_MESSAGE] = g_signal_new("console-message",
576             G_TYPE_FROM_CLASS(webViewClass),
577             (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
578             G_STRUCT_OFFSET(WebKitWebViewClass, console_message),
579             g_signal_accumulator_true_handled,
580             NULL,
581             webkit_marshal_BOOLEAN__STRING_INT_STRING,
582             G_TYPE_BOOLEAN, 3,
583             G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
584
585     /**
586      * WebKitWebView::script-alert:
587      * @web_view: the object on which the signal is emitted
588      * @frame: the relevant frame
589      * @message: the message text
590      * @return: TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
591      *
592      * A JavaScript alert dialog was created.
593      */
594     webkit_web_view_signals[SCRIPT_ALERT] = g_signal_new("script-alert",
595             G_TYPE_FROM_CLASS(webViewClass),
596             (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
597             G_STRUCT_OFFSET(WebKitWebViewClass, script_alert),
598             g_signal_accumulator_true_handled,
599             NULL,
600             webkit_marshal_BOOLEAN__OBJECT_STRING,
601             G_TYPE_BOOLEAN, 2,
602             G_TYPE_OBJECT, G_TYPE_STRING);
603
604     /**
605      * WebKitWebView::script-confirm:
606      * @web_view: the object on which the signal is emitted
607      * @frame: the relevant frame
608      * @message: the message text
609      * @confirmed: whether the dialog has been confirmed
610      * @return: TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
611      *
612      * A JavaScript confirm dialog was created, providing Yes and No buttons.
613      */
614     webkit_web_view_signals[SCRIPT_CONFIRM] = g_signal_new("script-confirm",
615             G_TYPE_FROM_CLASS(webViewClass),
616             (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
617             G_STRUCT_OFFSET(WebKitWebViewClass, script_confirm),
618             g_signal_accumulator_true_handled,
619             NULL,
620             webkit_marshal_BOOLEAN__OBJECT_STRING_BOOLEAN,
621             G_TYPE_BOOLEAN, 3,
622             G_TYPE_OBJECT, G_TYPE_STRING, G_TYPE_BOOLEAN);
623
624     /**
625      * WebKitWebView::script-prompt:
626      * @web_view: the object on which the signal is emitted
627      * @frame: the relevant frame
628      * @message: the message text
629      * @default: the default value
630      * @text: To be filled with the return value or NULL if the dialog was cancelled.
631      * @return: TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
632      *
633      * A JavaScript prompt dialog was created, providing an entry to input text.
634      */
635     webkit_web_view_signals[SCRIPT_PROMPT] = g_signal_new("script-prompt",
636             G_TYPE_FROM_CLASS(webViewClass),
637             (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
638             G_STRUCT_OFFSET(WebKitWebViewClass, script_prompt),
639             g_signal_accumulator_true_handled,
640             NULL,
641             webkit_marshal_BOOLEAN__OBJECT_STRING_STRING_STRING,
642             G_TYPE_BOOLEAN, 4,
643             G_TYPE_OBJECT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER);
644
645     /**
646      * WebKitWebView::select-all:
647      * @web_view: the object which received the signal
648      *
649      * The ::select-all signal is a keybinding signal which gets emitted to
650      * select the complete contents of the text view.
651      *
652      * The default bindings for this signal is Ctrl-a.
653      */
654     webkit_web_view_signals[SELECT_ALL] = g_signal_new("select-all",
655             G_TYPE_FROM_CLASS(webViewClass),
656             (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
657             G_STRUCT_OFFSET(WebKitWebViewClass, select_all),
658             NULL, NULL,
659             g_cclosure_marshal_VOID__VOID,
660             G_TYPE_NONE, 0);
661
662     /**
663      * WebKitWebView::cut-clipboard:
664      * @web_view: the object which received the signal
665      *
666      * The ::cut-clipboard signal is a keybinding signal which gets emitted to
667      * cut the selection to the clipboard.
668      *
669      * The default bindings for this signal are Ctrl-x and Shift-Delete.
670      */
671     webkit_web_view_signals[CUT_CLIPBOARD] = g_signal_new("cut-clipboard",
672             G_TYPE_FROM_CLASS(webViewClass),
673             (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
674             G_STRUCT_OFFSET(WebKitWebViewClass, cut_clipboard),
675             NULL, NULL,
676             g_cclosure_marshal_VOID__VOID,
677             G_TYPE_NONE, 0);
678
679     /**
680      * WebKitWebView::copy-clipboard:
681      * @web_view: the object which received the signal
682      *
683      * The ::copy-clipboard signal is a keybinding signal which gets emitted to
684      * copy the selection to the clipboard.
685      *
686      * The default bindings for this signal are Ctrl-c and Ctrl-Insert.
687      */
688     webkit_web_view_signals[COPY_CLIPBOARD] = g_signal_new("copy-clipboard",
689             G_TYPE_FROM_CLASS(webViewClass),
690             (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
691             G_STRUCT_OFFSET(WebKitWebViewClass, copy_clipboard),
692             NULL, NULL,
693             g_cclosure_marshal_VOID__VOID,
694             G_TYPE_NONE, 0);
695
696     /**
697      * WebKitWebView::paste-clipboard:
698      * @web_view: the object which received the signal
699      *
700      * The ::paste-clipboard signal is a keybinding signal which gets emitted to
701      * paste the contents of the clipboard into the Web view.
702      *
703      * The default bindings for this signal are Ctrl-v and Shift-Insert.
704      */
705     webkit_web_view_signals[PASTE_CLIPBOARD] = g_signal_new("paste-clipboard",
706             G_TYPE_FROM_CLASS(webViewClass),
707             (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
708             G_STRUCT_OFFSET(WebKitWebViewClass, paste_clipboard),
709             NULL, NULL,
710             g_cclosure_marshal_VOID__VOID,
711             G_TYPE_NONE, 0);
712
713     /*
714      * implementations of virtual methods
715      */
716     webViewClass->create_web_view = webkit_web_view_real_create_web_view;
717     webViewClass->navigation_requested = webkit_web_view_real_navigation_requested;
718     webViewClass->window_object_cleared = webkit_web_view_real_window_object_cleared;
719     webViewClass->choose_file = webkit_web_view_real_choose_file;
720     webViewClass->script_alert = webkit_web_view_real_script_alert;
721     webViewClass->script_confirm = webkit_web_view_real_script_confirm;
722     webViewClass->script_prompt = webkit_web_view_real_script_prompt;
723     webViewClass->console_message = webkit_web_view_real_console_message;
724     webViewClass->select_all = webkit_web_view_real_select_all;
725     webViewClass->cut_clipboard = webkit_web_view_real_cut_clipboard;
726     webViewClass->copy_clipboard = webkit_web_view_real_copy_clipboard;
727     webViewClass->paste_clipboard = webkit_web_view_real_paste_clipboard;
728
729     G_OBJECT_CLASS(webViewClass)->finalize = webkit_web_view_finalize;
730
731     GtkWidgetClass* widgetClass = GTK_WIDGET_CLASS(webViewClass);
732     widgetClass->realize = webkit_web_view_realize;
733     widgetClass->expose_event = webkit_web_view_expose_event;
734     widgetClass->key_press_event = webkit_web_view_key_press_event;
735     widgetClass->key_release_event = webkit_web_view_key_release_event;
736     widgetClass->button_press_event = webkit_web_view_button_press_event;
737     widgetClass->button_release_event = webkit_web_view_button_release_event;
738     widgetClass->motion_notify_event = webkit_web_view_motion_event;
739     widgetClass->scroll_event = webkit_web_view_scroll_event;
740     widgetClass->size_allocate = webkit_web_view_size_allocate;
741
742     GtkContainerClass* containerClass = GTK_CONTAINER_CLASS(webViewClass);
743     containerClass->add = webkit_web_view_container_add;
744     containerClass->remove = webkit_web_view_container_remove;
745     containerClass->forall = webkit_web_view_container_forall;
746
747     /*
748      * make us scrollable (e.g. addable to a GtkScrolledWindow)
749      */
750     webViewClass->set_scroll_adjustments = webkit_web_view_set_scroll_adjustments;
751     GTK_WIDGET_CLASS(webViewClass)->set_scroll_adjustments_signal = g_signal_new("set-scroll-adjustments",
752             G_TYPE_FROM_CLASS(webViewClass),
753             (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
754             G_STRUCT_OFFSET(WebKitWebViewClass, set_scroll_adjustments),
755             NULL, NULL,
756             webkit_marshal_VOID__OBJECT_OBJECT,
757             G_TYPE_NONE, 2,
758             GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
759
760     /*
761      * Key bindings
762      */
763
764     binding_set = gtk_binding_set_by_class(webViewClass);
765
766     gtk_binding_entry_add_signal(binding_set, GDK_a, GDK_CONTROL_MASK,
767                                  "select_all", 0);
768
769     /* Cut/copy/paste */
770
771     gtk_binding_entry_add_signal(binding_set, GDK_x, GDK_CONTROL_MASK,
772                                  "cut_clipboard", 0);
773     gtk_binding_entry_add_signal(binding_set, GDK_c, GDK_CONTROL_MASK,
774                                  "copy_clipboard", 0);
775     gtk_binding_entry_add_signal(binding_set, GDK_v, GDK_CONTROL_MASK,
776                                  "paste_clipboard", 0);
777
778     gtk_binding_entry_add_signal(binding_set, GDK_Delete, GDK_SHIFT_MASK,
779                                  "cut_clipboard", 0);
780     gtk_binding_entry_add_signal(binding_set, GDK_Insert, GDK_CONTROL_MASK,
781                                  "copy_clipboard", 0);
782     gtk_binding_entry_add_signal(binding_set, GDK_Insert, GDK_SHIFT_MASK,
783                                  "paste_clipboard", 0);
784 }
785
786 static void webkit_web_view_init(WebKitWebView* webView)
787 {
788     WebKitWebViewPrivate* webViewData = WEBKIT_WEB_VIEW_GET_PRIVATE(WEBKIT_WEB_VIEW(webView));
789     webViewData->corePage = new Page(new WebKit::ChromeClient(webView), new WebKit::ContextMenuClient, new WebKit::EditorClient(webView), new WebKit::DragClient, new WebKit::InspectorClient);
790
791     Settings* settings = webViewData->corePage->settings();
792     settings->setLoadsImagesAutomatically(true);
793     settings->setMinimumFontSize(5);
794     settings->setDOMPasteAllowed(true);
795     settings->setMinimumLogicalFontSize(5);
796     settings->setShouldPrintBackgrounds(true);
797     settings->setJavaScriptEnabled(true);
798     settings->setDefaultFixedFontSize(14);
799     settings->setDefaultFontSize(14);
800     settings->setSerifFontFamily("Times New Roman");
801     settings->setSansSerifFontFamily("Arial");
802     settings->setFixedFontFamily("Courier New");
803     settings->setStandardFontFamily("Arial");
804
805     GTK_WIDGET_SET_FLAGS(webView, GTK_CAN_FOCUS);
806     webViewData->mainFrame = WEBKIT_WEB_FRAME(webkit_web_frame_new(webView));
807     webViewData->editable = false;
808 }
809
810 GtkWidget* webkit_web_view_new(void)
811 {
812     WebKitWebView* webView = WEBKIT_WEB_VIEW(g_object_new(WEBKIT_TYPE_WEB_VIEW, NULL));
813
814     return GTK_WIDGET(webView);
815 }
816
817 void webkit_web_view_set_settings(WebKitWebView* webView, WebKitSettings* settings)
818 {
819     g_return_if_fail(WEBKIT_IS_WEB_VIEW(webView));
820     g_return_if_fail(settings);
821
822     notImplemented();
823 }
824
825 WebKitSettings* webkit_web_view_get_settings(WebKitWebView* webView)
826 {
827     g_return_val_if_fail(WEBKIT_IS_WEB_VIEW(webView), NULL);
828
829     notImplemented();
830     return NULL;
831 }
832
833 void webkit_web_view_go_backward(WebKitWebView* webView)
834 {
835     g_return_if_fail(WEBKIT_IS_WEB_VIEW(webView));
836
837     WebKitWebViewPrivate* webViewData = WEBKIT_WEB_VIEW_GET_PRIVATE(webView);
838     WebKitWebFramePrivate* frameData = WEBKIT_WEB_FRAME_GET_PRIVATE(webViewData->mainFrame);
839     frameData->frame->loader()->goBackOrForward(-1);
840 }
841
842 void webkit_web_view_go_forward(WebKitWebView* webView)
843 {
844     g_return_if_fail(WEBKIT_IS_WEB_VIEW(webView));
845
846     WebKitWebViewPrivate* webViewData = WEBKIT_WEB_VIEW_GET_PRIVATE(webView);
847     WebKitWebFramePrivate* frameData = WEBKIT_WEB_FRAME_GET_PRIVATE(webViewData->mainFrame);
848     frameData->frame->loader()->goBackOrForward(1);
849 }
850
851 gboolean webkit_web_view_can_go_backward(WebKitWebView* webView)
852 {
853     g_return_val_if_fail(WEBKIT_IS_WEB_VIEW(webView), FALSE);
854
855     WebKitWebViewPrivate* webViewData = WEBKIT_WEB_VIEW_GET_PRIVATE(webView);
856     WebKitWebFramePrivate* frameData = WEBKIT_WEB_FRAME_GET_PRIVATE(webViewData->mainFrame);
857     return frameData->frame->loader()->canGoBackOrForward(-1);
858 }
859
860 gboolean webkit_web_view_can_go_forward(WebKitWebView* webView)
861 {
862     g_return_val_if_fail(WEBKIT_IS_WEB_VIEW(webView), FALSE);
863
864     WebKitWebViewPrivate* webViewData = WEBKIT_WEB_VIEW_GET_PRIVATE(webView);
865     WebKitWebFramePrivate* frameData = WEBKIT_WEB_FRAME_GET_PRIVATE(webViewData->mainFrame);
866     return frameData->frame->loader()->canGoBackOrForward(1);
867 }
868
869 void webkit_web_view_open(WebKitWebView* webView, const gchar* uri)
870 {
871     g_return_if_fail(WEBKIT_IS_WEB_VIEW(webView));
872
873     WebKitWebViewPrivate* webViewData = WEBKIT_WEB_VIEW_GET_PRIVATE(webView);
874     WebKitWebFramePrivate* frameData = WEBKIT_WEB_FRAME_GET_PRIVATE(webViewData->mainFrame);
875
876     DeprecatedString string = DeprecatedString::fromUtf8(uri);
877     frameData->frame->loader()->load(ResourceRequest(KURL(string)));
878 }
879
880 void webkit_web_view_reload(WebKitWebView* webView)
881 {
882     g_return_if_fail(WEBKIT_IS_WEB_VIEW(webView));
883
884     WebKitWebViewPrivate* webViewData = WEBKIT_WEB_VIEW_GET_PRIVATE(webView);
885     WebKitWebFramePrivate* frameData = WEBKIT_WEB_FRAME_GET_PRIVATE(webViewData->mainFrame);
886     frameData->frame->loader()->reload();
887 }
888
889 void webkit_web_view_load_string(WebKitWebView* webView, const gchar* content, const gchar* contentMimeType, const gchar* contentEncoding, const gchar* baseUri)
890 {
891     g_return_if_fail(WEBKIT_IS_WEB_VIEW(webView));
892
893     WebKitWebViewPrivate* webViewData = WEBKIT_WEB_VIEW_GET_PRIVATE(webView);
894     WebKitWebFramePrivate* frameData = WEBKIT_WEB_FRAME_GET_PRIVATE(webViewData->mainFrame);
895
896     KURL url(DeprecatedString::fromUtf8(baseUri));
897     RefPtr<SharedBuffer> sharedBuffer = new SharedBuffer(strdup(content), strlen(content));
898     SubstituteData substituteData(sharedBuffer.release(), String(contentMimeType), String(contentEncoding), KURL("about:blank"), url);
899
900     frameData->frame->loader()->load(ResourceRequest(url), substituteData);
901 }
902
903 void webkit_web_view_load_html_string(WebKitWebView* webView, const gchar* content, const gchar* baseUrl)
904 {
905     g_return_if_fail(WEBKIT_IS_WEB_VIEW(webView));
906
907     webkit_web_view_load_string(webView, content, "text/html", "UTF-8", baseUrl);
908 }
909
910 void webkit_web_view_stop_loading(WebKitWebView* webView)
911 {
912     g_return_if_fail(WEBKIT_IS_WEB_VIEW(webView));
913
914     WebKitWebViewPrivate* webViewData = WEBKIT_WEB_VIEW_GET_PRIVATE(webView);
915     WebKitWebFramePrivate* frameData = WEBKIT_WEB_FRAME_GET_PRIVATE(webViewData->mainFrame);
916
917     if (FrameLoader* loader = frameData->frame->loader())
918         loader->stopAllLoaders();
919 }
920
921 WebKitWebFrame* webkit_web_view_get_main_frame(WebKitWebView* webView)
922 {
923     g_return_val_if_fail(WEBKIT_IS_WEB_VIEW(webView), NULL);
924
925     WebKitWebViewPrivate* webViewData = WEBKIT_WEB_VIEW_GET_PRIVATE(webView);
926     return webViewData->mainFrame;
927 }
928
929 void webkit_web_view_execute_script(WebKitWebView* webView, const gchar* script)
930 {
931     g_return_if_fail(WEBKIT_IS_WEB_VIEW(webView));
932     g_return_if_fail(script);
933
934     WebKitWebViewPrivate* webViewData = WEBKIT_WEB_VIEW_GET_PRIVATE(webView);
935     WebKitWebFramePrivate* frameData = WEBKIT_WEB_FRAME_GET_PRIVATE(webViewData->mainFrame);
936
937     if (FrameLoader* loader = frameData->frame->loader())
938         loader->executeScript(String::fromUTF8(script), true);
939 }
940
941 /**
942  * webkit_web_view_get_editable:
943  * @web_view: a #WebKitWebView
944  *
945  * Returns whether the user is allowed to edit the document.
946  *
947  * Returns %TRUE if @web_view allows the user to edit the HTML document, %FALSE if
948  * it doesn't. You can change @web_view's document programmatically regardless of
949  * this setting.
950  *
951  * Return value: a #gboolean indicating the editable state
952  */
953 gboolean webkit_web_view_get_editable(WebKitWebView* webView)
954 {
955     g_return_val_if_fail(WEBKIT_IS_WEB_VIEW(webView), FALSE);
956
957     WebKitWebViewPrivate* webViewData = WEBKIT_WEB_VIEW_GET_PRIVATE(webView);
958     ASSERT(webViewData);
959
960     return webViewData->editable;
961 }
962
963 /**
964  * webkit_web_view_set_editable:
965  * @web_view: a #WebKitWebView
966  * @flag: a #gboolean indicating the editable state
967  *
968  * Sets whether @web_view allows the user to edit its HTML document.
969  *
970  * If @flag is %TRUE, @web_view allows the user to edit the document. If @flag is
971  * %FALSE, an element in @web_view's document can only be edited if the
972  * CONTENTEDITABLE attribute has been set on the element or one of its parent
973  * elements. You can change @web_view's document programmatically regardless of
974  * this setting. By default a #WebKitWebView is not editable.
975
976  * Normally, an HTML document is not editable unless the elements within the
977  * document are editable. This function provides a low-level way to make the
978  * contents of a #WebKitWebView editable without altering the document or DOM
979  * structure.
980  */
981 void webkit_web_view_set_editable(WebKitWebView* webView, gboolean flag)
982 {
983     g_return_if_fail(WEBKIT_IS_WEB_VIEW(webView));
984     flag = flag != FALSE;
985
986     WebKitWebViewPrivate* webViewData = WEBKIT_WEB_VIEW_GET_PRIVATE(webView);
987     ASSERT(webViewData);
988
989     Frame* mainFrame = core(webViewData->mainFrame);
990     g_return_if_fail(mainFrame);
991
992     // TODO: What happens when the frame is replaced?
993     if (flag == webViewData->editable)
994         return;
995
996     webViewData->editable = flag;
997
998     if (flag) {
999         mainFrame->applyEditingStyleToBodyElement();
1000         // TODO: If the WebKitWebView is made editable and the selection is empty, set it to something.
1001         //if (!webkit_web_view_get_selected_dom_range(webView))
1002         //    mainFrame->setSelectionFromNone();
1003     } else
1004         mainFrame->removeEditingStyleFromBodyElement();
1005 }
1006
1007 }