d4cce8a54dd347b1050377caea6b7950c804d46d
[WebKit-https.git] / WebKit / gtk / WebCoreSupport / ChromeClientGtk.cpp
1 /*
2  * Copyright (C) 2007, 2008 Holger Hans Peter Freyther
3  * Copyright (C) 2007, 2008 Christian Dywan <christian@imendio.com>
4  * Copyright (C) 2008 Nuanti Ltd.
5  * Copyright (C) 2008 Alp Toker <alp@atoker.com>
6  * Copyright (C) 2008 Gustavo Noronha Silva <gns@gnome.org>
7  *
8  *  This library is free software; you can redistribute it and/or
9  *  modify it under the terms of the GNU Lesser General Public
10  *  License as published by the Free Software Foundation; either
11  *  version 2 of the License, or (at your option) any later version.
12  *
13  *  This library is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  *  Lesser General Public License for more details.
17  *
18  *  You should have received a copy of the GNU Lesser General Public
19  *  License along with this library; if not, write to the Free Software
20  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21  */
22
23 #include "config.h"
24 #include "ChromeClientGtk.h"
25
26 #include "Console.h"
27 #include "FileSystem.h"
28 #include "FileChooser.h"
29 #include "FloatRect.h"
30 #include "FrameLoadRequest.h"
31 #include "IntRect.h"
32 #include "PlatformString.h"
33 #include "CString.h"
34 #include "HitTestResult.h"
35 #include "KURL.h"
36 #include "webkitwebview.h"
37 #include "webkitnetworkrequest.h"
38 #include "webkitprivate.h"
39 #include "NotImplemented.h"
40 #include "WindowFeatures.h"
41 #if ENABLE(DATABASE)
42 #include "DatabaseTracker.h"
43 #endif
44
45 #include <glib.h>
46 #include <glib/gi18n-lib.h>
47 #include <gtk/gtk.h>
48
49 using namespace WebCore;
50
51 namespace WebKit {
52
53 ChromeClient::ChromeClient(WebKitWebView* webView)
54     : m_webView(webView)
55 {
56     ASSERT(m_webView);
57 }
58
59 void ChromeClient::chromeDestroyed()
60 {
61     delete this;
62 }
63
64 FloatRect ChromeClient::windowRect()
65 {
66     GtkWidget* window = gtk_widget_get_toplevel(GTK_WIDGET(m_webView));
67 #if GTK_CHECK_VERSION(2, 18, 0)
68     if (gtk_widget_is_toplevel(window)) {
69 #else
70     if (GTK_WIDGET_TOPLEVEL(window)) {
71 #endif
72         gint left, top, width, height;
73         gtk_window_get_position(GTK_WINDOW(window), &left, &top);
74         gtk_window_get_size(GTK_WINDOW(window), &width, &height);
75         return IntRect(left, top, width, height);
76     }
77     return FloatRect();
78 }
79
80 void ChromeClient::setWindowRect(const FloatRect& rect)
81 {
82     IntRect intrect = IntRect(rect);
83     WebKitWebWindowFeatures* webWindowFeatures = webkit_web_view_get_window_features(m_webView);
84
85     g_object_set(webWindowFeatures,
86                  "x", intrect.x(),
87                  "y", intrect.y(),
88                  "width", intrect.width(),
89                  "height", intrect.height(),
90                  NULL);
91
92     gboolean autoResizeWindow;
93     WebKitWebSettings* settings = webkit_web_view_get_settings(m_webView);
94     g_object_get(settings, "auto-resize-window", &autoResizeWindow, NULL);
95
96     if (!autoResizeWindow)
97         return;
98
99     GtkWidget* window = gtk_widget_get_toplevel(GTK_WIDGET(m_webView));
100 #if GTK_CHECK_VERSION(2, 18, 0)
101     if (gtk_widget_is_toplevel(window)) {
102 #else
103     if (GTK_WIDGET_TOPLEVEL(window)) {
104 #endif
105         gtk_window_move(GTK_WINDOW(window), intrect.x(), intrect.y());
106         gtk_window_resize(GTK_WINDOW(window), intrect.width(), intrect.height());
107     }
108 }
109
110 FloatRect ChromeClient::pageRect()
111 {
112     GtkAllocation allocation = GTK_WIDGET(m_webView)->allocation;
113     return IntRect(allocation.x, allocation.y, allocation.width, allocation.height);
114 }
115
116 float ChromeClient::scaleFactor()
117 {
118     // Not implementable
119     return 1.0;
120 }
121
122 void ChromeClient::focus()
123 {
124     gtk_widget_grab_focus(GTK_WIDGET(m_webView));
125 }
126
127 void ChromeClient::unfocus()
128 {
129     GtkWidget* window = gtk_widget_get_toplevel(GTK_WIDGET(m_webView));
130 #if GTK_CHECK_VERSION(2, 18, 0)
131     if (gtk_widget_is_toplevel(window))
132 #else
133     if (GTK_WIDGET_TOPLEVEL(window))
134 #endif
135         gtk_window_set_focus(GTK_WINDOW(window), NULL);
136 }
137
138 Page* ChromeClient::createWindow(Frame* frame, const FrameLoadRequest& frameLoadRequest, const WindowFeatures& coreFeatures)
139 {
140     WebKitWebView* webView = 0;
141
142     g_signal_emit_by_name(m_webView, "create-web-view", kit(frame), &webView);
143
144     if (!webView)
145         return 0;
146
147     WebKitWebWindowFeatures* webWindowFeatures = webkit_web_window_features_new_from_core_features(coreFeatures);
148     g_object_set(webView, "window-features", webWindowFeatures, NULL);
149     g_object_unref(webWindowFeatures);
150
151     if (!frameLoadRequest.isEmpty())
152         webkit_web_view_open(webView, frameLoadRequest.resourceRequest().url().string().utf8().data());
153
154     return core(webView);
155 }
156
157 void ChromeClient::show()
158 {
159     webkit_web_view_notify_ready(m_webView);
160 }
161
162 bool ChromeClient::canRunModal()
163 {
164     notImplemented();
165     return false;
166 }
167
168 void ChromeClient::runModal()
169 {
170     notImplemented();
171 }
172
173 void ChromeClient::setToolbarsVisible(bool visible)
174 {
175     WebKitWebWindowFeatures* webWindowFeatures = webkit_web_view_get_window_features(m_webView);
176
177     g_object_set(webWindowFeatures, "toolbar-visible", visible, NULL);
178 }
179
180 bool ChromeClient::toolbarsVisible()
181 {
182     WebKitWebWindowFeatures* webWindowFeatures = webkit_web_view_get_window_features(m_webView);
183     gboolean visible;
184
185     g_object_get(webWindowFeatures, "toolbar-visible", &visible, NULL);
186     return visible;
187 }
188
189 void ChromeClient::setStatusbarVisible(bool visible)
190 {
191     WebKitWebWindowFeatures* webWindowFeatures = webkit_web_view_get_window_features(m_webView);
192
193     g_object_set(webWindowFeatures, "statusbar-visible", visible, NULL);
194 }
195
196 bool ChromeClient::statusbarVisible()
197 {
198     WebKitWebWindowFeatures* webWindowFeatures = webkit_web_view_get_window_features(m_webView);
199     gboolean visible;
200
201     g_object_get(webWindowFeatures, "statusbar-visible", &visible, NULL);
202     return visible;
203 }
204
205 void ChromeClient::setScrollbarsVisible(bool visible)
206 {
207     WebKitWebWindowFeatures* webWindowFeatures = webkit_web_view_get_window_features(m_webView);
208
209     g_object_set(webWindowFeatures, "scrollbar-visible", visible, NULL);
210 }
211
212 bool ChromeClient::scrollbarsVisible() {
213     WebKitWebWindowFeatures* webWindowFeatures = webkit_web_view_get_window_features(m_webView);
214     gboolean visible;
215
216     g_object_get(webWindowFeatures, "scrollbar-visible", &visible, NULL);
217     return visible;
218 }
219
220 void ChromeClient::setMenubarVisible(bool visible)
221 {
222     WebKitWebWindowFeatures* webWindowFeatures = webkit_web_view_get_window_features(m_webView);
223
224     g_object_set(webWindowFeatures, "menubar-visible", visible, NULL);
225 }
226
227 bool ChromeClient::menubarVisible()
228 {
229     WebKitWebWindowFeatures* webWindowFeatures = webkit_web_view_get_window_features(m_webView);
230     gboolean visible;
231
232     g_object_get(webWindowFeatures, "menubar-visible", &visible, NULL);
233     return visible;
234 }
235
236 void ChromeClient::setResizable(bool)
237 {
238     // Ignored for now
239 }
240
241 void ChromeClient::closeWindowSoon()
242 {
243     // We may not have a WebView as create-web-view can return NULL.
244     if (!m_webView)
245         return;
246
247     webkit_web_view_stop_loading(m_webView);
248
249     gboolean isHandled = false;
250     g_signal_emit_by_name(m_webView, "close-web-view", &isHandled);
251
252     if (isHandled)
253         return;
254
255     // FIXME: should we clear the frame group name here explicitly? Mac does it.
256     // But this gets cleared in Page's destructor anyway.
257     // webkit_web_view_set_group_name(m_webView, "");
258 }
259
260 bool ChromeClient::canTakeFocus(FocusDirection)
261 {
262 #if GTK_CHECK_VERSION(2, 18, 0)
263     return gtk_widget_get_can_focus(GTK_WIDGET(m_webView));
264 #else
265     return GTK_WIDGET_CAN_FOCUS(m_webView);
266 #endif
267 }
268
269 void ChromeClient::takeFocus(FocusDirection)
270 {
271     unfocus();
272 }
273
274 void ChromeClient::focusedNodeChanged(Node*)
275 {
276 }
277
278 bool ChromeClient::canRunBeforeUnloadConfirmPanel()
279 {
280     return true;
281 }
282
283 bool ChromeClient::runBeforeUnloadConfirmPanel(const WebCore::String& message, WebCore::Frame* frame)
284 {
285     return runJavaScriptConfirm(frame, message);
286 }
287
288 void ChromeClient::addMessageToConsole(WebCore::MessageSource source, WebCore::MessageType type, WebCore::MessageLevel level, const WebCore::String& message, unsigned int lineNumber, const WebCore::String& sourceId)
289 {
290     gboolean retval;
291     g_signal_emit_by_name(m_webView, "console-message", message.utf8().data(), lineNumber, sourceId.utf8().data(), &retval);
292 }
293
294 void ChromeClient::runJavaScriptAlert(Frame* frame, const String& message)
295 {
296     gboolean retval;
297     g_signal_emit_by_name(m_webView, "script-alert", kit(frame), message.utf8().data(), &retval);
298 }
299
300 bool ChromeClient::runJavaScriptConfirm(Frame* frame, const String& message)
301 {
302     gboolean retval;
303     gboolean didConfirm;
304     g_signal_emit_by_name(m_webView, "script-confirm", kit(frame), message.utf8().data(), &didConfirm, &retval);
305     return didConfirm == TRUE;
306 }
307
308 bool ChromeClient::runJavaScriptPrompt(Frame* frame, const String& message, const String& defaultValue, String& result)
309 {
310     gboolean retval;
311     gchar* value = 0;
312     g_signal_emit_by_name(m_webView, "script-prompt", kit(frame), message.utf8().data(), defaultValue.utf8().data(), &value, &retval);
313     if (value) {
314         result = String::fromUTF8(value);
315         g_free(value);
316         return true;
317     }
318     return false;
319 }
320
321 void ChromeClient::setStatusbarText(const String& string)
322 {
323     CString stringMessage = string.utf8();
324     g_signal_emit_by_name(m_webView, "status-bar-text-changed", stringMessage.data());
325 }
326
327 bool ChromeClient::shouldInterruptJavaScript()
328 {
329     notImplemented();
330     return false;
331 }
332
333 bool ChromeClient::tabsToLinks() const
334 {
335     return true;
336 }
337
338 IntRect ChromeClient::windowResizerRect() const
339 {
340     notImplemented();
341     return IntRect();
342 }
343
344 void ChromeClient::repaint(const IntRect& windowRect, bool contentChanged, bool immediate, bool repaintContentOnly)
345 {
346     GdkRectangle rect = windowRect;
347     GdkWindow* window = GTK_WIDGET(m_webView)->window;
348
349     if (window) {
350         if (contentChanged)
351             gdk_window_invalidate_rect(window, &rect, FALSE);
352         // We don't currently do immediate updates since they delay other UI elements.
353         //if (immediate)
354         //    gdk_window_process_updates(window, FALSE);
355     }
356 }
357
358 void ChromeClient::scroll(const IntSize& delta, const IntRect& rectToScroll, const IntRect& clipRect)
359 {
360     GdkWindow* window = GTK_WIDGET(m_webView)->window;
361     if (!window)
362         return;
363
364     // We cannot use gdk_window_scroll here because it is only able to
365     // scroll the whole window at once, and we often need to scroll
366     // portions of the window only (think frames).
367     GdkRectangle area = clipRect;
368     GdkRectangle moveRect;
369
370     GdkRectangle sourceRect = area;
371     sourceRect.x -= delta.width();
372     sourceRect.y -= delta.height();
373
374     GdkRegion* invalidRegion = gdk_region_rectangle(&area);
375
376     if (gdk_rectangle_intersect(&area, &sourceRect, &moveRect)) {
377         GdkRegion* moveRegion = gdk_region_rectangle(&moveRect);
378         gdk_window_move_region(window, moveRegion, delta.width(), delta.height());
379         gdk_region_offset(moveRegion, delta.width(), delta.height());
380         gdk_region_subtract(invalidRegion, moveRegion);
381         gdk_region_destroy(moveRegion);
382     }
383
384     gdk_window_invalidate_region(window, invalidRegion, FALSE);
385     gdk_region_destroy(invalidRegion);
386 }
387
388 // FIXME: this does not take into account the WM decorations
389 static IntPoint widgetScreenPosition(GtkWidget* widget)
390 {
391     GtkWidget* window = gtk_widget_get_toplevel(widget);
392     int widgetX = 0, widgetY = 0;
393
394     gtk_widget_translate_coordinates(widget, window, 0, 0, &widgetX, &widgetY);
395
396     IntPoint result(widgetX, widgetY);
397     int originX, originY;
398     gdk_window_get_origin(window->window, &originX, &originY);
399     result.move(originX, originY);
400
401     return result;
402 }
403
404 IntRect ChromeClient::windowToScreen(const IntRect& rect) const
405 {
406     IntRect result(rect);
407     IntPoint screenPosition = widgetScreenPosition(GTK_WIDGET(m_webView));
408     result.move(screenPosition.x(), screenPosition.y());
409
410     return result;
411 }
412
413 IntPoint ChromeClient::screenToWindow(const IntPoint& point) const
414 {
415     IntPoint result(point);
416     IntPoint screenPosition = widgetScreenPosition(GTK_WIDGET(m_webView));
417     result.move(-screenPosition.x(), -screenPosition.y());
418
419     return result;
420 }
421
422 PlatformPageClient ChromeClient::platformPageClient() const
423 {
424     return GTK_WIDGET(m_webView);
425 }
426
427 void ChromeClient::contentsSizeChanged(Frame* frame, const IntSize& size) const
428 {
429     // We need to queue a resize request only if the size changed,
430     // otherwise we get into an infinite loop!
431     GtkWidget* widget = GTK_WIDGET(m_webView);
432     if (GTK_WIDGET_REALIZED(widget)
433         && (widget->requisition.height != size.height())
434         || (widget->requisition.width != size.width()))
435         gtk_widget_queue_resize_no_redraw(widget);
436 }
437
438 void ChromeClient::scrollbarsModeDidChange() const
439 {
440     WebKitWebFrame* webFrame = webkit_web_view_get_main_frame(m_webView);
441
442     g_object_notify(G_OBJECT(webFrame), "horizontal-scrollbar-policy");
443     g_object_notify(G_OBJECT(webFrame), "vertical-scrollbar-policy");
444
445     gboolean isHandled;
446     g_signal_emit_by_name(webFrame, "scrollbars-policy-changed", &isHandled);
447
448     if (isHandled)
449         return;
450
451     GtkWidget* parent = gtk_widget_get_parent(GTK_WIDGET(m_webView));
452     if (!parent || !GTK_IS_SCROLLED_WINDOW(parent))
453         return;
454
455     GtkPolicyType horizontalPolicy = webkit_web_frame_get_horizontal_scrollbar_policy(webFrame);
456     GtkPolicyType verticalPolicy = webkit_web_frame_get_vertical_scrollbar_policy(webFrame);
457
458     // ScrolledWindow doesn't like to display only part of a widget if
459     // the scrollbars are completely disabled; We have a disparity
460     // here on what the policy requested by the web app is and what we
461     // can represent; the idea is not to show scrollbars, only.
462     if (horizontalPolicy == GTK_POLICY_NEVER)
463         horizontalPolicy = GTK_POLICY_AUTOMATIC;
464
465     if (verticalPolicy == GTK_POLICY_NEVER)
466         verticalPolicy = GTK_POLICY_AUTOMATIC;
467
468     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(parent),
469                                    horizontalPolicy, verticalPolicy);
470 }
471
472 void ChromeClient::mouseDidMoveOverElement(const HitTestResult& hit, unsigned modifierFlags)
473 {
474     // check if the element is a link...
475     bool isLink = hit.isLiveLink();
476     if (isLink) {
477         KURL url = hit.absoluteLinkURL();
478         if (!url.isEmpty() && url != m_hoveredLinkURL) {
479             TextDirection dir;
480             CString titleString = hit.title(dir).utf8();
481             CString urlString = url.prettyURL().utf8();
482             g_signal_emit_by_name(m_webView, "hovering-over-link", titleString.data(), urlString.data());
483             m_hoveredLinkURL = url;
484         }
485     } else if (!isLink && !m_hoveredLinkURL.isEmpty()) {
486         g_signal_emit_by_name(m_webView, "hovering-over-link", 0, 0);
487         m_hoveredLinkURL = KURL();
488     }
489 }
490
491 void ChromeClient::setToolTip(const String& toolTip, TextDirection)
492 {
493     webkit_web_view_set_tooltip_text(m_webView, toolTip.utf8().data());
494 }
495
496 void ChromeClient::print(Frame* frame)
497 {
498     WebKitWebFrame* webFrame = kit(frame);
499     gboolean isHandled = false;
500     g_signal_emit_by_name(m_webView, "print-requested", webFrame, &isHandled);
501
502     if (isHandled)
503         return;
504
505     webkit_web_frame_print(webFrame);
506 }
507
508 #if ENABLE(DATABASE)
509 void ChromeClient::exceededDatabaseQuota(Frame* frame, const String& databaseName)
510 {
511     guint64 defaultQuota = webkit_get_default_web_database_quota();
512     DatabaseTracker::tracker().setQuota(frame->document()->securityOrigin(), defaultQuota);
513
514     WebKitWebFrame* webFrame = kit(frame);
515     WebKitWebView* webView = getViewFromFrame(webFrame);
516
517     WebKitSecurityOrigin* origin = webkit_web_frame_get_security_origin(webFrame);
518     WebKitWebDatabase* webDatabase = webkit_security_origin_get_web_database(origin, databaseName.utf8().data());
519     g_signal_emit_by_name(webView, "database-quota-exceeded", webFrame, webDatabase);
520 }
521 #endif
522
523 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
524 void ChromeClient::reachedMaxAppCacheSize(int64_t spaceNeeded)
525 {
526     // FIXME: Free some space.
527     notImplemented();
528 }
529 #endif
530
531 void ChromeClient::runOpenPanel(Frame*, PassRefPtr<FileChooser> prpFileChooser)
532 {
533     RefPtr<FileChooser> chooser = prpFileChooser;
534
535     GtkWidget* dialog = gtk_file_chooser_dialog_new(_("Upload File"),
536                                                     GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(platformPageClient()))),
537                                                     GTK_FILE_CHOOSER_ACTION_OPEN,
538                                                     GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
539                                                     GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
540                                                     NULL);
541
542     gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), chooser->allowsMultipleFiles());
543
544     if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
545         if (gtk_file_chooser_get_select_multiple(GTK_FILE_CHOOSER(dialog))) {
546             GSList* filenames = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
547             Vector<String> names;
548             for (GSList* item = filenames ; item ; item = item->next) {
549                 if (!item->data)
550                     continue;
551                 names.append(filenameToString(static_cast<char*>(item->data)));
552                 g_free(item->data);
553             }
554             g_slist_free(filenames);
555             chooser->chooseFiles(names);
556         } else {
557             gchar* filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
558             if (filename)
559                 chooser->chooseFile(filenameToString(filename));
560             g_free(filename);
561         }
562     }
563     gtk_widget_destroy(dialog);
564 }
565
566 void ChromeClient::iconForFiles(const Vector<WebCore::String>&, PassRefPtr<WebCore::FileChooser>)
567 {
568     // FIXME: Move the code in Icon::createIconForFiles() here.
569     notImplemented();
570 }
571
572 bool ChromeClient::setCursor(PlatformCursorHandle)
573 {
574     notImplemented();
575     return false;
576 }
577
578 void ChromeClient::requestGeolocationPermissionForFrame(Frame*, Geolocation*)
579 {
580     // See the comment in WebCore/page/ChromeClient.h
581     notImplemented();
582 }
583
584 }