Home | History | Annotate | Download | only in webkit
      1 /*
      2  * Copyright (C) 2008 Gustavo Noronha Silva
      3  * Copyright (C) 2008, 2009 Holger Hans Peter Freyther
      4  * Copyright (C) 2009 Collabora Ltd.
      5  *
      6  * This library is free software; you can redistribute it and/or
      7  * modify it under the terms of the GNU Library General Public
      8  * License as published by the Free Software Foundation; either
      9  * version 2 of the License, or (at your option) any later version.
     10  *
     11  * This library is distributed in the hope that it will be useful,
     12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14  * Library General Public License for more details.
     15  *
     16  * You should have received a copy of the GNU Library General Public License
     17  * along with this library; see the file COPYING.LIB.  If not, write to
     18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     19  * Boston, MA 02110-1301, USA.
     20  */
     21 
     22 #include "config.h"
     23 #include "webkitwebinspector.h"
     24 
     25 #include "FocusController.h"
     26 #include "Frame.h"
     27 #include <glib/gi18n-lib.h>
     28 #include "HitTestRequest.h"
     29 #include "HitTestResult.h"
     30 #include "InspectorClientGtk.h"
     31 #include "IntPoint.h"
     32 #include "Page.h"
     33 #include "RenderView.h"
     34 #include "webkitmarshal.h"
     35 #include "webkitprivate.h"
     36 
     37 /**
     38  * SECTION:webkitwebinspector
     39  * @short_description: Access to the WebKit Inspector
     40  *
     41  * The WebKit Inspector is a graphical tool to inspect and change
     42  * the content of a #WebKitWebView. It also includes an interactive
     43  * JavaScriptDebugger. Using this class one can get a GtkWidget which
     44  * can be embedded into an application to show the inspector.
     45  *
     46  * The inspector is available when the #WebKitWebSettings of the
     47  * #WebKitWebView has set the #WebKitWebSettings:enable-developer-extras
     48  * to true otherwise no inspector is available.
     49  *
     50  * <informalexample><programlisting>
     51  * /<!-- -->* Enable the developer extras *<!-- -->/
     52  * WebKitWebSettings *setting = webkit_web_view_get_settings (WEBKIT_WEB_VIEW(my_webview));
     53  * g_object_set (G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
     54  *
     55  * /<!-- -->* load some data or reload to be able to inspect the page*<!-- -->/
     56  * webkit_web_view_open (WEBKIT_WEB_VIEW(my_webview), "http://www.gnome.org");
     57  *
     58  * /<!-- -->* Embed the inspector somewhere *<!-- -->/
     59  * WebKitWebInspector *inspector = webkit_web_view_get_inspector (WEBKIT_WEB_VIEW(my_webview));
     60  * g_signal_connect (G_OBJECT (inspector), "inspect-web-view", G_CALLBACK(create_gtk_window_around_it), NULL);
     61  * g_signal_connect (G_OBJECT (inspector), "show-window", G_CALLBACK(show_inpector_window), NULL));
     62  * g_signal_connect (G_OBJECT (inspector), "notify::inspected-uri", G_CALLBACK(inspected_uri_changed_do_stuff), NULL);
     63  * </programlisting></informalexample>
     64  */
     65 
     66 using namespace WebKit;
     67 using namespace WebCore;
     68 
     69 enum {
     70     INSPECT_WEB_VIEW,
     71     SHOW_WINDOW,
     72     ATTACH_WINDOW,
     73     DETACH_WINDOW,
     74     CLOSE_WINDOW,
     75     FINISHED,
     76     LAST_SIGNAL
     77 };
     78 
     79 static guint webkit_web_inspector_signals[LAST_SIGNAL] = { 0, };
     80 
     81 enum {
     82     PROP_0,
     83 
     84     PROP_WEB_VIEW,
     85     PROP_INSPECTED_URI,
     86     PROP_JAVASCRIPT_PROFILING_ENABLED,
     87     PROP_TIMELINE_PROFILING_ENABLED
     88 };
     89 
     90 G_DEFINE_TYPE(WebKitWebInspector, webkit_web_inspector, G_TYPE_OBJECT)
     91 
     92 struct _WebKitWebInspectorPrivate {
     93     WebCore::Page* page;
     94     WebKitWebView* inspector_view;
     95     gchar* inspected_uri;
     96 };
     97 
     98 #define WEBKIT_WEB_INSPECTOR_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), WEBKIT_TYPE_WEB_INSPECTOR, WebKitWebInspectorPrivate))
     99 
    100 static void webkit_web_inspector_finalize(GObject* object);
    101 
    102 static void webkit_web_inspector_set_property(GObject* object, guint prop_id, const GValue* value, GParamSpec* pspec);
    103 
    104 static void webkit_web_inspector_get_property(GObject* object, guint prop_id, GValue* value, GParamSpec* pspec);
    105 
    106 static gboolean webkit_inspect_web_view_request_handled(GSignalInvocationHint* ihint, GValue* returnAccu, const GValue* handlerReturn, gpointer dummy)
    107 {
    108     gboolean continueEmission = TRUE;
    109     gpointer newWebView = g_value_get_object(handlerReturn);
    110     g_value_set_object(returnAccu, newWebView);
    111 
    112     if (newWebView)
    113         continueEmission = FALSE;
    114 
    115     return continueEmission;
    116 }
    117 
    118 static void webkit_web_inspector_class_init(WebKitWebInspectorClass* klass)
    119 {
    120     GObjectClass* gobject_class = G_OBJECT_CLASS(klass);
    121     gobject_class->finalize = webkit_web_inspector_finalize;
    122     gobject_class->set_property = webkit_web_inspector_set_property;
    123     gobject_class->get_property = webkit_web_inspector_get_property;
    124 
    125     /**
    126      * WebKitWebInspector::inspect-web-view:
    127      * @web_inspector: the object on which the signal is emitted
    128      * @web_view: the #WebKitWeb which will be inspected
    129      * @return: a newly allocated #WebKitWebView or %NULL
    130      *
    131      * Emitted when the user activates the 'inspect' context menu item
    132      * to inspect a web view. The application which is interested in
    133      * the inspector should create a window, or otherwise add the
    134      * #WebKitWebView it creates to an existing window.
    135      *
    136      * You don't need to handle the reference count of the
    137      * #WebKitWebView instance you create; the widget to which you add
    138      * it will do that.
    139      *
    140      * Since: 1.0.3
    141      */
    142     webkit_web_inspector_signals[INSPECT_WEB_VIEW] = g_signal_new("inspect-web-view",
    143             G_TYPE_FROM_CLASS(klass),
    144             (GSignalFlags)G_SIGNAL_RUN_LAST,
    145             0,
    146             webkit_inspect_web_view_request_handled,
    147             NULL,
    148             webkit_marshal_OBJECT__OBJECT,
    149             WEBKIT_TYPE_WEB_VIEW , 1,
    150             WEBKIT_TYPE_WEB_VIEW);
    151 
    152     /**
    153      * WebKitWebInspector::show-window:
    154      * @web_inspector: the object on which the signal is emitted
    155      * @return: %TRUE if the signal has been handled
    156      *
    157      * Emitted when the inspector window should be displayed. Notice
    158      * that the window must have been created already by handling
    159      * #WebKitWebInspector::inspect-web-view.
    160      *
    161      * Since: 1.0.3
    162      */
    163     webkit_web_inspector_signals[SHOW_WINDOW] = g_signal_new("show-window",
    164             G_TYPE_FROM_CLASS(klass),
    165             (GSignalFlags)G_SIGNAL_RUN_LAST,
    166             0,
    167             g_signal_accumulator_true_handled,
    168             NULL,
    169             webkit_marshal_BOOLEAN__VOID,
    170             G_TYPE_BOOLEAN , 0);
    171 
    172     /**
    173      * WebKitWebInspector::attach-window:
    174      * @web_inspector: the object on which the signal is emitted
    175      * @return: %TRUE if the signal has been handled
    176      *
    177      * Emitted when the inspector should appear at the same window as
    178      * the #WebKitWebView being inspected.
    179      *
    180      * Since: 1.0.3
    181      */
    182     webkit_web_inspector_signals[ATTACH_WINDOW] = g_signal_new("attach-window",
    183             G_TYPE_FROM_CLASS(klass),
    184             (GSignalFlags)G_SIGNAL_RUN_LAST,
    185             0,
    186             g_signal_accumulator_true_handled,
    187             NULL,
    188             webkit_marshal_BOOLEAN__VOID,
    189             G_TYPE_BOOLEAN , 0);
    190 
    191     /**
    192      * WebKitWebInspector::detach-window:
    193      * @web_inspector: the object on which the signal is emitted
    194      * @return: %TRUE if the signal has been handled
    195      *
    196      * Emitted when the inspector should appear in a separate window.
    197      *
    198      * Since: 1.0.3
    199      */
    200     webkit_web_inspector_signals[DETACH_WINDOW] = g_signal_new("detach-window",
    201             G_TYPE_FROM_CLASS(klass),
    202             (GSignalFlags)G_SIGNAL_RUN_LAST,
    203             0,
    204             g_signal_accumulator_true_handled,
    205             NULL,
    206             webkit_marshal_BOOLEAN__VOID,
    207             G_TYPE_BOOLEAN , 0);
    208 
    209     /**
    210      * WebKitWebInspector::close-window:
    211      * @web_inspector: the object on which the signal is emitted
    212      * @return: %TRUE if the signal has been handled
    213      *
    214      * Emitted when the inspector window should be closed. You can
    215      * destroy the window or hide it so that it can be displayed again
    216      * by handling #WebKitWebInspector::show-window later on.
    217      *
    218      * Notice that the inspected #WebKitWebView may no longer exist
    219      * when this signal is emitted.
    220      *
    221      * Notice, too, that if you decide to destroy the window,
    222      * #WebKitWebInspector::inspect-web-view will be emmited again, when the user
    223      * inspects an element.
    224      *
    225      * Since: 1.0.3
    226      */
    227     webkit_web_inspector_signals[CLOSE_WINDOW] = g_signal_new("close-window",
    228             G_TYPE_FROM_CLASS(klass),
    229             (GSignalFlags)G_SIGNAL_RUN_LAST,
    230             0,
    231             g_signal_accumulator_true_handled,
    232             NULL,
    233             webkit_marshal_BOOLEAN__VOID,
    234             G_TYPE_BOOLEAN , 0);
    235 
    236     /**
    237      * WebKitWebInspector::finished:
    238      * @web_inspector: the object on which the signal is emitted
    239      *
    240      * Emitted when the inspection is done. You should release your
    241      * references on the inspector at this time. The inspected
    242      * #WebKitWebView may no longer exist when this signal is emitted.
    243      *
    244      * Since: 1.0.3
    245      */
    246     webkit_web_inspector_signals[FINISHED] = g_signal_new("finished",
    247             G_TYPE_FROM_CLASS(klass),
    248             (GSignalFlags)G_SIGNAL_RUN_LAST,
    249             0,
    250             NULL,
    251             NULL,
    252             g_cclosure_marshal_VOID__VOID,
    253             G_TYPE_NONE , 0);
    254 
    255     /*
    256      * properties
    257      */
    258 
    259     /**
    260      * WebKitWebInspector:web-view:
    261      *
    262      * The Web View that renders the Web Inspector itself.
    263      *
    264      * Since: 1.0.3
    265      */
    266     g_object_class_install_property(gobject_class, PROP_WEB_VIEW,
    267                                     g_param_spec_object("web-view",
    268                                                         _("Web View"),
    269                                                         _("The Web View that renders the Web Inspector itself"),
    270                                                         WEBKIT_TYPE_WEB_VIEW,
    271                                                         WEBKIT_PARAM_READABLE));
    272 
    273     /**
    274      * WebKitWebInspector:inspected-uri:
    275      *
    276      * The URI that is currently being inspected.
    277      *
    278      * Since: 1.0.3
    279      */
    280     g_object_class_install_property(gobject_class, PROP_INSPECTED_URI,
    281                                     g_param_spec_string("inspected-uri",
    282                                                         _("Inspected URI"),
    283                                                         _("The URI that is currently being inspected"),
    284                                                         NULL,
    285                                                         WEBKIT_PARAM_READABLE));
    286 
    287     /**
    288     * WebKitWebInspector:javascript-profiling-enabled
    289     *
    290     * This is enabling JavaScript profiling in the Inspector. This means
    291     * that Console.profiles will return the profiles.
    292     *
    293     * Since: 1.1.1
    294     */
    295     g_object_class_install_property(gobject_class,
    296                                     PROP_JAVASCRIPT_PROFILING_ENABLED,
    297                                     g_param_spec_boolean(
    298                                         "javascript-profiling-enabled",
    299                                         _("Enable JavaScript profiling"),
    300                                         _("Profile the executed JavaScript."),
    301                                         FALSE,
    302                                         WEBKIT_PARAM_READWRITE));
    303 
    304     /**
    305     * WebKitWebInspector:timeline-profiling-enabled
    306     *
    307     * This is enabling Timeline profiling in the Inspector.
    308     *
    309     * Since: 1.1.17
    310     */
    311     g_object_class_install_property(gobject_class,
    312                                     PROP_TIMELINE_PROFILING_ENABLED,
    313                                     g_param_spec_boolean(
    314                                         "timeline-profiling-enabled",
    315                                         _("Enable Timeline profiling"),
    316                                         _("Profile the WebCore instrumentation."),
    317                                         FALSE,
    318                                         WEBKIT_PARAM_READWRITE));
    319 
    320     g_type_class_add_private(klass, sizeof(WebKitWebInspectorPrivate));
    321 }
    322 
    323 static void webkit_web_inspector_init(WebKitWebInspector* web_inspector)
    324 {
    325     web_inspector->priv = WEBKIT_WEB_INSPECTOR_GET_PRIVATE(web_inspector);
    326 }
    327 
    328 static void webkit_web_inspector_finalize(GObject* object)
    329 {
    330     WebKitWebInspector* web_inspector = WEBKIT_WEB_INSPECTOR(object);
    331     WebKitWebInspectorPrivate* priv = web_inspector->priv;
    332 
    333     if (priv->inspector_view)
    334         g_object_unref(priv->inspector_view);
    335 
    336     if (priv->inspected_uri)
    337         g_free(priv->inspected_uri);
    338 
    339     G_OBJECT_CLASS(webkit_web_inspector_parent_class)->finalize(object);
    340 }
    341 
    342 static void webkit_web_inspector_set_property(GObject* object, guint prop_id, const GValue* value, GParamSpec* pspec)
    343 {
    344     WebKitWebInspector* web_inspector = WEBKIT_WEB_INSPECTOR(object);
    345     WebKitWebInspectorPrivate* priv = web_inspector->priv;
    346 
    347     switch(prop_id) {
    348     case PROP_JAVASCRIPT_PROFILING_ENABLED: {
    349 #if ENABLE(JAVASCRIPT_DEBUGGER)
    350         bool enabled = g_value_get_boolean(value);
    351         WebCore::InspectorController* controller = priv->page->inspectorController();
    352         if (enabled)
    353             controller->enableProfiler();
    354         else
    355             controller->disableProfiler();
    356 #else
    357         g_message("PROP_JAVASCRIPT_PROFILING_ENABLED is not work because of the javascript debugger is disabled\n");
    358 #endif
    359         break;
    360     }
    361     case PROP_TIMELINE_PROFILING_ENABLED: {
    362         bool enabled = g_value_get_boolean(value);
    363         WebCore::InspectorController* controller = priv->page->inspectorController();
    364         if (enabled)
    365             controller->startTimelineProfiler();
    366         else
    367             controller->stopTimelineProfiler();
    368         break;
    369     }
    370     default:
    371         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
    372         break;
    373     }
    374 }
    375 
    376 static void webkit_web_inspector_get_property(GObject* object, guint prop_id, GValue* value, GParamSpec* pspec)
    377 {
    378     WebKitWebInspector* web_inspector = WEBKIT_WEB_INSPECTOR(object);
    379     WebKitWebInspectorPrivate* priv = web_inspector->priv;
    380 
    381     switch (prop_id) {
    382     case PROP_WEB_VIEW:
    383         g_value_set_object(value, priv->inspector_view);
    384         break;
    385     case PROP_INSPECTED_URI:
    386         g_value_set_string(value, priv->inspected_uri);
    387         break;
    388     case PROP_JAVASCRIPT_PROFILING_ENABLED:
    389 #if ENABLE(JAVASCRIPT_DEBUGGER)
    390         g_value_set_boolean(value, priv->page->inspectorController()->profilerEnabled());
    391 #else
    392         g_message("PROP_JAVASCRIPT_PROFILING_ENABLED is not work because of the javascript debugger is disabled\n");
    393 #endif
    394         break;
    395     case PROP_TIMELINE_PROFILING_ENABLED:
    396         g_value_set_boolean(value, priv->page->inspectorController()->timelineAgent() != 0);
    397         break;
    398     default:
    399         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
    400         break;
    401     }
    402 }
    403 
    404 // internal use only
    405 void webkit_web_inspector_set_web_view(WebKitWebInspector *web_inspector, WebKitWebView *web_view)
    406 {
    407     g_return_if_fail(WEBKIT_IS_WEB_INSPECTOR(web_inspector));
    408     g_return_if_fail(WEBKIT_IS_WEB_VIEW(web_view));
    409 
    410     WebKitWebInspectorPrivate* priv = web_inspector->priv;
    411 
    412     if (priv->inspector_view)
    413         g_object_unref(priv->inspector_view);
    414 
    415     g_object_ref(web_view);
    416     priv->inspector_view = web_view;
    417 }
    418 
    419 /**
    420  * webkit_web_inspector_get_web_view:
    421  *
    422  * Obtains the #WebKitWebView that is used to render the
    423  * inspector. The #WebKitWebView instance is created by the
    424  * application, by handling the #WebKitWebInspector::inspect-web-view signal. This means
    425  * that this method may return %NULL if the user hasn't inspected
    426  * anything.
    427  *
    428  * Returns: the #WebKitWebView instance that is used to render the
    429  * inspector or %NULL if it is not yet created.
    430  *
    431  * Since: 1.0.3
    432  **/
    433 WebKitWebView* webkit_web_inspector_get_web_view(WebKitWebInspector *web_inspector)
    434 {
    435     WebKitWebInspectorPrivate* priv = web_inspector->priv;
    436 
    437     return priv->inspector_view;
    438 }
    439 
    440 // internal use only
    441 void webkit_web_inspector_set_inspected_uri(WebKitWebInspector* web_inspector, const gchar* inspected_uri)
    442 {
    443     g_return_if_fail(WEBKIT_IS_WEB_INSPECTOR(web_inspector));
    444 
    445     WebKitWebInspectorPrivate* priv = web_inspector->priv;
    446 
    447     g_free(priv->inspected_uri);
    448     priv->inspected_uri = g_strdup(inspected_uri);
    449 }
    450 
    451 /**
    452  * webkit_web_inspector_get_inspected_uri:
    453  *
    454  * Obtains the URI that is currently being inspected.
    455  *
    456  * Returns: a pointer to the URI as an internally allocated string; it
    457  * should not be freed, modified or stored.
    458  *
    459  * Since: 1.0.3
    460  **/
    461 const gchar* webkit_web_inspector_get_inspected_uri(WebKitWebInspector *web_inspector)
    462 {
    463     WebKitWebInspectorPrivate* priv = web_inspector->priv;
    464 
    465     return priv->inspected_uri;
    466 }
    467 
    468 void
    469 webkit_web_inspector_set_inspector_client(WebKitWebInspector* web_inspector, WebCore::Page* page)
    470 {
    471     WebKitWebInspectorPrivate* priv = web_inspector->priv;
    472 
    473     priv->page = page;
    474 }
    475 
    476 /**
    477  * webkit_web_inspector_show:
    478  * @web_inspector: the #WebKitWebInspector that will be shown
    479  *
    480  * Causes the Web Inspector to be shown.
    481  *
    482  * Since: 1.1.17
    483  */
    484 void webkit_web_inspector_show(WebKitWebInspector* webInspector)
    485 {
    486     g_return_if_fail(WEBKIT_IS_WEB_INSPECTOR(webInspector));
    487 
    488     WebKitWebInspectorPrivate* priv = webInspector->priv;
    489 
    490     Frame* frame = priv->page->focusController()->focusedOrMainFrame();
    491     FrameView* view = frame->view();
    492 
    493     if (!view)
    494         return;
    495 
    496     priv->page->inspectorController()->show();
    497 }
    498 
    499 /**
    500  * webkit_web_inspector_inspect_coordinates:
    501  * @web_inspector: the #WebKitWebInspector that will do the inspection
    502  * @x: the X coordinate of the node to be inspected
    503  * @y: the Y coordinate of the node to be inspected
    504  *
    505  * Causes the Web Inspector to inspect the node that is located at the
    506  * given coordinates of the widget. The coordinates should be relative
    507  * to the #WebKitWebView widget, not to the scrollable content, and
    508  * may be obtained from a #GdkEvent directly.
    509  *
    510  * This means @x, and @y being zero doesn't guarantee you will hit the
    511  * left-most top corner of the content, since the contents may have
    512  * been scrolled.
    513  *
    514  * Since: 1.1.17
    515  */
    516 void webkit_web_inspector_inspect_coordinates(WebKitWebInspector* webInspector, gdouble x, gdouble y)
    517 {
    518     g_return_if_fail(WEBKIT_IS_WEB_INSPECTOR(webInspector));
    519     g_return_if_fail(x >= 0 && y >= 0);
    520 
    521     WebKitWebInspectorPrivate* priv = webInspector->priv;
    522 
    523     Frame* frame = priv->page->focusController()->focusedOrMainFrame();
    524     FrameView* view = frame->view();
    525 
    526     if (!view)
    527         return;
    528 
    529     HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active);
    530     IntPoint documentPoint = view->windowToContents(IntPoint(static_cast<int>(x), static_cast<int>(y)));
    531     HitTestResult result(documentPoint);
    532 
    533     frame->contentRenderer()->layer()->hitTest(request, result);
    534     priv->page->inspectorController()->inspect(result.innerNonSharedNode());
    535 }
    536 
    537 /**
    538  * webkit_web_inspector_close:
    539  * @web_inspector: the #WebKitWebInspector that will be closed
    540  *
    541  * Causes the Web Inspector to be closed.
    542  *
    543  * Since: 1.1.17
    544  */
    545 void webkit_web_inspector_close(WebKitWebInspector* webInspector)
    546 {
    547     g_return_if_fail(WEBKIT_IS_WEB_INSPECTOR(webInspector));
    548 
    549     WebKitWebInspectorPrivate* priv = webInspector->priv;
    550     priv->page->inspectorController()->close();
    551 }
    552 
    553 void webkit_web_inspector_execute_script(WebKitWebInspector* webInspector, long callId, const gchar* script)
    554 {
    555     g_return_if_fail(WEBKIT_IS_WEB_INSPECTOR(webInspector));
    556     g_return_if_fail(script);
    557 
    558     WebKitWebInspectorPrivate* priv = webInspector->priv;
    559     priv->page->inspectorController()->evaluateForTestInFrontend(callId, script);
    560 }
    561