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.
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.
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.
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
26 #include "ChromeClientGtk.h"
30 #include "DumpRenderTreeSupportGtk.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"
43 #include "InspectorController.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"
65 #include <glib/gi18n-lib.h>
67 #include <wtf/MathExtras.h>
68 #include <wtf/text/CString.h>
70 #if ENABLE(SQL_DATABASE)
71 #include "DatabaseTracker.h"
74 using namespace WebCore;
78 ChromeClient::ChromeClient(WebKitWebView* webView)
80 , m_adjustmentWatcher(webView)
82 , m_displayTimer(this, &ChromeClient::paint)
83 , m_lastDisplayTime(0)
84 , m_repaintSoonSourceId(0)
89 void ChromeClient::chromeDestroyed()
92 g_source_remove(m_closeSoonTimer);
94 if (m_repaintSoonSourceId)
95 g_source_remove(m_repaintSoonSourceId);
100 FloatRect ChromeClient::windowRect()
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);
112 void ChromeClient::setWindowRect(const FloatRect& rect)
114 IntRect intrect = IntRect(rect);
115 WebKitWebWindowFeatures* webWindowFeatures = webkit_web_view_get_window_features(m_webView);
117 g_object_set(webWindowFeatures,
120 "width", intrect.width(),
121 "height", intrect.height(),
124 gboolean autoResizeWindow;
125 WebKitWebSettings* settings = webkit_web_view_get_settings(m_webView);
126 g_object_get(settings, "auto-resize-window", &autoResizeWindow, NULL);
128 if (!autoResizeWindow)
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());
138 FloatRect ChromeClient::pageRect()
140 GtkAllocation allocation;
141 #if GTK_CHECK_VERSION(2, 18, 0)
142 gtk_widget_get_allocation(GTK_WIDGET(m_webView), &allocation);
144 allocation = GTK_WIDGET(m_webView)->allocation;
146 return IntRect(allocation.x, allocation.y, allocation.width, allocation.height);
149 void ChromeClient::focus()
151 gtk_widget_grab_focus(GTK_WIDGET(m_webView));
154 void ChromeClient::unfocus()
156 GtkWidget* window = gtk_widget_get_toplevel(GTK_WIDGET(m_webView));
157 if (widgetIsOnscreenToplevelWindow(window))
158 gtk_window_set_focus(GTK_WINDOW(window), NULL);
161 Page* ChromeClient::createWindow(Frame* frame, const FrameLoadRequest& frameLoadRequest, const WindowFeatures& coreFeatures, const NavigationAction&)
163 WebKitWebView* webView = 0;
165 g_signal_emit_by_name(m_webView, "create-web-view", kit(frame), &webView);
170 GRefPtr<WebKitWebWindowFeatures> webWindowFeatures(adoptGRef(kitNew(coreFeatures)));
171 g_object_set(webView, "window-features", webWindowFeatures.get(), NULL);
173 return core(webView);
176 void ChromeClient::show()
178 webkit_web_view_notify_ready(m_webView);
181 bool ChromeClient::canRunModal()
187 void ChromeClient::runModal()
192 void ChromeClient::setToolbarsVisible(bool visible)
194 WebKitWebWindowFeatures* webWindowFeatures = webkit_web_view_get_window_features(m_webView);
196 g_object_set(webWindowFeatures, "toolbar-visible", visible, NULL);
199 bool ChromeClient::toolbarsVisible()
201 WebKitWebWindowFeatures* webWindowFeatures = webkit_web_view_get_window_features(m_webView);
204 g_object_get(webWindowFeatures, "toolbar-visible", &visible, NULL);
208 void ChromeClient::setStatusbarVisible(bool visible)
210 WebKitWebWindowFeatures* webWindowFeatures = webkit_web_view_get_window_features(m_webView);
212 g_object_set(webWindowFeatures, "statusbar-visible", visible, NULL);
215 bool ChromeClient::statusbarVisible()
217 WebKitWebWindowFeatures* webWindowFeatures = webkit_web_view_get_window_features(m_webView);
220 g_object_get(webWindowFeatures, "statusbar-visible", &visible, NULL);
224 void ChromeClient::setScrollbarsVisible(bool visible)
226 WebKitWebWindowFeatures* webWindowFeatures = webkit_web_view_get_window_features(m_webView);
228 g_object_set(webWindowFeatures, "scrollbar-visible", visible, NULL);
231 bool ChromeClient::scrollbarsVisible()
233 WebKitWebWindowFeatures* webWindowFeatures = webkit_web_view_get_window_features(m_webView);
236 g_object_get(webWindowFeatures, "scrollbar-visible", &visible, NULL);
240 void ChromeClient::setMenubarVisible(bool visible)
242 WebKitWebWindowFeatures* webWindowFeatures = webkit_web_view_get_window_features(m_webView);
244 g_object_set(webWindowFeatures, "menubar-visible", visible, NULL);
247 bool ChromeClient::menubarVisible()
249 WebKitWebWindowFeatures* webWindowFeatures = webkit_web_view_get_window_features(m_webView);
252 g_object_get(webWindowFeatures, "menubar-visible", &visible, NULL);
256 void ChromeClient::setResizable(bool)
261 static gboolean emitCloseWebViewSignalLater(WebKitWebView* view)
264 g_signal_emit_by_name(view, "close-web-view", &isHandled);
268 void ChromeClient::closeWindowSoon()
270 // We may not have a WebView as create-web-view can return NULL.
273 if (m_closeSoonTimer) // Don't call close-web-view more than once.
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("");
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);
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);
294 bool ChromeClient::canTakeFocus(FocusDirection)
296 return gtk_widget_get_can_focus(GTK_WIDGET(m_webView));
299 void ChromeClient::takeFocus(FocusDirection)
304 void ChromeClient::focusedNodeChanged(Node*)
308 void ChromeClient::focusedFrameChanged(Frame*)
312 bool ChromeClient::canRunBeforeUnloadConfirmPanel()
317 bool ChromeClient::runBeforeUnloadConfirmPanel(const WTF::String& message, WebCore::Frame* frame)
319 return runJavaScriptConfirm(frame, message);
322 void ChromeClient::addMessageToConsole(WebCore::MessageSource source, WebCore::MessageType type, WebCore::MessageLevel level, const WTF::String& message, unsigned int lineNumber, const WTF::String& sourceId)
325 g_signal_emit_by_name(m_webView, "console-message", message.utf8().data(), lineNumber, sourceId.utf8().data(), &retval);
328 void ChromeClient::runJavaScriptAlert(Frame* frame, const String& message)
331 g_signal_emit_by_name(m_webView, "script-alert", kit(frame), message.utf8().data(), &retval);
334 bool ChromeClient::runJavaScriptConfirm(Frame* frame, const String& message)
338 g_signal_emit_by_name(m_webView, "script-confirm", kit(frame), message.utf8().data(), &didConfirm, &retval);
339 return didConfirm == TRUE;
342 bool ChromeClient::runJavaScriptPrompt(Frame* frame, const String& message, const String& defaultValue, String& result)
346 g_signal_emit_by_name(m_webView, "script-prompt", kit(frame), message.utf8().data(), defaultValue.utf8().data(), &value, &retval);
348 result = String::fromUTF8(value);
355 void ChromeClient::setStatusbarText(const String& string)
357 CString stringMessage = string.utf8();
358 g_signal_emit_by_name(m_webView, "status-bar-text-changed", stringMessage.data());
361 bool ChromeClient::shouldInterruptJavaScript()
367 KeyboardUIMode ChromeClient::keyboardUIMode()
369 bool tabsToLinks = true;
370 if (DumpRenderTreeSupportGtk::dumpRenderTreeModeEnabled())
371 tabsToLinks = DumpRenderTreeSupportGtk::linksIncludedInFocusChain();
373 return tabsToLinks ? KeyboardAccessTabsToLinks : KeyboardAccessDefault;
376 IntRect ChromeClient::windowResizerRect() const
382 #if ENABLE(REGISTER_PROTOCOL_HANDLER)
383 void ChromeClient::registerProtocolHandler(const String& scheme, const String& baseURL, const String& url, const String& title)
389 static gboolean repaintEverythingSoonTimeout(ChromeClient* client)
395 static void clipOutOldWidgetArea(cairo_t* cr, const IntSize& oldSize, const IntSize& newSize)
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);
407 static void clearEverywhereInBackingStore(WebKitWebView* webView, cairo_t* cr)
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);
416 cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
420 void ChromeClient::widgetSizeChanged(const IntSize& oldWidgetSize, IntSize newSize)
422 WidgetBackingStore* backingStore = m_webView->priv->backingStore.get();
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.
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)));
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.
437 || newSize.width() > backingStore->size().width()
438 || newSize.height() > backingStore->size().height()) {
440 OwnPtr<WidgetBackingStore> newBackingStore =
441 WebCore::WidgetBackingStore::create(GTK_WIDGET(m_webView), newSize);
442 RefPtr<cairo_t> cr = adoptRef(cairo_create(newBackingStore->cairoSurface()));
444 clearEverywhereInBackingStore(m_webView, cr.get());
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.
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());
455 m_webView->priv->backingStore = newBackingStore.release();
456 backingStore = m_webView->priv->backingStore.get();
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());
466 // We need to force a redraw and ignore the framerate cap.
467 m_lastDisplayTime = 0;
468 m_dirtyRegion.unite(IntRect(IntPoint(), backingStore->size()));
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);
476 static void coalesceRectsIfPossible(const IntRect& clipRect, Vector<IntRect>& rects)
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;
498 rects.append(clipRect);
501 static void paintWebView(WebKitWebView* webView, Frame* frame, Region dirtyRegion)
503 if (!webView->priv->backingStore)
506 Vector<IntRect> rects = dirtyRegion.rects();
507 coalesceRectsIfPossible(dirtyRegion.bounds(), rects);
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];
516 if (webView->priv->transparent)
518 frame->view()->paint(&gc, rect);
523 gc.clip(dirtyRegion.bounds());
524 frame->page()->inspectorController()->drawHighlight(gc);
528 void ChromeClient::invalidateWidgetRect(const IntRect& rect)
530 #if USE(ACCELERATED_COMPOSITING)
531 AcceleratedCompositingContext* acContext = m_webView->priv->acceleratedCompositingContext.get();
532 if (acContext->enabled()) {
533 acContext->scheduleRootLayerRepaint(rect);
537 gtk_widget_queue_draw_area(GTK_WIDGET(m_webView),
539 rect.width(), rect.height());
542 void ChromeClient::performAllPendingScrolls()
544 if (!m_webView->priv->backingStore)
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);
554 m_rectsToScroll.clear();
555 m_scrollOffsets.clear();
558 void ChromeClient::paint(WebCore::Timer<ChromeClient>*)
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;
564 if (timeUntilNextDisplay > 0) {
565 m_displayTimer.startOneShot(timeUntilNextDisplay);
569 Frame* frame = core(m_webView)->mainFrame();
570 if (!frame || !frame->contentRenderer() || !frame->view())
573 frame->view()->updateLayoutAndStyleIfNeededRecursive();
574 performAllPendingScrolls();
575 paintWebView(m_webView, frame, m_dirtyRegion);
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));
586 const IntRect& rect = m_dirtyRegion.bounds();
587 invalidateWidgetRect(rect);
589 #if USE(ACCELERATED_COMPOSITING)
590 m_webView->priv->acceleratedCompositingContext->syncLayersNow();
591 m_webView->priv->acceleratedCompositingContext->renderLayersToWindow(rect);
594 m_dirtyRegion = Region();
595 m_lastDisplayTime = currentTime();
596 m_repaintSoonSourceId = 0;
599 void ChromeClient::invalidateRootView(const IntRect&, bool immediate)
603 void ChromeClient::invalidateContentsAndRootView(const IntRect& updateRect, bool immediate)
605 if (updateRect.isEmpty())
607 m_dirtyRegion.unite(updateRect);
608 m_displayTimer.startOneShot(0);
611 void ChromeClient::invalidateContentsForSlowScroll(const IntRect& updateRect, bool immediate)
613 invalidateContentsAndRootView(updateRect, immediate);
614 m_adjustmentWatcher.updateAdjustmentsFromScrollbarsLater();
617 void ChromeClient::scroll(const IntSize& delta, const IntRect& rectToScroll, const IntRect& clipRect)
619 m_rectsToScroll.append(rectToScroll);
620 m_scrollOffsets.append(delta);
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);
630 // Move the dirty parts.
631 Region movedDirtyRegionInScrollRect = intersect(translate(dirtyRegionInScrollRect, delta), rectToScroll);
633 // And add them back.
634 m_dirtyRegion.unite(movedDirtyRegionInScrollRect);
637 // Compute the scroll repaint region.
638 Region scrollRepaintRegion = subtract(rectToScroll, translate(rectToScroll, delta));
639 m_dirtyRegion.unite(scrollRepaintRegion);
640 m_displayTimer.startOneShot(0);
642 m_adjustmentWatcher.updateAdjustmentsFromScrollbarsLater();
645 IntRect ChromeClient::rootViewToScreen(const IntRect& rect) const
647 return IntRect(convertWidgetPointToScreenPoint(GTK_WIDGET(m_webView), rect.location()), rect.size());
650 IntPoint ChromeClient::screenToRootView(const IntPoint& point) const
652 IntPoint widgetPositionOnScreen = convertWidgetPointToScreenPoint(GTK_WIDGET(m_webView), IntPoint());
653 IntPoint result(point);
654 result.move(-widgetPositionOnScreen.x(), -widgetPositionOnScreen.y());
658 PlatformPageClient ChromeClient::platformPageClient() const
660 return GTK_WIDGET(m_webView);
663 void ChromeClient::contentsSizeChanged(Frame* frame, const IntSize& size) const
665 if (m_adjustmentWatcher.scrollbarsDisabled())
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);
675 requisition = widget->requisition;
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);
682 // If this was a main frame size change, update the scrollbars.
683 if (frame != frame->page()->mainFrame())
685 m_adjustmentWatcher.updateAdjustmentsFromScrollbarsLater();
688 void ChromeClient::scrollbarsModeDidChange() const
690 WebKitWebFrame* webFrame = webkit_web_view_get_main_frame(m_webView);
694 g_object_notify(G_OBJECT(webFrame), "horizontal-scrollbar-policy");
695 g_object_notify(G_OBJECT(webFrame), "vertical-scrollbar-policy");
698 g_signal_emit_by_name(webFrame, "scrollbars-policy-changed", &isHandled);
703 GtkWidget* parent = gtk_widget_get_parent(GTK_WIDGET(m_webView));
704 if (!parent || !GTK_IS_SCROLLED_WINDOW(parent))
707 GtkPolicyType horizontalPolicy = webkit_web_frame_get_horizontal_scrollbar_policy(webFrame);
708 GtkPolicyType verticalPolicy = webkit_web_frame_get_vertical_scrollbar_policy(webFrame);
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;
717 if (verticalPolicy == GTK_POLICY_NEVER)
718 verticalPolicy = GTK_POLICY_AUTOMATIC;
720 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(parent),
721 horizontalPolicy, verticalPolicy);
724 void ChromeClient::mouseDidMoveOverElement(const HitTestResult& hit, unsigned modifierFlags)
726 // check if the element is a link...
727 bool isLink = hit.isLiveLink();
729 KURL url = hit.absoluteLinkURL();
730 if (!url.isEmpty() && url != m_hoveredLinkURL) {
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;
737 } else if (!isLink && !m_hoveredLinkURL.isEmpty()) {
738 g_signal_emit_by_name(m_webView, "hovering-over-link", 0, 0);
739 m_hoveredLinkURL = KURL();
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();
747 m_webView->priv->tooltipArea = IntRect();
750 void ChromeClient::setToolTip(const String& toolTip, TextDirection)
752 webkit_web_view_set_tooltip_text(m_webView, toolTip.utf8().data());
755 void ChromeClient::print(Frame* frame)
757 WebKitWebFrame* webFrame = kit(frame);
758 gboolean isHandled = false;
759 g_signal_emit_by_name(m_webView, "print-requested", webFrame, &isHandled);
764 webkit_web_frame_print(webFrame);
767 #if ENABLE(SQL_DATABASE)
768 void ChromeClient::exceededDatabaseQuota(Frame* frame, const String& databaseName)
770 guint64 defaultQuota = webkit_get_default_web_database_quota();
771 DatabaseTracker::tracker().setQuota(frame->document()->securityOrigin(), defaultQuota);
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);
780 void ChromeClient::reachedMaxAppCacheSize(int64_t spaceNeeded)
782 // FIXME: Free some space.
786 void ChromeClient::reachedApplicationCacheOriginQuota(SecurityOrigin*, int64_t)
791 void ChromeClient::runOpenPanel(Frame*, PassRefPtr<FileChooser> prpFileChooser)
793 RefPtr<FileChooser> chooser = prpFileChooser;
795 GtkWidget* toplevel = gtk_widget_get_toplevel(GTK_WIDGET(m_webView));
796 if (!widgetIsOnscreenToplevelWindow(toplevel))
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,
806 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), chooser->settings().allowsMultipleFiles);
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) {
815 names.append(filenameToString(static_cast<char*>(item->data)));
818 chooser->chooseFiles(names);
820 gchar* filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
822 chooser->chooseFile(filenameToString(filename));
826 gtk_widget_destroy(dialog);
829 void ChromeClient::loadIconForFiles(const Vector<WTF::String>& filenames, WebCore::FileIconLoader* loader)
831 loader->notifyFinished(Icon::createIconForFiles(filenames));
834 void ChromeClient::dispatchViewportPropertiesDidChange(const ViewportArguments& arguments) const
836 // Recompute the viewport attributes making it valid.
837 webkitViewportAttributesRecompute(webkit_web_view_get_viewport_attributes(m_webView));
840 void ChromeClient::setCursor(const Cursor& cursor)
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());
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);
856 void ChromeClient::setCursorHiddenUntilMouseMoves(bool)
861 void ChromeClient::requestGeolocationPermissionForFrame(Frame* frame, Geolocation* geolocation)
863 WebKitWebFrame* webFrame = kit(frame);
864 GRefPtr<WebKitGeolocationPolicyDecision> policyDecision(adoptGRef(webkit_geolocation_policy_decision_new(webFrame, geolocation)));
866 gboolean isHandled = FALSE;
867 g_signal_emit_by_name(m_webView, "geolocation-policy-decision-requested", webFrame, policyDecision.get(), &isHandled);
869 webkit_geolocation_policy_deny(policyDecision.get());
872 void ChromeClient::cancelGeolocationPermissionRequestForFrame(WebCore::Frame* frame, WebCore::Geolocation*)
874 g_signal_emit_by_name(m_webView, "geolocation-policy-decision-cancelled", kit(frame));
877 bool ChromeClient::selectItemWritingDirectionIsNatural()
882 bool ChromeClient::selectItemAlignmentFollowsMenuWritingDirection()
887 bool ChromeClient::hasOpenedPopup() const
893 PassRefPtr<WebCore::PopupMenu> ChromeClient::createPopupMenu(WebCore::PopupMenuClient* client) const
895 return adoptRef(new PopupMenuGtk(client));
898 PassRefPtr<WebCore::SearchPopupMenu> ChromeClient::createSearchPopupMenu(WebCore::PopupMenuClient* client) const
900 return adoptRef(new SearchPopupMenuGtk(client));
905 bool ChromeClient::supportsFullscreenForNode(const Node* node)
907 return node->hasTagName(HTMLNames::videoTag);
910 void ChromeClient::enterFullscreenForNode(Node* node)
912 webViewEnterFullscreen(m_webView, node);
915 void ChromeClient::exitFullscreenForNode(Node* node)
917 webViewExitFullscreen(m_webView);
921 #if ENABLE(FULLSCREEN_API)
922 bool ChromeClient::supportsFullScreenForElement(const WebCore::Element* element, bool withKeyboard)
927 void ChromeClient::enterFullScreenForElement(WebCore::Element* element)
929 element->document()->webkitWillEnterFullScreenForElement(element);
930 m_adjustmentWatcher.disableAllScrollbars();
932 if (element->tagName() == "VIDEO")
933 enterFullscreenForNode(static_cast<Node*>(element));
935 element->document()->webkitDidEnterFullScreenForElement(element);
938 void ChromeClient::exitFullScreenForElement(WebCore::Element* element)
940 element->document()->webkitWillExitFullScreenForElement(element);
941 m_adjustmentWatcher.enableAllScrollbars();
943 if (element->tagName() == "VIDEO")
944 webViewExitFullscreen(m_webView);
946 element->document()->webkitDidExitFullScreenForElement(element);
950 #if USE(ACCELERATED_COMPOSITING)
951 void ChromeClient::attachRootGraphicsLayer(Frame* frame, GraphicsLayer* rootLayer)
953 m_webView->priv->acceleratedCompositingContext->attachRootGraphicsLayer(rootLayer);
956 void ChromeClient::setNeedsOneShotDrawingSynchronization()
958 m_webView->priv->acceleratedCompositingContext->markForSync();
961 void ChromeClient::scheduleCompositingLayerSync()
963 m_webView->priv->acceleratedCompositingContext->markForSync();
966 ChromeClient::CompositingTriggerFlags ChromeClient::allowedCompositingTriggers() const
968 if (!platformPageClient())
971 // Currently, we only support CSS 3D Transforms.
972 return ThreeDTransformTrigger;