2feb0644a73e111f41ac589dbc1206d6c918f46d
[WebKit-https.git] / WebKit / gtk / webkit / webkitwebinspector.cpp
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 }