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