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