Home | History | Annotate | Download | only in WebCoreSupport
      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