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 66using namespace WebKit; 67using namespace WebCore; 68 69enum { 70 INSPECT_WEB_VIEW, 71 SHOW_WINDOW, 72 ATTACH_WINDOW, 73 DETACH_WINDOW, 74 CLOSE_WINDOW, 75 FINISHED, 76 LAST_SIGNAL 77}; 78 79static guint webkit_web_inspector_signals[LAST_SIGNAL] = { 0, }; 80 81enum { 82 PROP_0, 83 84 PROP_WEB_VIEW, 85 PROP_INSPECTED_URI, 86 PROP_JAVASCRIPT_PROFILING_ENABLED, 87 PROP_TIMELINE_PROFILING_ENABLED 88}; 89 90G_DEFINE_TYPE(WebKitWebInspector, webkit_web_inspector, G_TYPE_OBJECT) 91 92struct _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 100static void webkit_web_inspector_finalize(GObject* object); 101 102static void webkit_web_inspector_set_property(GObject* object, guint prop_id, const GValue* value, GParamSpec* pspec); 103 104static void webkit_web_inspector_get_property(GObject* object, guint prop_id, GValue* value, GParamSpec* pspec); 105 106static 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 118static 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 323static void webkit_web_inspector_init(WebKitWebInspector* web_inspector) 324{ 325 web_inspector->priv = WEBKIT_WEB_INSPECTOR_GET_PRIVATE(web_inspector); 326} 327 328static 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 342static 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 376static 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 405void 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 **/ 433WebKitWebView* 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 441void 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 **/ 461const 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 468void 469webkit_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 */ 484void 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 */ 516void 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 */ 545void 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 553void 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