[GTK] Using a native window for the WebView breaks GtkOverlay
[WebKit-https.git] / Source / 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  * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
8  * Copyright (C) 2012 Igalia S. L.
9  *
10  *  This library is free software; you can redistribute it and/or
11  *  modify it under the terms of the GNU Lesser General Public
12  *  License as published by the Free Software Foundation; either
13  *  version 2 of the License, or (at your option) any later version.
14  *
15  *  This library is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  *  Lesser General Public License for more details.
19  *
20  *  You should have received a copy of the GNU Lesser General Public
21  *  License along with this library; if not, write to the Free Software
22  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
23  */
24
25 #include "config.h"
26 #include "ChromeClientGtk.h"
27
28 #include "Chrome.h"
29 #include "Console.h"
30 #include "DumpRenderTreeSupportGtk.h"
31 #include "Element.h"
32 #include "FileChooser.h"
33 #include "FileIconLoader.h"
34 #include "FileSystem.h"
35 #include "FloatRect.h"
36 #include "FocusController.h"
37 #include "FrameLoadRequest.h"
38 #include "FrameView.h"
39 #include "GtkUtilities.h"
40 #include "GtkVersioning.h"
41 #include "HTMLNames.h"
42 #include "HitTestResult.h"
43 #include "Icon.h"
44 #include "InspectorController.h"
45 #include "IntRect.h"
46 #include "KURL.h"
47 #include "NavigationAction.h"
48 #include "NotImplemented.h"
49 #include "PlatformString.h"
50 #include "PopupMenuClient.h"
51 #include "PopupMenuGtk.h"
52 #include "RefPtrCairo.h"
53 #include "SearchPopupMenuGtk.h"
54 #include "SecurityOrigin.h"
55 #include "WebKitDOMBinding.h"
56 #include "WebKitDOMHTMLElementPrivate.h"
57 #include "WindowFeatures.h"
58 #include "webkitfilechooserrequestprivate.h"
59 #include "webkitgeolocationpolicydecision.h"
60 #include "webkitgeolocationpolicydecisionprivate.h"
61 #include "webkitnetworkrequest.h"
62 #include "webkitsecurityoriginprivate.h"
63 #include "webkitviewportattributesprivate.h"
64 #include "webkitwebframeprivate.h"
65 #include "webkitwebview.h"
66 #include "webkitwebviewprivate.h"
67 #include "webkitwebwindowfeaturesprivate.h"
68 #include <gdk/gdk.h>
69 #include <gdk/gdkkeysyms.h>
70 #include <glib.h>
71 #include <glib/gi18n-lib.h>
72 #include <gtk/gtk.h>
73 #include <wtf/MathExtras.h>
74 #include <wtf/text/CString.h>
75
76 #if ENABLE(SQL_DATABASE)
77 #include "DatabaseTracker.h"
78 #endif
79
80 using namespace WebCore;
81
82 namespace WebKit {
83
84 ChromeClient::ChromeClient(WebKitWebView* webView)
85     : m_webView(webView)
86     , m_adjustmentWatcher(webView)
87     , m_closeSoonTimer(0)
88     , m_displayTimer(this, &ChromeClient::paint)
89     , m_forcePaint(false)
90     , m_lastDisplayTime(0)
91     , m_repaintSoonSourceId(0)
92 {
93     ASSERT(m_webView);
94 }
95
96 void ChromeClient::chromeDestroyed()
97 {
98     if (m_closeSoonTimer)
99         g_source_remove(m_closeSoonTimer);
100
101     if (m_repaintSoonSourceId)
102         g_source_remove(m_repaintSoonSourceId);
103
104     delete this;
105 }
106
107 FloatRect ChromeClient::windowRect()
108 {
109     GtkWidget* window = gtk_widget_get_toplevel(GTK_WIDGET(m_webView));
110     if (widgetIsOnscreenToplevelWindow(window)) {
111         gint left, top, width, height;
112         gtk_window_get_position(GTK_WINDOW(window), &left, &top);
113         gtk_window_get_size(GTK_WINDOW(window), &width, &height);
114         return IntRect(left, top, width, height);
115     }
116     return FloatRect();
117 }
118
119 void ChromeClient::setWindowRect(const FloatRect& rect)
120 {
121     IntRect intrect = IntRect(rect);
122     WebKitWebWindowFeatures* webWindowFeatures = webkit_web_view_get_window_features(m_webView);
123
124     g_object_set(webWindowFeatures,
125                  "x", intrect.x(),
126                  "y", intrect.y(),
127                  "width", intrect.width(),
128                  "height", intrect.height(),
129                  NULL);
130
131     gboolean autoResizeWindow;
132     WebKitWebSettings* settings = webkit_web_view_get_settings(m_webView);
133     g_object_get(settings, "auto-resize-window", &autoResizeWindow, NULL);
134
135     if (!autoResizeWindow)
136         return;
137
138     GtkWidget* window = gtk_widget_get_toplevel(GTK_WIDGET(m_webView));
139     if (widgetIsOnscreenToplevelWindow(window)) {
140         gtk_window_move(GTK_WINDOW(window), intrect.x(), intrect.y());
141         gtk_window_resize(GTK_WINDOW(window), intrect.width(), intrect.height());
142     }
143 }
144
145 static IntRect getWebViewRect(WebKitWebView* webView)
146 {
147     GtkAllocation allocation;
148     gtk_widget_get_allocation(GTK_WIDGET(webView), &allocation);
149     return IntRect(allocation.x, allocation.y, allocation.width, allocation.height);
150 }
151
152 FloatRect ChromeClient::pageRect()
153 {
154     return getWebViewRect(m_webView);
155 }
156
157 void ChromeClient::focus()
158 {
159     gtk_widget_grab_focus(GTK_WIDGET(m_webView));
160 }
161
162 void ChromeClient::unfocus()
163 {
164     GtkWidget* window = gtk_widget_get_toplevel(GTK_WIDGET(m_webView));
165     if (widgetIsOnscreenToplevelWindow(window))
166         gtk_window_set_focus(GTK_WINDOW(window), NULL);
167 }
168
169 Page* ChromeClient::createWindow(Frame* frame, const FrameLoadRequest& frameLoadRequest, const WindowFeatures& coreFeatures, const NavigationAction&)
170 {
171     WebKitWebView* webView = 0;
172
173     g_signal_emit_by_name(m_webView, "create-web-view", kit(frame), &webView);
174
175     if (!webView)
176         return 0;
177
178     GRefPtr<WebKitWebWindowFeatures> webWindowFeatures(adoptGRef(kitNew(coreFeatures)));
179     g_object_set(webView, "window-features", webWindowFeatures.get(), NULL);
180
181     return core(webView);
182 }
183
184 void ChromeClient::show()
185 {
186     webkit_web_view_notify_ready(m_webView);
187 }
188
189 bool ChromeClient::canRunModal()
190 {
191     notImplemented();
192     return false;
193 }
194
195 void ChromeClient::runModal()
196 {
197     notImplemented();
198 }
199
200 void ChromeClient::setToolbarsVisible(bool visible)
201 {
202     WebKitWebWindowFeatures* webWindowFeatures = webkit_web_view_get_window_features(m_webView);
203
204     g_object_set(webWindowFeatures, "toolbar-visible", visible, NULL);
205 }
206
207 bool ChromeClient::toolbarsVisible()
208 {
209     WebKitWebWindowFeatures* webWindowFeatures = webkit_web_view_get_window_features(m_webView);
210     gboolean visible;
211
212     g_object_get(webWindowFeatures, "toolbar-visible", &visible, NULL);
213     return visible;
214 }
215
216 void ChromeClient::setStatusbarVisible(bool visible)
217 {
218     WebKitWebWindowFeatures* webWindowFeatures = webkit_web_view_get_window_features(m_webView);
219
220     g_object_set(webWindowFeatures, "statusbar-visible", visible, NULL);
221 }
222
223 bool ChromeClient::statusbarVisible()
224 {
225     WebKitWebWindowFeatures* webWindowFeatures = webkit_web_view_get_window_features(m_webView);
226     gboolean visible;
227
228     g_object_get(webWindowFeatures, "statusbar-visible", &visible, NULL);
229     return visible;
230 }
231
232 void ChromeClient::setScrollbarsVisible(bool visible)
233 {
234     WebKitWebWindowFeatures* webWindowFeatures = webkit_web_view_get_window_features(m_webView);
235
236     g_object_set(webWindowFeatures, "scrollbar-visible", visible, NULL);
237 }
238
239 bool ChromeClient::scrollbarsVisible()
240 {
241     WebKitWebWindowFeatures* webWindowFeatures = webkit_web_view_get_window_features(m_webView);
242     gboolean visible;
243
244     g_object_get(webWindowFeatures, "scrollbar-visible", &visible, NULL);
245     return visible;
246 }
247
248 void ChromeClient::setMenubarVisible(bool visible)
249 {
250     WebKitWebWindowFeatures* webWindowFeatures = webkit_web_view_get_window_features(m_webView);
251
252     g_object_set(webWindowFeatures, "menubar-visible", visible, NULL);
253 }
254
255 bool ChromeClient::menubarVisible()
256 {
257     WebKitWebWindowFeatures* webWindowFeatures = webkit_web_view_get_window_features(m_webView);
258     gboolean visible;
259
260     g_object_get(webWindowFeatures, "menubar-visible", &visible, NULL);
261     return visible;
262 }
263
264 void ChromeClient::setResizable(bool)
265 {
266     // Ignored for now
267 }
268
269 static gboolean emitCloseWebViewSignalLater(WebKitWebView* view)
270 {
271     gboolean isHandled;
272     g_signal_emit_by_name(view, "close-web-view", &isHandled);
273     return FALSE;
274 }
275
276 void ChromeClient::closeWindowSoon()
277 {
278     // We may not have a WebView as create-web-view can return NULL.
279     if (!m_webView)
280         return;
281     if (m_closeSoonTimer) // Don't call close-web-view more than once.
282         return;
283
284     // We need to remove the parent WebView from WebViewSets here, before it actually
285     // closes, to make sure that JavaScript code that executes before it closes
286     // can't find it. Otherwise, window.open will select a closed WebView instead of 
287     // opening a new one <rdar://problem/3572585>.
288     m_webView->priv->corePage->setGroupName("");
289
290     // We also need to stop the load to prevent further parsing or JavaScript execution
291     // after the window has torn down <rdar://problem/4161660>.
292     webkit_web_view_stop_loading(m_webView);
293
294     // Clients commonly destroy the web view during the close-web-view signal, but our caller
295     // may need to send more signals to the web view. For instance, if this happened in the
296     // onload handler, it will need to call FrameLoaderClient::dispatchDidHandleOnloadEvents.
297     // Instead of firing the close-web-view signal now, fire it after the caller finishes.
298     // This seems to match the Mac/Windows port behavior.
299     m_closeSoonTimer = g_timeout_add(0, reinterpret_cast<GSourceFunc>(emitCloseWebViewSignalLater), m_webView);
300 }
301
302 bool ChromeClient::canTakeFocus(FocusDirection)
303 {
304     return gtk_widget_get_can_focus(GTK_WIDGET(m_webView));
305 }
306
307 void ChromeClient::takeFocus(FocusDirection)
308 {
309     unfocus();
310 }
311
312 void ChromeClient::focusedNodeChanged(Node*)
313 {
314 }
315
316 void ChromeClient::focusedFrameChanged(Frame*)
317 {
318 }
319
320 bool ChromeClient::canRunBeforeUnloadConfirmPanel()
321 {
322     return true;
323 }
324
325 bool ChromeClient::runBeforeUnloadConfirmPanel(const WTF::String& message, WebCore::Frame* frame)
326 {
327     return runJavaScriptConfirm(frame, message);
328 }
329
330 void ChromeClient::addMessageToConsole(WebCore::MessageSource source, WebCore::MessageType type, WebCore::MessageLevel level, const WTF::String& message, unsigned int lineNumber, const WTF::String& sourceId)
331 {
332     gboolean retval;
333     g_signal_emit_by_name(m_webView, "console-message", message.utf8().data(), lineNumber, sourceId.utf8().data(), &retval);
334 }
335
336 void ChromeClient::runJavaScriptAlert(Frame* frame, const String& message)
337 {
338     gboolean retval;
339     g_signal_emit_by_name(m_webView, "script-alert", kit(frame), message.utf8().data(), &retval);
340 }
341
342 bool ChromeClient::runJavaScriptConfirm(Frame* frame, const String& message)
343 {
344     gboolean retval;
345     gboolean didConfirm;
346     g_signal_emit_by_name(m_webView, "script-confirm", kit(frame), message.utf8().data(), &didConfirm, &retval);
347     return didConfirm == TRUE;
348 }
349
350 bool ChromeClient::runJavaScriptPrompt(Frame* frame, const String& message, const String& defaultValue, String& result)
351 {
352     gboolean retval;
353     gchar* value = 0;
354     g_signal_emit_by_name(m_webView, "script-prompt", kit(frame), message.utf8().data(), defaultValue.utf8().data(), &value, &retval);
355     if (value) {
356         result = String::fromUTF8(value);
357         g_free(value);
358         return true;
359     }
360     return false;
361 }
362
363 void ChromeClient::setStatusbarText(const String& string)
364 {
365     CString stringMessage = string.utf8();
366     g_signal_emit_by_name(m_webView, "status-bar-text-changed", stringMessage.data());
367 }
368
369 bool ChromeClient::shouldInterruptJavaScript()
370 {
371     notImplemented();
372     return false;
373 }
374
375 KeyboardUIMode ChromeClient::keyboardUIMode()
376 {
377     bool tabsToLinks = true;
378     if (DumpRenderTreeSupportGtk::dumpRenderTreeModeEnabled())
379         tabsToLinks = DumpRenderTreeSupportGtk::linksIncludedInFocusChain();
380
381     return tabsToLinks ? KeyboardAccessTabsToLinks : KeyboardAccessDefault;
382 }
383
384 IntRect ChromeClient::windowResizerRect() const
385 {
386     notImplemented();
387     return IntRect();
388 }
389
390 static gboolean repaintEverythingSoonTimeout(ChromeClient* client)
391 {
392     client->paint(0);
393     return FALSE;
394 }
395
396 static void clipOutOldWidgetArea(cairo_t* cr, const IntSize& oldSize, const IntSize& newSize)
397 {
398     cairo_move_to(cr, oldSize.width(), 0);
399     cairo_line_to(cr, newSize.width(), 0);
400     cairo_line_to(cr, newSize.width(), newSize.height());
401     cairo_line_to(cr, 0, newSize.height());
402     cairo_line_to(cr, 0, oldSize.height());
403     cairo_line_to(cr, oldSize.width(), oldSize.height());
404     cairo_close_path(cr);
405     cairo_clip(cr);
406 }
407
408 static void clearEverywhereInBackingStore(WebKitWebView* webView, cairo_t* cr)
409 {
410     // The strategy here is to quickly draw white into this new canvas, so that
411     // when a user quickly resizes the WebView in an environment that has opaque
412     // resizing (like Gnome Shell), there are no drawing artifacts.
413     if (!webView->priv->transparent) {
414         cairo_set_source_rgb(cr, 1, 1, 1);
415         cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
416     } else
417         cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
418     cairo_paint(cr);
419 }
420
421 void ChromeClient::widgetSizeChanged(const IntSize& oldWidgetSize, IntSize newSize)
422 {
423 #if USE(ACCELERATED_COMPOSITING)
424     AcceleratedCompositingContext* compositingContext = m_webView->priv->acceleratedCompositingContext.get();
425     if (compositingContext->enabled()) {
426         m_webView->priv->acceleratedCompositingContext->resizeRootLayer(newSize);
427         return;
428     }
429 #endif
430
431     // Grow the backing store by at least 1.5 times the current size. This prevents
432     // lots of unnecessary allocations during an opaque resize.
433     WidgetBackingStore* backingStore = m_webView->priv->backingStore.get();
434     if (backingStore && oldWidgetSize == newSize)
435         return;
436
437     if (backingStore) {
438         const IntSize& oldSize = backingStore->size();
439         if (newSize.width() > oldSize.width())
440             newSize.setWidth(std::max(newSize.width(), static_cast<int>(oldSize.width() * 1.5)));
441         if (newSize.height() > oldSize.height())
442             newSize.setHeight(std::max(newSize.height(), static_cast<int>(oldSize.height() * 1.5)));
443     }
444
445     // If we did not have a backing store before or if the backing store is growing, we need
446     // to reallocate a new one and set it up so that we don't see artifacts while resizing.
447     if (!backingStore
448         || newSize.width() > backingStore->size().width()
449         || newSize.height() > backingStore->size().height()) {
450
451         OwnPtr<WidgetBackingStore> newBackingStore =
452             WebCore::WidgetBackingStore::create(GTK_WIDGET(m_webView), newSize);
453         RefPtr<cairo_t> cr = adoptRef(cairo_create(newBackingStore->cairoSurface()));
454
455         clearEverywhereInBackingStore(m_webView, cr.get());
456
457         // Now we copy the old backing store image over the new cleared surface to prevent
458         // annoying flashing as the widget grows. We do the "real" paint in a timeout
459         // since we don't want to block resizing too long.
460         if (backingStore) {
461             cairo_set_source_surface(cr.get(), backingStore->cairoSurface(), 0, 0);
462             cairo_rectangle(cr.get(), 0, 0, backingStore->size().width(), backingStore->size().height());
463             cairo_fill(cr.get());
464         }
465
466         m_webView->priv->backingStore = newBackingStore.release();
467         backingStore = m_webView->priv->backingStore.get();
468
469     } else if (oldWidgetSize.width() < newSize.width() || oldWidgetSize.height() < newSize.height()) {
470         // The widget is growing, but we did not need to create a new backing store.
471         // We should clear any old data outside of the old widget region.
472         RefPtr<cairo_t> cr = adoptRef(cairo_create(backingStore->cairoSurface()));
473         clipOutOldWidgetArea(cr.get(), oldWidgetSize, newSize);
474         clearEverywhereInBackingStore(m_webView, cr.get());
475     }
476
477     // We need to force a redraw and ignore the framerate cap.
478     m_lastDisplayTime = 0;
479     m_dirtyRegion.unite(IntRect(IntPoint(), backingStore->size()));
480
481     // WebCore timers by default have a lower priority which leads to more artifacts when opaque
482     // resize is on, thus we use g_timeout_add here to force a higher timeout priority.
483     if (!m_repaintSoonSourceId)
484         m_repaintSoonSourceId = g_timeout_add(0, reinterpret_cast<GSourceFunc>(repaintEverythingSoonTimeout), this);
485 }
486
487 static void coalesceRectsIfPossible(const IntRect& clipRect, Vector<IntRect>& rects)
488 {
489     const unsigned int cRectThreshold = 10;
490     const float cWastedSpaceThreshold = 0.75f;
491     bool useUnionedRect = (rects.size() <= 1) || (rects.size() > cRectThreshold);
492     if (!useUnionedRect) {
493         // Attempt to guess whether or not we should use the unioned rect or the individual rects.
494         // We do this by computing the percentage of "wasted space" in the union. If that wasted space
495         // is too large, then we will do individual rect painting instead.
496         float unionPixels = (clipRect.width() * clipRect.height());
497         float singlePixels = 0;
498         for (size_t i = 0; i < rects.size(); ++i)
499             singlePixels += rects[i].width() * rects[i].height();
500         float wastedSpace = 1 - (singlePixels / unionPixels);
501         if (wastedSpace <= cWastedSpaceThreshold)
502             useUnionedRect = true;
503     }
504
505     if (!useUnionedRect)
506         return;
507
508     rects.clear();
509     rects.append(clipRect);
510 }
511
512 static void paintWebView(WebKitWebView* webView, Frame* frame, Region dirtyRegion)
513 {
514     if (!webView->priv->backingStore)
515         return;
516
517     Vector<IntRect> rects = dirtyRegion.rects();
518     coalesceRectsIfPossible(dirtyRegion.bounds(), rects);
519
520     RefPtr<cairo_t> backingStoreContext = adoptRef(cairo_create(webView->priv->backingStore->cairoSurface()));
521     GraphicsContext gc(backingStoreContext.get());
522     gc.applyDeviceScaleFactor(frame->page()->deviceScaleFactor());
523     for (size_t i = 0; i < rects.size(); i++) {
524         const IntRect& rect = rects[i];
525
526         gc.save();
527         gc.clip(rect);
528         if (webView->priv->transparent)
529             gc.clearRect(rect);
530         frame->view()->paint(&gc, rect);
531         gc.restore();
532     }
533
534     gc.save();
535     gc.clip(dirtyRegion.bounds());
536     frame->page()->inspectorController()->drawHighlight(gc);
537     gc.restore();
538 }
539
540 void ChromeClient::performAllPendingScrolls()
541 {
542     if (!m_webView->priv->backingStore)
543         return;
544
545     // Scroll all pending scroll rects and invalidate those parts of the widget.
546     for (size_t i = 0; i < m_rectsToScroll.size(); i++) {
547         IntRect& scrollRect = m_rectsToScroll[i];
548         m_webView->priv->backingStore->scroll(scrollRect, m_scrollOffsets[i]);
549         gtk_widget_queue_draw_area(GTK_WIDGET(m_webView), scrollRect.x(), scrollRect.y(), scrollRect.width(), scrollRect.height());
550     }
551
552     m_rectsToScroll.clear();
553     m_scrollOffsets.clear();
554 }
555
556 void ChromeClient::paint(WebCore::Timer<ChromeClient>*)
557 {
558     static const double minimumFrameInterval = 1.0 / 60.0; // No more than 60 frames a second.
559     double timeSinceLastDisplay = currentTime() - m_lastDisplayTime;
560     double timeUntilNextDisplay = minimumFrameInterval - timeSinceLastDisplay;
561
562     if (timeUntilNextDisplay > 0 && !m_forcePaint) {
563         m_displayTimer.startOneShot(timeUntilNextDisplay);
564         return;
565     }
566
567     Frame* frame = core(m_webView)->mainFrame();
568     if (!frame || !frame->contentRenderer() || !frame->view())
569         return;
570
571     frame->view()->updateLayoutAndStyleIfNeededRecursive();
572     performAllPendingScrolls();
573     paintWebView(m_webView, frame, m_dirtyRegion);
574
575     HashSet<GtkWidget*> children = m_webView->priv->children;
576     HashSet<GtkWidget*>::const_iterator end = children.end();
577     for (HashSet<GtkWidget*>::const_iterator current = children.begin(); current != end; ++current) {
578         if (static_cast<GtkAllocation*>(g_object_get_data(G_OBJECT(*current), "delayed-allocation"))) {
579             gtk_widget_queue_resize_no_redraw(GTK_WIDGET(m_webView));
580             break;
581         }
582     }
583
584     const IntRect& rect = m_dirtyRegion.bounds();
585     gtk_widget_queue_draw_area(GTK_WIDGET(m_webView), rect.x(), rect.y(), rect.width(), rect.height());
586
587     m_dirtyRegion = Region();
588     m_lastDisplayTime = currentTime();
589     m_repaintSoonSourceId = 0;
590
591     // We update the IM context window location here, because we want it to be
592     // synced with cursor movement. For instance, a text field can move without
593     // the selection changing.
594     Frame* focusedFrame = core(m_webView)->focusController()->focusedOrMainFrame();
595     if (focusedFrame && focusedFrame->editor()->canEdit())
596         m_webView->priv->imFilter.setCursorRect(frame->selection()->absoluteCaretBounds());
597 }
598
599 void ChromeClient::forcePaint()
600 {
601 #if USE(ACCELERATED_COMPOSITING)
602     if (m_webView->priv->acceleratedCompositingContext->enabled())
603         return;
604 #endif
605
606     m_forcePaint = true;
607     paint(0);
608     m_forcePaint = false;
609 }
610
611 void ChromeClient::invalidateRootView(const IntRect&, bool immediate)
612 {
613 }
614
615 void ChromeClient::invalidateContentsAndRootView(const IntRect& updateRect, bool immediate)
616 {
617 #if USE(ACCELERATED_COMPOSITING)
618     AcceleratedCompositingContext* acContext = m_webView->priv->acceleratedCompositingContext.get();
619     if (acContext->enabled()) {
620         acContext->setNonCompositedContentsNeedDisplay(updateRect);
621         return;
622     }
623 #endif
624
625     if (updateRect.isEmpty())
626         return;
627     m_dirtyRegion.unite(updateRect);
628     m_displayTimer.startOneShot(0);
629 }
630
631 void ChromeClient::invalidateContentsForSlowScroll(const IntRect& updateRect, bool immediate)
632 {
633     m_adjustmentWatcher.updateAdjustmentsFromScrollbarsLater();
634
635 #if USE(ACCELERATED_COMPOSITING)
636     AcceleratedCompositingContext* acContext = m_webView->priv->acceleratedCompositingContext.get();
637     if (acContext->enabled()) {
638         acContext->setNonCompositedContentsNeedDisplay(updateRect);
639         return;
640     }
641 #endif
642
643     invalidateContentsAndRootView(updateRect, immediate);
644 }
645
646 void ChromeClient::scroll(const IntSize& delta, const IntRect& rectToScroll, const IntRect& clipRect)
647 {
648     m_adjustmentWatcher.updateAdjustmentsFromScrollbarsLater();
649
650 #if USE(ACCELERATED_COMPOSITING)
651     AcceleratedCompositingContext* compositingContext = m_webView->priv->acceleratedCompositingContext.get();
652     if (compositingContext->enabled()) {
653         ASSERT(!rectToScroll.isEmpty());
654         ASSERT(!delta.isEmpty());
655
656         compositingContext->scrollNonCompositedContents(rectToScroll, delta);
657         return;
658     }
659 #endif
660
661     m_rectsToScroll.append(rectToScroll);
662     m_scrollOffsets.append(delta);
663
664     // The code to calculate the scroll repaint region is originally from WebKit2.
665     // Get the part of the dirty region that is in the scroll rect.
666     Region dirtyRegionInScrollRect = intersect(rectToScroll, m_dirtyRegion);
667     if (!dirtyRegionInScrollRect.isEmpty()) {
668         // There are parts of the dirty region that are inside the scroll rect.
669         // We need to subtract them from the region, move them and re-add them.
670         m_dirtyRegion.subtract(rectToScroll);
671
672         // Move the dirty parts.
673         Region movedDirtyRegionInScrollRect = intersect(translate(dirtyRegionInScrollRect, delta), rectToScroll);
674
675         // And add them back.
676         m_dirtyRegion.unite(movedDirtyRegionInScrollRect);
677     }
678
679     // Compute the scroll repaint region. We ensure that we are not subtracting areas
680     // that we've scrolled from outside the viewport from the repaint region.
681     IntRect onScreenScrollRect = rectToScroll;
682     onScreenScrollRect.intersect(IntRect(IntPoint(), enclosingIntRect(pageRect()).size()));
683     Region scrollRepaintRegion = subtract(rectToScroll, translate(onScreenScrollRect, delta));
684
685     m_dirtyRegion.unite(scrollRepaintRegion);
686     m_displayTimer.startOneShot(0);
687 }
688
689 IntRect ChromeClient::rootViewToScreen(const IntRect& rect) const
690 {
691     return IntRect(convertWidgetPointToScreenPoint(GTK_WIDGET(m_webView), rect.location()), rect.size());
692 }
693
694 IntPoint ChromeClient::screenToRootView(const IntPoint& point) const
695 {
696     IntPoint widgetPositionOnScreen = convertWidgetPointToScreenPoint(GTK_WIDGET(m_webView), IntPoint());
697     IntPoint result(point);
698     result.move(-widgetPositionOnScreen.x(), -widgetPositionOnScreen.y());
699     return result;
700 }
701
702 PlatformPageClient ChromeClient::platformPageClient() const
703 {
704     return GTK_WIDGET(m_webView);
705 }
706
707 void ChromeClient::contentsSizeChanged(Frame* frame, const IntSize& size) const
708 {
709     if (m_adjustmentWatcher.scrollbarsDisabled())
710         return;
711
712     // We need to queue a resize request only if the size changed,
713     // otherwise we get into an infinite loop!
714     GtkWidget* widget = GTK_WIDGET(m_webView);
715     GtkRequisition requisition;
716 #if GTK_CHECK_VERSION(2, 20, 0)
717     gtk_widget_get_requisition(widget, &requisition);
718 #else
719     requisition = widget->requisition;
720 #endif
721     if (gtk_widget_get_realized(widget)
722         && (requisition.height != size.height())
723         || (requisition.width != size.width()))
724         gtk_widget_queue_resize_no_redraw(widget);
725
726     // If this was a main frame size change, update the scrollbars.
727     if (frame != frame->page()->mainFrame())
728         return;
729     m_adjustmentWatcher.updateAdjustmentsFromScrollbarsLater();
730 }
731
732 void ChromeClient::scrollbarsModeDidChange() const
733 {
734     WebKitWebFrame* webFrame = webkit_web_view_get_main_frame(m_webView);
735     if (!webFrame)
736         return;
737
738     g_object_notify(G_OBJECT(webFrame), "horizontal-scrollbar-policy");
739     g_object_notify(G_OBJECT(webFrame), "vertical-scrollbar-policy");
740
741     gboolean isHandled;
742     g_signal_emit_by_name(webFrame, "scrollbars-policy-changed", &isHandled);
743
744     if (isHandled)
745         return;
746
747     GtkWidget* parent = gtk_widget_get_parent(GTK_WIDGET(m_webView));
748     if (!parent || !GTK_IS_SCROLLED_WINDOW(parent))
749         return;
750
751     GtkPolicyType horizontalPolicy = webkit_web_frame_get_horizontal_scrollbar_policy(webFrame);
752     GtkPolicyType verticalPolicy = webkit_web_frame_get_vertical_scrollbar_policy(webFrame);
753
754     // ScrolledWindow doesn't like to display only part of a widget if
755     // the scrollbars are completely disabled; We have a disparity
756     // here on what the policy requested by the web app is and what we
757     // can represent; the idea is not to show scrollbars, only.
758     if (horizontalPolicy == GTK_POLICY_NEVER)
759         horizontalPolicy = GTK_POLICY_AUTOMATIC;
760
761     if (verticalPolicy == GTK_POLICY_NEVER)
762         verticalPolicy = GTK_POLICY_AUTOMATIC;
763
764     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(parent),
765                                    horizontalPolicy, verticalPolicy);
766 }
767
768 void ChromeClient::mouseDidMoveOverElement(const HitTestResult& hit, unsigned modifierFlags)
769 {
770     // check if the element is a link...
771     bool isLink = hit.isLiveLink();
772     if (isLink) {
773         KURL url = hit.absoluteLinkURL();
774         if (!url.isEmpty() && url != m_hoveredLinkURL) {
775             TextDirection dir;
776             CString titleString = hit.title(dir).utf8();
777             CString urlString = url.string().utf8();
778             g_signal_emit_by_name(m_webView, "hovering-over-link", titleString.data(), urlString.data());
779             m_hoveredLinkURL = url;
780         }
781     } else if (!isLink && !m_hoveredLinkURL.isEmpty()) {
782         g_signal_emit_by_name(m_webView, "hovering-over-link", 0, 0);
783         m_hoveredLinkURL = KURL();
784     }
785
786     if (Node* node = hit.innerNonSharedNode()) {
787         Frame* frame = node->document()->frame();
788         FrameView* view = frame ? frame->view() : 0;
789         m_webView->priv->tooltipArea = view ? view->contentsToWindow(node->getPixelSnappedRect()) : IntRect();
790     } else
791         m_webView->priv->tooltipArea = IntRect();
792 }
793
794 void ChromeClient::setToolTip(const String& toolTip, TextDirection)
795 {
796     webkit_web_view_set_tooltip_text(m_webView, toolTip.utf8().data());
797 }
798
799 void ChromeClient::print(Frame* frame)
800 {
801     WebKitWebFrame* webFrame = kit(frame);
802     gboolean isHandled = false;
803     g_signal_emit_by_name(m_webView, "print-requested", webFrame, &isHandled);
804
805     if (isHandled)
806         return;
807
808     webkit_web_frame_print(webFrame);
809 }
810
811 #if ENABLE(SQL_DATABASE)
812 void ChromeClient::exceededDatabaseQuota(Frame* frame, const String& databaseName)
813 {
814     guint64 defaultQuota = webkit_get_default_web_database_quota();
815     DatabaseTracker::tracker().setQuota(frame->document()->securityOrigin(), defaultQuota);
816
817     WebKitWebFrame* webFrame = kit(frame);
818     WebKitSecurityOrigin* origin = webkit_web_frame_get_security_origin(webFrame);
819     WebKitWebDatabase* webDatabase = webkit_security_origin_get_web_database(origin, databaseName.utf8().data());
820     g_signal_emit_by_name(m_webView, "database-quota-exceeded", webFrame, webDatabase);
821 }
822 #endif
823
824 void ChromeClient::reachedMaxAppCacheSize(int64_t spaceNeeded)
825 {
826     // FIXME: Free some space.
827     notImplemented();
828 }
829
830 void ChromeClient::reachedApplicationCacheOriginQuota(SecurityOrigin*, int64_t)
831 {
832     notImplemented();
833 }
834
835 void ChromeClient::runOpenPanel(Frame*, PassRefPtr<FileChooser> prpFileChooser)
836 {
837     GRefPtr<WebKitFileChooserRequest> request = adoptGRef(webkit_file_chooser_request_create(prpFileChooser));
838     webkitWebViewRunFileChooserRequest(m_webView, request.get());
839 }
840
841 void ChromeClient::loadIconForFiles(const Vector<WTF::String>& filenames, WebCore::FileIconLoader* loader)
842 {
843     loader->notifyFinished(Icon::createIconForFiles(filenames));
844 }
845
846 void ChromeClient::dispatchViewportPropertiesDidChange(const ViewportArguments& arguments) const
847 {
848     // Recompute the viewport attributes making it valid.
849     webkitViewportAttributesRecompute(webkit_web_view_get_viewport_attributes(m_webView));
850 }
851
852 void ChromeClient::setCursor(const Cursor& cursor)
853 {
854     // [GTK] Widget::setCursor() gets called frequently
855     // http://bugs.webkit.org/show_bug.cgi?id=16388
856     // Setting the cursor may be an expensive operation in some backends,
857     // so don't re-set the cursor if it's already set to the target value.
858     GdkWindow* window = gtk_widget_get_window(platformPageClient());
859     if (!window)
860         return;
861
862     GdkCursor* currentCursor = gdk_window_get_cursor(window);
863     GdkCursor* newCursor = cursor.platformCursor().get();
864     if (currentCursor != newCursor)
865         gdk_window_set_cursor(window, newCursor);
866 }
867
868 void ChromeClient::setCursorHiddenUntilMouseMoves(bool)
869 {
870     notImplemented();
871 }
872
873 bool ChromeClient::selectItemWritingDirectionIsNatural()
874 {
875     return false;
876 }
877
878 bool ChromeClient::selectItemAlignmentFollowsMenuWritingDirection()
879 {
880     return true;
881 }
882
883 bool ChromeClient::hasOpenedPopup() const
884 {
885     notImplemented();
886     return false;
887 }
888
889 PassRefPtr<WebCore::PopupMenu> ChromeClient::createPopupMenu(WebCore::PopupMenuClient* client) const
890 {
891     return adoptRef(new PopupMenuGtk(client));
892 }
893
894 PassRefPtr<WebCore::SearchPopupMenu> ChromeClient::createSearchPopupMenu(WebCore::PopupMenuClient* client) const
895 {
896     return adoptRef(new SearchPopupMenuGtk(client));
897 }
898
899 #if ENABLE(VIDEO)
900
901 bool ChromeClient::supportsFullscreenForNode(const Node* node)
902 {
903     return node->hasTagName(HTMLNames::videoTag);
904 }
905
906 void ChromeClient::enterFullscreenForNode(Node* node)
907 {
908     webViewEnterFullscreen(m_webView, node);
909 }
910
911 void ChromeClient::exitFullscreenForNode(Node* node)
912 {
913     webViewExitFullscreen(m_webView);
914 }
915 #endif
916
917 #if ENABLE(FULLSCREEN_API)
918 bool ChromeClient::supportsFullScreenForElement(const WebCore::Element* element, bool withKeyboard)
919 {
920     return !withKeyboard;
921 }
922
923 static gboolean onFullscreenGtkKeyPressEvent(GtkWidget* widget, GdkEventKey* event, ChromeClient* chromeClient)
924 {
925     switch (event->keyval) {
926     case GDK_KEY_Escape:
927     case GDK_KEY_f:
928     case GDK_KEY_F:
929         chromeClient->cancelFullScreen();
930         return TRUE;
931     default:
932         break;
933     }
934
935     return FALSE;
936 }
937
938 void ChromeClient::cancelFullScreen()
939 {
940     ASSERT(m_fullScreenElement);
941     m_fullScreenElement->document()->webkitCancelFullScreen();
942 }
943
944 void ChromeClient::enterFullScreenForElement(WebCore::Element* element)
945 {
946     gboolean returnValue;
947     GRefPtr<WebKitDOMHTMLElement> kitElement(adoptGRef(kit(reinterpret_cast<HTMLElement*>(element))));
948     g_signal_emit_by_name(m_webView, "entering-fullscreen", kitElement.get(), &returnValue);
949     if (returnValue)
950         return;
951
952     GtkWidget* window = gtk_widget_get_toplevel(GTK_WIDGET(m_webView));
953     if (!widgetIsOnscreenToplevelWindow(window))
954         return;
955
956     g_signal_connect(window, "key-press-event", G_CALLBACK(onFullscreenGtkKeyPressEvent), this);
957
958     m_fullScreenElement = element;
959
960     element->document()->webkitWillEnterFullScreenForElement(element);
961     m_adjustmentWatcher.disableAllScrollbars();
962     gtk_window_fullscreen(GTK_WINDOW(window));
963     element->document()->webkitDidEnterFullScreenForElement(element);
964 }
965
966 void ChromeClient::exitFullScreenForElement(WebCore::Element*)
967 {
968     // The element passed into this function is not reliable, i.e. it could
969     // be null. In addition the parameter may be disappearing in the future.
970     // So we use the reference to the element we saved above.
971     ASSERT(m_fullScreenElement);
972
973     gboolean returnValue;
974     GRefPtr<WebKitDOMHTMLElement> kitElement(adoptGRef(kit(reinterpret_cast<HTMLElement*>(m_fullScreenElement.get()))));
975     g_signal_emit_by_name(m_webView, "leaving-fullscreen", kitElement.get(), &returnValue);
976     if (returnValue)
977         return;
978
979     GtkWidget* window = gtk_widget_get_toplevel(GTK_WIDGET(m_webView));
980     ASSERT(widgetIsOnscreenToplevelWindow(window));
981     g_signal_handlers_disconnect_by_func(window, reinterpret_cast<void*>(onFullscreenGtkKeyPressEvent), this);
982
983     m_fullScreenElement->document()->webkitWillExitFullScreenForElement(m_fullScreenElement.get());
984     gtk_window_unfullscreen(GTK_WINDOW(window));
985     m_adjustmentWatcher.enableAllScrollbars();
986     m_fullScreenElement->document()->webkitDidExitFullScreenForElement(m_fullScreenElement.get());
987     m_fullScreenElement.clear();
988 }
989 #endif
990
991 #if USE(ACCELERATED_COMPOSITING)
992 void ChromeClient::attachRootGraphicsLayer(Frame* frame, GraphicsLayer* rootLayer)
993 {
994     AcceleratedCompositingContext* context = m_webView->priv->acceleratedCompositingContext.get();
995     bool turningOffCompositing = !rootLayer && context->enabled();
996     bool turningOnCompositing = rootLayer && !context->enabled();
997
998     context->setRootCompositingLayer(rootLayer);
999
1000     if (turningOnCompositing) {
1001         m_displayTimer.stop();
1002         m_webView->priv->backingStore = WebCore::WidgetBackingStore::create(GTK_WIDGET(m_webView), IntSize(1, 1));
1003     }
1004
1005     if (turningOffCompositing) {
1006         m_webView->priv->backingStore = WebCore::WidgetBackingStore::create(GTK_WIDGET(m_webView), getWebViewRect(m_webView).size());
1007         RefPtr<cairo_t> cr = adoptRef(cairo_create(m_webView->priv->backingStore->cairoSurface()));
1008         clearEverywhereInBackingStore(m_webView, cr.get());
1009     }
1010 }
1011
1012 void ChromeClient::setNeedsOneShotDrawingSynchronization()
1013 {
1014     m_webView->priv->acceleratedCompositingContext->scheduleLayerFlush();
1015 }
1016
1017 void ChromeClient::scheduleCompositingLayerSync()
1018 {
1019     m_webView->priv->acceleratedCompositingContext->scheduleLayerFlush();
1020 }
1021
1022 ChromeClient::CompositingTriggerFlags ChromeClient::allowedCompositingTriggers() const
1023 {
1024      if (!platformPageClient())
1025         return false;
1026 #if USE(CLUTTER)
1027     // Currently, we only support CSS 3D Transforms.
1028     return ThreeDTransformTrigger;
1029 #else
1030     return AllTriggers;
1031 #endif
1032 }
1033 #endif
1034
1035 }