/* * Copyright (C) 2012 Igalia S.L. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2,1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "config.h" #include "WebKitWebInspector.h" #include "WebInspectorProxy.h" #include "WebInspectorProxyClient.h" #include "WebKitWebInspectorPrivate.h" #include <glib/gi18n-lib.h> #include <wtf/glib/GRefPtr.h> #include <wtf/glib/WTFGType.h> #include <wtf/text/CString.h> using namespace WebKit; /** * SECTION: WebKitWebInspector * @Short_description: Access to the WebKit inspector * @Title: WebKitWebInspector * * The WebKit Inspector is a graphical tool to inspect and change the * content of a #WebKitWebView. It also includes an interactive * JavaScript debugger. Using this class one can get a #GtkWidget * which can be embedded into an application to show the inspector. * * The inspector is available when the #WebKitSettings of the * #WebKitWebView has set the #WebKitSettings:enable-developer-extras * to true, otherwise no inspector is available. * * <informalexample><programlisting> * /<!-- -->* Enable the developer extras *<!-- -->/ * WebKitSettings *setting = webkit_web_view_get_settings (WEBKIT_WEB_VIEW(my_webview)); * g_object_set (G_OBJECT(settings), "enable-developer-extras", TRUE, NULL); * * /<!-- -->* Load some data or reload to be able to inspect the page*<!-- -->/ * webkit_web_view_load_uri (WEBKIT_WEB_VIEW(my_webview), "http://www.gnome.org"); * * /<!-- -->* Show the inspector *<!-- -->/ * WebKitWebInspector *inspector = webkit_web_view_get_inspector (WEBKIT_WEB_VIEW(my_webview)); * webkit_web_inspector_show (WEBKIT_WEB_INSPECTOR(inspector)); * </programlisting></informalexample> * */ enum { OPEN_WINDOW, BRING_TO_FRONT, CLOSED, ATTACH, DETACH, LAST_SIGNAL }; enum { PROP_0, PROP_INSPECTED_URI, PROP_ATTACHED_HEIGHT, PROP_CAN_ATTACH }; struct _WebKitWebInspectorPrivate { ~_WebKitWebInspectorPrivate() { webInspector->setClient(nullptr); } RefPtr<WebInspectorProxy> webInspector; CString inspectedURI; unsigned attachedHeight; bool canAttach; }; WEBKIT_DEFINE_TYPE(WebKitWebInspector, webkit_web_inspector, G_TYPE_OBJECT) static guint signals[LAST_SIGNAL] = { 0, }; static void webkitWebInspectorGetProperty(GObject* object, guint propId, GValue* value, GParamSpec* paramSpec) { WebKitWebInspector* inspector = WEBKIT_WEB_INSPECTOR(object); switch (propId) { case PROP_INSPECTED_URI: g_value_set_string(value, webkit_web_inspector_get_inspected_uri(inspector)); break; case PROP_ATTACHED_HEIGHT: g_value_set_uint(value, webkit_web_inspector_get_attached_height(inspector)); break; case PROP_CAN_ATTACH: g_value_set_boolean(value, webkit_web_inspector_get_can_attach(inspector)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, paramSpec); } } static void webkit_web_inspector_class_init(WebKitWebInspectorClass* findClass) { GObjectClass* gObjectClass = G_OBJECT_CLASS(findClass); gObjectClass->get_property = webkitWebInspectorGetProperty; /** * WebKitWebInspector:inspected-uri: * * The URI that is currently being inspected. */ g_object_class_install_property(gObjectClass, PROP_INSPECTED_URI, g_param_spec_string("inspected-uri", _("Inspected URI"), _("The URI that is currently being inspected"), 0, WEBKIT_PARAM_READABLE)); /** * WebKitWebInspector:attached-height: * * The height that the inspector view should have when it is attached. */ g_object_class_install_property(gObjectClass, PROP_ATTACHED_HEIGHT, g_param_spec_uint("attached-height", _("Attached Height"), _("The height that the inspector view should have when it is attached"), 0, G_MAXUINT, 0, WEBKIT_PARAM_READABLE)); /** * WebKitWebInspector:can-attach: * * Whether the @inspector can be attached to the same window that contains * the inspected view. * * Since: 2.8 */ g_object_class_install_property( gObjectClass, PROP_CAN_ATTACH, g_param_spec_boolean( "can-attach", _("Can Attach"), _("Whether the inspector can be attached to the same window that contains the inspected view"), FALSE, WEBKIT_PARAM_READABLE)); /** * WebKitWebInspector::open-window: * @inspector: the #WebKitWebInspector on which the signal is emitted * * Emitted when the inspector is requested to open in a separate window. * If this signal is not handled, a #GtkWindow with the inspector will be * created and shown, so you only need to handle this signal if you want * to use your own window. * This signal is emitted after #WebKitWebInspector::detach to show * the inspector in a separate window after being detached. * * To prevent the inspector from being shown you can connect to this * signal and simply return %TRUE * * Returns: %TRUE to stop other handlers from being invoked for the event. * %FALSE to propagate the event further. */ signals[OPEN_WINDOW] = g_signal_new( "open-window", G_TYPE_FROM_CLASS(gObjectClass), G_SIGNAL_RUN_LAST, 0, g_signal_accumulator_true_handled, nullptr, g_cclosure_marshal_generic, G_TYPE_BOOLEAN, 0); /** * WebKitWebInspector::bring-to-front: * @inspector: the #WebKitWebInspector on which the signal is emitted * * Emitted when the inspector should be shown. * * If the inspector is not attached the inspector window should be shown * on top of any other windows. * If the inspector is attached the inspector view should be made visible. * For example, if the inspector view is attached using a tab in a browser * window, the browser window should be raised and the tab containing the * inspector view should be the active one. * In both cases, if this signal is not handled, the default implementation * calls gtk_window_present() on the current toplevel #GtkWindow of the * inspector view. * * Returns: %TRUE to stop other handlers from being invoked for the event. * %FALSE to propagate the event further. */ signals[BRING_TO_FRONT] = g_signal_new( "bring-to-front", G_TYPE_FROM_CLASS(gObjectClass), G_SIGNAL_RUN_LAST, 0, g_signal_accumulator_true_handled, nullptr, g_cclosure_marshal_generic, G_TYPE_BOOLEAN, 0); /** * WebKitWebInspector::closed: * @inspector: the #WebKitWebInspector on which the signal is emitted * * Emitted when the inspector page is closed. If you are using your own * inspector window, you should connect to this signal and destroy your * window. */ signals[CLOSED] = g_signal_new("closed", G_TYPE_FROM_CLASS(gObjectClass), G_SIGNAL_RUN_LAST, 0, 0, 0, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); /** * WebKitWebInspector::attach: * @inspector: the #WebKitWebInspector on which the signal is emitted * * Emitted when the inspector is requested to be attached to the window * where the inspected web view is. * If this signal is not handled the inspector view will be automatically * attached to the inspected view, so you only need to handle this signal * if you want to attach the inspector view yourself (for example, to add * the inspector view to a browser tab). * * To prevent the inspector view from being attached you can connect to this * signal and simply return %TRUE. * * Returns: %TRUE to stop other handlers from being invoked for the event. * %FALSE to propagate the event further. */ signals[ATTACH] = g_signal_new( "attach", G_TYPE_FROM_CLASS(gObjectClass), G_SIGNAL_RUN_LAST, 0, g_signal_accumulator_true_handled, nullptr, g_cclosure_marshal_generic, G_TYPE_BOOLEAN, 0); /** * WebKitWebInspector::detach: * @inspector: the #WebKitWebInspector on which the signal is emitted * * Emitted when the inspector is requested to be detached from the window * it is currently attached to. The inspector is detached when the inspector page * is about to be closed, and this signal is emitted right before * #WebKitWebInspector::closed, or when the user clicks on the detach button * in the inspector view to show the inspector in a separate window. In this case * the signal #WebKitWebInspector::open-window is emitted after this one. * * To prevent the inspector view from being detached you can connect to this * signal and simply return %TRUE. * * Returns: %TRUE to stop other handlers from being invoked for the event. * %FALSE to propagate the event further. */ signals[DETACH] = g_signal_new( "detach", G_TYPE_FROM_CLASS(gObjectClass), G_SIGNAL_RUN_LAST, 0, g_signal_accumulator_true_handled, nullptr, g_cclosure_marshal_generic, G_TYPE_BOOLEAN, 0); } class WebKitInspectorClient final : public WebInspectorProxyClient { public: explicit WebKitInspectorClient(WebKitWebInspector* inspector) : m_inspector(inspector) { } private: bool openWindow(WebInspectorProxy&) override { gboolean returnValue; g_signal_emit(m_inspector, signals[OPEN_WINDOW], 0, &returnValue); return returnValue; } void didClose(WebInspectorProxy&) override { g_signal_emit(m_inspector, signals[CLOSED], 0); } bool bringToFront(WebInspectorProxy&) override { gboolean returnValue; g_signal_emit(m_inspector, signals[BRING_TO_FRONT], 0, &returnValue); return returnValue; } void inspectedURLChanged(WebInspectorProxy&, const String& url) override { CString uri = url.utf8(); if (uri == m_inspector->priv->inspectedURI) return; m_inspector->priv->inspectedURI = uri; g_object_notify(G_OBJECT(m_inspector), "inspected-uri"); } bool attach(WebInspectorProxy&) override { gboolean returnValue; g_signal_emit(m_inspector, signals[ATTACH], 0, &returnValue); return returnValue; } bool detach(WebInspectorProxy&) override { gboolean returnValue; g_signal_emit(m_inspector, signals[DETACH], 0, &returnValue); return returnValue; } void didChangeAttachedHeight(WebInspectorProxy&, unsigned height) override { if (m_inspector->priv->attachedHeight == height) return; m_inspector->priv->attachedHeight = height; g_object_notify(G_OBJECT(m_inspector), "attached-height"); } void didChangeAttachedWidth(WebInspectorProxy&, unsigned width) override { } void didChangeAttachAvailability(WebInspectorProxy&, bool available) override { if (m_inspector->priv->canAttach == available) return; m_inspector->priv->canAttach = available; g_object_notify(G_OBJECT(m_inspector), "can-attach"); } WebKitWebInspector* m_inspector; }; WebKitWebInspector* webkitWebInspectorCreate(WebInspectorProxy* webInspector) { WebKitWebInspector* inspector = WEBKIT_WEB_INSPECTOR(g_object_new(WEBKIT_TYPE_WEB_INSPECTOR, NULL)); inspector->priv->webInspector = webInspector; webInspector->setClient(std::make_unique<WebKitInspectorClient>(inspector)); return inspector; } /** * webkit_web_inspector_get_web_view: * @inspector: a #WebKitWebInspector * * Get the #WebKitWebViewBase used to display the inspector. * This might be %NULL if the inspector hasn't been loaded yet, * or it has been closed. * * Returns: (transfer none): the #WebKitWebViewBase used to display the inspector or %NULL */ WebKitWebViewBase* webkit_web_inspector_get_web_view(WebKitWebInspector* inspector) { g_return_val_if_fail(WEBKIT_IS_WEB_INSPECTOR(inspector), 0); return WEBKIT_WEB_VIEW_BASE(inspector->priv->webInspector->inspectorView()); } /** * webkit_web_inspector_get_inspected_uri: * @inspector: a #WebKitWebInspector * * Get the URI that is currently being inspected. This can be %NULL if * nothing has been loaded yet in the inspected view, if the inspector * has been closed or when inspected view was loaded from a HTML string * instead of a URI. * * Returns: the URI that is currently being inspected or %NULL */ const char* webkit_web_inspector_get_inspected_uri(WebKitWebInspector* inspector) { g_return_val_if_fail(WEBKIT_IS_WEB_INSPECTOR(inspector), 0); return inspector->priv->inspectedURI.data(); } /** * webkit_web_inspector_get_can_attach: * @inspector: a #WebKitWebInspector * * Whether the @inspector can be attached to the same window that contains * the inspected view. * * Returns: %TRUE if there is enough room for the inspector view inside the * window that contains the inspected view, or %FALSE otherwise. * * Since: 2.8 */ gboolean webkit_web_inspector_get_can_attach(WebKitWebInspector* inspector) { g_return_val_if_fail(WEBKIT_IS_WEB_INSPECTOR(inspector), FALSE); return inspector->priv->canAttach; } /** * webkit_web_inspector_is_attached: * @inspector: a #WebKitWebInspector * * Whether the @inspector view is currently attached to the same window that contains * the inspected view. * * Returns: %TRUE if @inspector is currently attached or %FALSE otherwise */ gboolean webkit_web_inspector_is_attached(WebKitWebInspector* inspector) { g_return_val_if_fail(WEBKIT_IS_WEB_INSPECTOR(inspector), FALSE); return inspector->priv->webInspector->isAttached(); } /** * webkit_web_inspector_attach: * @inspector: a #WebKitWebInspector * * Request @inspector to be attached. The signal #WebKitWebInspector::attach * will be emitted. If the inspector is already attached it does nothing. */ void webkit_web_inspector_attach(WebKitWebInspector* inspector) { g_return_if_fail(WEBKIT_IS_WEB_INSPECTOR(inspector)); if (inspector->priv->webInspector->isAttached()) return; inspector->priv->webInspector->attach(); } /** * webkit_web_inspector_detach: * @inspector: a #WebKitWebInspector * * Request @inspector to be detached. The signal #WebKitWebInspector::detach * will be emitted. If the inspector is already detached it does nothing. */ void webkit_web_inspector_detach(WebKitWebInspector* inspector) { g_return_if_fail(WEBKIT_IS_WEB_INSPECTOR(inspector)); if (!inspector->priv->webInspector->isAttached()) return; inspector->priv->webInspector->detach(); } /** * webkit_web_inspector_show: * @inspector: a #WebKitWebInspector * * Request @inspector to be shown. */ void webkit_web_inspector_show(WebKitWebInspector* inspector) { g_return_if_fail(WEBKIT_IS_WEB_INSPECTOR(inspector)); inspector->priv->webInspector->show(); } /** * webkit_web_inspector_close: * @inspector: a #WebKitWebInspector * * Request @inspector to be closed. */ void webkit_web_inspector_close(WebKitWebInspector* inspector) { g_return_if_fail(WEBKIT_IS_WEB_INSPECTOR(inspector)); inspector->priv->webInspector->close(); } /** * webkit_web_inspector_get_attached_height: * @inspector: a #WebKitWebInspector * * Get the height that the inspector view should have when * it's attached. If the inspector view is not attached this * returns 0. * * Returns: the height of the inspector view when attached */ guint webkit_web_inspector_get_attached_height(WebKitWebInspector* inspector) { g_return_val_if_fail(WEBKIT_IS_WEB_INSPECTOR(inspector), 0); if (!inspector->priv->webInspector->isAttached()) return 0; return inspector->priv->attachedHeight; }