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
webkit_inspect_web_view_request_handled(GSignalInvocationHint * ihint,GValue * returnAccu,const GValue * handlerReturn,gpointer dummy)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
webkit_web_inspector_class_init(WebKitWebInspectorClass * klass)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
webkit_web_inspector_init(WebKitWebInspector * web_inspector)323 static void webkit_web_inspector_init(WebKitWebInspector* web_inspector)
324 {
325 web_inspector->priv = WEBKIT_WEB_INSPECTOR_GET_PRIVATE(web_inspector);
326 }
327
webkit_web_inspector_finalize(GObject * object)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
webkit_web_inspector_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)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
webkit_web_inspector_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)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
webkit_web_inspector_set_web_view(WebKitWebInspector * web_inspector,WebKitWebView * web_view)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 **/
webkit_web_inspector_get_web_view(WebKitWebInspector * web_inspector)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
webkit_web_inspector_set_inspected_uri(WebKitWebInspector * web_inspector,const gchar * inspected_uri)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 **/
webkit_web_inspector_get_inspected_uri(WebKitWebInspector * web_inspector)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
webkit_web_inspector_set_inspector_client(WebKitWebInspector * web_inspector,WebCore::Page * page)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 */
webkit_web_inspector_show(WebKitWebInspector * webInspector)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 */
webkit_web_inspector_inspect_coordinates(WebKitWebInspector * webInspector,gdouble x,gdouble y)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 */
webkit_web_inspector_close(WebKitWebInspector * webInspector)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
webkit_web_inspector_execute_script(WebKitWebInspector * webInspector,long callId,const gchar * script)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