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).
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25 #include "ChromeClientGtk.h"
29 #include "DumpRenderTreeSupportGtk.h"
31 #include "FileChooser.h"
32 #include "FileSystem.h"
33 #include "FloatRect.h"
34 #include "FrameLoadRequest.h"
35 #include "FrameView.h"
36 #include "GtkVersioning.h"
37 #include "HTMLNames.h"
38 #include "HitTestResult.h"
42 #include "NavigationAction.h"
43 #include "NotImplemented.h"
44 #include "PlatformString.h"
45 #include "PopupMenuClient.h"
46 #include "PopupMenuGtk.h"
47 #include "SearchPopupMenuGtk.h"
48 #include "SecurityOrigin.h"
49 #include "WindowFeatures.h"
50 #include "webkitgeolocationpolicydecision.h"
51 #include "webkitnetworkrequest.h"
52 #include "webkitprivate.h"
53 #include "webkitwebview.h"
54 #include "webkitwebviewprivate.h"
56 #include <glib/gi18n-lib.h>
58 #include <wtf/text/CString.h>
61 #include "DatabaseTracker.h"
64 using namespace WebCore;
68 ChromeClient::ChromeClient(WebKitWebView* webView)
74 void ChromeClient::chromeDestroyed()
79 FloatRect ChromeClient::windowRect()
81 GtkWidget* window = gtk_widget_get_toplevel(GTK_WIDGET(m_webView));
82 if (gtk_widget_is_toplevel(window)) {
83 gint left, top, width, height;
84 gtk_window_get_position(GTK_WINDOW(window), &left, &top);
85 gtk_window_get_size(GTK_WINDOW(window), &width, &height);
86 return IntRect(left, top, width, height);
91 void ChromeClient::setWindowRect(const FloatRect& rect)
93 IntRect intrect = IntRect(rect);
94 WebKitWebWindowFeatures* webWindowFeatures = webkit_web_view_get_window_features(m_webView);
96 g_object_set(webWindowFeatures,
99 "width", intrect.width(),
100 "height", intrect.height(),
103 gboolean autoResizeWindow;
104 WebKitWebSettings* settings = webkit_web_view_get_settings(m_webView);
105 g_object_get(settings, "auto-resize-window", &autoResizeWindow, NULL);
107 if (!autoResizeWindow)
110 GtkWidget* window = gtk_widget_get_toplevel(GTK_WIDGET(m_webView));
111 if (gtk_widget_is_toplevel(window)) {
112 gtk_window_move(GTK_WINDOW(window), intrect.x(), intrect.y());
113 gtk_window_resize(GTK_WINDOW(window), intrect.width(), intrect.height());
117 FloatRect ChromeClient::pageRect()
119 GtkAllocation allocation;
120 #if GTK_CHECK_VERSION(2, 18, 0)
121 gtk_widget_get_allocation(GTK_WIDGET(m_webView), &allocation);
123 allocation = GTK_WIDGET(m_webView)->allocation;
125 return IntRect(allocation.x, allocation.y, allocation.width, allocation.height);
128 float ChromeClient::scaleFactor()
134 void ChromeClient::focus()
136 gtk_widget_grab_focus(GTK_WIDGET(m_webView));
139 void ChromeClient::unfocus()
141 GtkWidget* window = gtk_widget_get_toplevel(GTK_WIDGET(m_webView));
142 if (gtk_widget_is_toplevel(window))
143 gtk_window_set_focus(GTK_WINDOW(window), NULL);
146 Page* ChromeClient::createWindow(Frame* frame, const FrameLoadRequest& frameLoadRequest, const WindowFeatures& coreFeatures, const NavigationAction&)
148 WebKitWebView* webView = 0;
150 g_signal_emit_by_name(m_webView, "create-web-view", kit(frame), &webView);
155 WebKitWebWindowFeatures* webWindowFeatures = webkit_web_window_features_new_from_core_features(coreFeatures);
156 g_object_set(webView, "window-features", webWindowFeatures, NULL);
157 g_object_unref(webWindowFeatures);
159 if (!frameLoadRequest.isEmpty())
160 webkit_web_view_open(webView, frameLoadRequest.resourceRequest().url().string().utf8().data());
162 return core(webView);
165 void ChromeClient::show()
167 webkit_web_view_notify_ready(m_webView);
170 bool ChromeClient::canRunModal()
176 void ChromeClient::runModal()
181 void ChromeClient::setToolbarsVisible(bool visible)
183 WebKitWebWindowFeatures* webWindowFeatures = webkit_web_view_get_window_features(m_webView);
185 g_object_set(webWindowFeatures, "toolbar-visible", visible, NULL);
188 bool ChromeClient::toolbarsVisible()
190 WebKitWebWindowFeatures* webWindowFeatures = webkit_web_view_get_window_features(m_webView);
193 g_object_get(webWindowFeatures, "toolbar-visible", &visible, NULL);
197 void ChromeClient::setStatusbarVisible(bool visible)
199 WebKitWebWindowFeatures* webWindowFeatures = webkit_web_view_get_window_features(m_webView);
201 g_object_set(webWindowFeatures, "statusbar-visible", visible, NULL);
204 bool ChromeClient::statusbarVisible()
206 WebKitWebWindowFeatures* webWindowFeatures = webkit_web_view_get_window_features(m_webView);
209 g_object_get(webWindowFeatures, "statusbar-visible", &visible, NULL);
213 void ChromeClient::setScrollbarsVisible(bool visible)
215 WebKitWebWindowFeatures* webWindowFeatures = webkit_web_view_get_window_features(m_webView);
217 g_object_set(webWindowFeatures, "scrollbar-visible", visible, NULL);
220 bool ChromeClient::scrollbarsVisible() {
221 WebKitWebWindowFeatures* webWindowFeatures = webkit_web_view_get_window_features(m_webView);
224 g_object_get(webWindowFeatures, "scrollbar-visible", &visible, NULL);
228 void ChromeClient::setMenubarVisible(bool visible)
230 WebKitWebWindowFeatures* webWindowFeatures = webkit_web_view_get_window_features(m_webView);
232 g_object_set(webWindowFeatures, "menubar-visible", visible, NULL);
235 bool ChromeClient::menubarVisible()
237 WebKitWebWindowFeatures* webWindowFeatures = webkit_web_view_get_window_features(m_webView);
240 g_object_get(webWindowFeatures, "menubar-visible", &visible, NULL);
244 void ChromeClient::setResizable(bool)
249 void ChromeClient::closeWindowSoon()
251 // We may not have a WebView as create-web-view can return NULL.
255 webkit_web_view_stop_loading(m_webView);
257 gboolean isHandled = false;
258 g_signal_emit_by_name(m_webView, "close-web-view", &isHandled);
264 bool ChromeClient::canTakeFocus(FocusDirection)
266 return gtk_widget_get_can_focus(GTK_WIDGET(m_webView));
269 void ChromeClient::takeFocus(FocusDirection)
274 void ChromeClient::focusedNodeChanged(Node*)
278 void ChromeClient::focusedFrameChanged(Frame*)
282 bool ChromeClient::canRunBeforeUnloadConfirmPanel()
287 bool ChromeClient::runBeforeUnloadConfirmPanel(const WTF::String& message, WebCore::Frame* frame)
289 return runJavaScriptConfirm(frame, message);
292 void ChromeClient::addMessageToConsole(WebCore::MessageSource source, WebCore::MessageType type, WebCore::MessageLevel level, const WTF::String& message, unsigned int lineNumber, const WTF::String& sourceId)
295 g_signal_emit_by_name(m_webView, "console-message", message.utf8().data(), lineNumber, sourceId.utf8().data(), &retval);
298 void ChromeClient::runJavaScriptAlert(Frame* frame, const String& message)
301 g_signal_emit_by_name(m_webView, "script-alert", kit(frame), message.utf8().data(), &retval);
304 bool ChromeClient::runJavaScriptConfirm(Frame* frame, const String& message)
308 g_signal_emit_by_name(m_webView, "script-confirm", kit(frame), message.utf8().data(), &didConfirm, &retval);
309 return didConfirm == TRUE;
312 bool ChromeClient::runJavaScriptPrompt(Frame* frame, const String& message, const String& defaultValue, String& result)
316 g_signal_emit_by_name(m_webView, "script-prompt", kit(frame), message.utf8().data(), defaultValue.utf8().data(), &value, &retval);
318 result = String::fromUTF8(value);
325 void ChromeClient::setStatusbarText(const String& string)
327 CString stringMessage = string.utf8();
328 g_signal_emit_by_name(m_webView, "status-bar-text-changed", stringMessage.data());
331 bool ChromeClient::shouldInterruptJavaScript()
337 bool ChromeClient::tabsToLinks() const
339 if (DumpRenderTreeSupportGtk::dumpRenderTreeModeEnabled())
340 return DumpRenderTreeSupportGtk::linksIncludedInFocusChain();
345 IntRect ChromeClient::windowResizerRect() const
351 void ChromeClient::invalidateWindow(const IntRect&, bool)
356 void ChromeClient::invalidateContentsAndWindow(const IntRect& updateRect, bool immediate)
358 GdkRectangle rect = updateRect;
359 GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(m_webView));
362 gdk_window_invalidate_rect(window, &rect, FALSE);
363 // We don't currently do immediate updates since they delay other UI elements.
365 // gdk_window_process_updates(window, FALSE);
369 void ChromeClient::invalidateContentsForSlowScroll(const IntRect& updateRect, bool immediate)
371 invalidateContentsAndWindow(updateRect, immediate);
374 void ChromeClient::scroll(const IntSize& delta, const IntRect& rectToScroll, const IntRect& clipRect)
376 GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(m_webView));
380 // We cannot use gdk_window_scroll here because it is only able to
381 // scroll the whole window at once, and we often need to scroll
382 // portions of the window only (think frames).
383 GdkRectangle area = clipRect;
384 GdkRectangle moveRect;
386 GdkRectangle sourceRect = area;
387 sourceRect.x -= delta.width();
388 sourceRect.y -= delta.height();
390 #ifdef GTK_API_VERSION_2
391 GdkRegion* invalidRegion = gdk_region_rectangle(&area);
393 if (gdk_rectangle_intersect(&area, &sourceRect, &moveRect)) {
394 GdkRegion* moveRegion = gdk_region_rectangle(&moveRect);
395 gdk_window_move_region(window, moveRegion, delta.width(), delta.height());
396 gdk_region_offset(moveRegion, delta.width(), delta.height());
397 gdk_region_subtract(invalidRegion, moveRegion);
398 gdk_region_destroy(moveRegion);
401 gdk_window_invalidate_region(window, invalidRegion, FALSE);
402 gdk_region_destroy(invalidRegion);
404 cairo_region_t* invalidRegion = cairo_region_create_rectangle(&area);
406 if (gdk_rectangle_intersect(&area, &sourceRect, &moveRect)) {
407 cairo_region_t* moveRegion = cairo_region_create_rectangle(&moveRect);
408 gdk_window_move_region(window, moveRegion, delta.width(), delta.height());
409 cairo_region_translate(moveRegion, delta.width(), delta.height());
410 cairo_region_subtract(invalidRegion, moveRegion);
411 cairo_region_destroy(moveRegion);
414 gdk_window_invalidate_region(window, invalidRegion, FALSE);
415 cairo_region_destroy(invalidRegion);
420 // FIXME: this does not take into account the WM decorations
421 static IntPoint widgetScreenPosition(GtkWidget* widget)
423 GtkWidget* window = gtk_widget_get_toplevel(widget);
424 int widgetX = 0, widgetY = 0;
426 gtk_widget_translate_coordinates(widget, window, 0, 0, &widgetX, &widgetY);
428 IntPoint result(widgetX, widgetY);
429 int originX, originY;
430 gdk_window_get_origin(gtk_widget_get_window(window), &originX, &originY);
431 result.move(originX, originY);
436 IntRect ChromeClient::windowToScreen(const IntRect& rect) const
438 IntRect result(rect);
439 IntPoint screenPosition = widgetScreenPosition(GTK_WIDGET(m_webView));
440 result.move(screenPosition.x(), screenPosition.y());
445 IntPoint ChromeClient::screenToWindow(const IntPoint& point) const
447 IntPoint result(point);
448 IntPoint screenPosition = widgetScreenPosition(GTK_WIDGET(m_webView));
449 result.move(-screenPosition.x(), -screenPosition.y());
454 PlatformPageClient ChromeClient::platformPageClient() const
456 return GTK_WIDGET(m_webView);
459 void ChromeClient::contentsSizeChanged(Frame* frame, const IntSize& size) const
461 // We need to queue a resize request only if the size changed,
462 // otherwise we get into an infinite loop!
463 GtkWidget* widget = GTK_WIDGET(m_webView);
464 GtkRequisition requisition;
465 #if GTK_CHECK_VERSION(2, 20, 0)
466 gtk_widget_get_requisition(widget, &requisition);
468 requisition = widget->requisition;
470 if (gtk_widget_get_realized(widget)
471 && (requisition.height != size.height())
472 || (requisition.width != size.width()))
473 gtk_widget_queue_resize_no_redraw(widget);
476 void ChromeClient::scrollbarsModeDidChange() const
478 WebKitWebFrame* webFrame = webkit_web_view_get_main_frame(m_webView);
480 g_object_notify(G_OBJECT(webFrame), "horizontal-scrollbar-policy");
481 g_object_notify(G_OBJECT(webFrame), "vertical-scrollbar-policy");
484 g_signal_emit_by_name(webFrame, "scrollbars-policy-changed", &isHandled);
489 GtkWidget* parent = gtk_widget_get_parent(GTK_WIDGET(m_webView));
490 if (!parent || !GTK_IS_SCROLLED_WINDOW(parent))
493 GtkPolicyType horizontalPolicy = webkit_web_frame_get_horizontal_scrollbar_policy(webFrame);
494 GtkPolicyType verticalPolicy = webkit_web_frame_get_vertical_scrollbar_policy(webFrame);
496 // ScrolledWindow doesn't like to display only part of a widget if
497 // the scrollbars are completely disabled; We have a disparity
498 // here on what the policy requested by the web app is and what we
499 // can represent; the idea is not to show scrollbars, only.
500 if (horizontalPolicy == GTK_POLICY_NEVER)
501 horizontalPolicy = GTK_POLICY_AUTOMATIC;
503 if (verticalPolicy == GTK_POLICY_NEVER)
504 verticalPolicy = GTK_POLICY_AUTOMATIC;
506 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(parent),
507 horizontalPolicy, verticalPolicy);
510 void ChromeClient::mouseDidMoveOverElement(const HitTestResult& hit, unsigned modifierFlags)
512 // check if the element is a link...
513 bool isLink = hit.isLiveLink();
515 KURL url = hit.absoluteLinkURL();
516 if (!url.isEmpty() && url != m_hoveredLinkURL) {
518 CString titleString = hit.title(dir).utf8();
519 CString urlString = url.prettyURL().utf8();
520 g_signal_emit_by_name(m_webView, "hovering-over-link", titleString.data(), urlString.data());
521 m_hoveredLinkURL = url;
523 } else if (!isLink && !m_hoveredLinkURL.isEmpty()) {
524 g_signal_emit_by_name(m_webView, "hovering-over-link", 0, 0);
525 m_hoveredLinkURL = KURL();
528 if (Node* node = hit.innerNonSharedNode()) {
529 Frame* frame = node->document()->frame();
530 FrameView* view = frame ? frame->view() : 0;
531 m_webView->priv->tooltipArea = view ? view->contentsToWindow(node->getRect()) : IntRect();
533 m_webView->priv->tooltipArea = IntRect();
536 void ChromeClient::setToolTip(const String& toolTip, TextDirection)
538 webkit_web_view_set_tooltip_text(m_webView, toolTip.utf8().data());
541 void ChromeClient::print(Frame* frame)
543 WebKitWebFrame* webFrame = kit(frame);
544 gboolean isHandled = false;
545 g_signal_emit_by_name(m_webView, "print-requested", webFrame, &isHandled);
550 webkit_web_frame_print(webFrame);
554 void ChromeClient::exceededDatabaseQuota(Frame* frame, const String& databaseName)
556 guint64 defaultQuota = webkit_get_default_web_database_quota();
557 DatabaseTracker::tracker().setQuota(frame->document()->securityOrigin(), defaultQuota);
559 WebKitWebFrame* webFrame = kit(frame);
560 WebKitWebView* webView = getViewFromFrame(webFrame);
562 WebKitSecurityOrigin* origin = webkit_web_frame_get_security_origin(webFrame);
563 WebKitWebDatabase* webDatabase = webkit_security_origin_get_web_database(origin, databaseName.utf8().data());
564 g_signal_emit_by_name(webView, "database-quota-exceeded", webFrame, webDatabase);
568 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
569 void ChromeClient::reachedMaxAppCacheSize(int64_t spaceNeeded)
571 // FIXME: Free some space.
575 void ChromeClient::reachedApplicationCacheOriginQuota(SecurityOrigin*)
581 void ChromeClient::runOpenPanel(Frame*, PassRefPtr<FileChooser> prpFileChooser)
583 RefPtr<FileChooser> chooser = prpFileChooser;
585 GtkWidget* dialog = gtk_file_chooser_dialog_new(_("Upload File"),
586 GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(platformPageClient()))),
587 GTK_FILE_CHOOSER_ACTION_OPEN,
588 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
589 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
592 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), chooser->allowsMultipleFiles());
594 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
595 if (gtk_file_chooser_get_select_multiple(GTK_FILE_CHOOSER(dialog))) {
596 GSList* filenames = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
597 Vector<String> names;
598 for (GSList* item = filenames ; item ; item = item->next) {
601 names.append(filenameToString(static_cast<char*>(item->data)));
604 g_slist_free(filenames);
605 chooser->chooseFiles(names);
607 gchar* filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
609 chooser->chooseFile(filenameToString(filename));
613 gtk_widget_destroy(dialog);
616 void ChromeClient::chooseIconForFiles(const Vector<WTF::String>& filenames, WebCore::FileChooser* chooser)
618 chooser->iconLoaded(Icon::createIconForFiles(filenames));
621 void ChromeClient::dispatchViewportDataDidChange(const ViewportArguments& arguments) const
623 // Recompute the viewport attributes making it valid.
624 webkitViewportAttributesRecompute(webkit_web_view_get_viewport_attributes(m_webView));
627 void ChromeClient::setCursor(const Cursor&)
632 void ChromeClient::requestGeolocationPermissionForFrame(Frame* frame, Geolocation* geolocation)
634 WebKitWebFrame* webFrame = kit(frame);
635 WebKitWebView* webView = getViewFromFrame(webFrame);
637 PlatformRefPtr<WebKitGeolocationPolicyDecision> policyDecision(adoptPlatformRef(webkit_geolocation_policy_decision_new(webFrame, geolocation)));
639 gboolean isHandled = FALSE;
640 g_signal_emit_by_name(webView, "geolocation-policy-decision-requested", webFrame, policyDecision.get(), &isHandled);
642 webkit_geolocation_policy_deny(policyDecision.get());
645 void ChromeClient::cancelGeolocationPermissionRequestForFrame(WebCore::Frame* frame, WebCore::Geolocation*)
647 WebKitWebFrame* webFrame = kit(frame);
648 WebKitWebView* webView = getViewFromFrame(webFrame);
649 g_signal_emit_by_name(webView, "geolocation-policy-decision-cancelled", webFrame);
652 bool ChromeClient::selectItemWritingDirectionIsNatural()
657 PassRefPtr<WebCore::PopupMenu> ChromeClient::createPopupMenu(WebCore::PopupMenuClient* client) const
659 return adoptRef(new PopupMenuGtk(client));
662 PassRefPtr<WebCore::SearchPopupMenu> ChromeClient::createSearchPopupMenu(WebCore::PopupMenuClient* client) const
664 return adoptRef(new SearchPopupMenuGtk(client));
669 bool ChromeClient::supportsFullscreenForNode(const Node* node)
671 return node->hasTagName(HTMLNames::videoTag);
674 void ChromeClient::enterFullscreenForNode(Node* node)
676 WebCore::Frame* frame = node->document()->frame();
677 WebKitWebFrame* webFrame = kit(frame);
678 WebKitWebView* webView = getViewFromFrame(webFrame);
679 webViewEnterFullscreen(webView, node);
682 void ChromeClient::exitFullscreenForNode(Node* node)
684 WebCore::Frame* frame = node->document()->frame();
685 WebKitWebFrame* webFrame = kit(frame);
686 WebKitWebView* webView = getViewFromFrame(webFrame);
687 webViewExitFullscreen(webView);