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