312b8c8953c1ab92f879a5aad4d9c868ca416765
[WebKit-https.git] / WebKit / gtk / WebCoreSupport / InspectorClientGtk.cpp
1 /*
2  * Copyright (C) 2008 Gustavo Noronha Silva
3  * Copyright (C) 2010 Collabora Ltd.
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public
7  *  License as published by the Free Software Foundation; either
8  *  version 2 of the License, or (at your option) any later version.
9  *
10  *  This library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *  Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public
16  *  License along with this library; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19
20 #include "config.h"
21 #include "InspectorClientGtk.h"
22
23 #include "Frame.h"
24 #include "webkitwebview.h"
25 #include "webkitwebinspector.h"
26 #include "webkitprivate.h"
27 #include "webkitversion.h"
28 #include "InspectorController.h"
29 #include "NotImplemented.h"
30 #include "PlatformString.h"
31 #include <wtf/text/CString.h>
32
33 using namespace WebCore;
34
35 namespace WebKit {
36
37 static void notifyWebViewDestroyed(WebKitWebView* webView, InspectorFrontendClient* inspectorFrontendClient)
38 {
39     inspectorFrontendClient->destroyInspectorWindow(true);
40 }
41
42 InspectorClient::InspectorClient(WebKitWebView* webView)
43     : m_inspectedWebView(webView)
44     , m_frontendPage(0)
45     , m_frontendClient(0)
46 {}
47
48 InspectorClient::~InspectorClient()
49 {
50     if (m_frontendClient) {
51         m_frontendClient->disconnectInspectorClient();
52         m_frontendClient = 0;
53     }
54 }
55
56 void InspectorClient::inspectorDestroyed()
57 {
58     delete this;
59 }
60
61 void InspectorClient::openInspectorFrontend(InspectorController* controller)
62 {
63     // This g_object_get will ref the inspector. We're not doing an
64     // unref if this method succeeds because the inspector object must
65     // be alive even after the inspected WebView is destroyed - the
66     // close-window and destroy signals still need to be
67     // emitted.
68     WebKitWebInspector* webInspector = 0;
69     g_object_get(m_inspectedWebView, "web-inspector", &webInspector, NULL);
70     ASSERT(webInspector);
71
72     WebKitWebView* inspectorWebView = 0;
73     g_signal_emit_by_name(webInspector, "inspect-web-view", m_inspectedWebView, &inspectorWebView);
74
75     if (!inspectorWebView) {
76         g_object_unref(webInspector);
77         return;
78     }
79
80     webkit_web_inspector_set_web_view(webInspector, inspectorWebView);
81  
82     GOwnPtr<gchar> inspectorPath(g_build_filename(inspectorFilesPath(), "inspector.html", NULL));
83     GOwnPtr<gchar> inspectorURI(g_filename_to_uri(inspectorPath.get(), 0, 0));
84     webkit_web_view_load_uri(inspectorWebView, inspectorURI.get());
85
86     gtk_widget_show(GTK_WIDGET(inspectorWebView));
87
88     m_frontendPage = core(inspectorWebView);
89     m_frontendClient = new InspectorFrontendClient(m_inspectedWebView, inspectorWebView, webInspector, m_frontendPage, this);
90     m_frontendPage->inspectorController()->setInspectorFrontendClient(m_frontendClient);
91
92     // The inspector must be in it's own PageGroup to avoid deadlock while debugging.
93     m_frontendPage->setGroupName("");
94 }
95
96 void InspectorClient::releaseFrontendPage()
97 {
98     m_frontendPage = 0;
99 }
100
101 void InspectorClient::highlight(Node*)
102 {
103     hideHighlight();
104 }
105
106 void InspectorClient::hideHighlight()
107 {
108     // FIXME: we should be able to only invalidate the actual rects of
109     // the new and old nodes. We need to track the nodes, and take the
110     // actual highlight size into account when calculating the damage
111     // rect.
112     gtk_widget_queue_draw(GTK_WIDGET(m_inspectedWebView));
113 }
114
115 #ifdef HAVE_GSETTINGS
116 static String toGSettingName(String inspectorSettingName)
117 {
118     if (inspectorSettingName == "resourceTrackingEnabled")
119         return String("resource-tracking-enabled");
120
121     if (inspectorSettingName == "xhrMonitor")
122         return String("xhr-monitor-enabled");
123
124     if (inspectorSettingName == "frontendSettings")
125         return String("frontend-settings");
126
127     if (inspectorSettingName == "debuggerEnabled")
128         return String("debugger-enabled");
129
130     if (inspectorSettingName == "profilerEnabled")
131         return String("profiler-enabled");
132
133     return inspectorSettingName;
134 }
135
136 static String truthStringFromVariant(GVariant* variant)
137 {
138     if (g_variant_get_boolean(variant))
139         return String("true");
140
141     return String("false");
142 }
143
144 static GVariant* variantFromTruthString(const String& truth)
145 {
146     if (truth == "true")
147         return g_variant_new_boolean(TRUE);
148
149     return g_variant_new_boolean(FALSE);
150 }
151
152 static bool shouldIgnoreSetting(const String& key)
153 {
154     // Ignore this setting for now, it doesn't seem to be used for
155     // anything right now.
156     if (key == "lastActivePanel")
157         return true;
158
159     // GSettings considers trying to fetch or set a setting that is
160     // not backed by a schema as programmer error, and aborts the
161     // program's execution. We check here to avoid having an unhandled
162     // setting as a fatal error.
163     if (key == "resourceTrackingEnabled" || key == "xhrMonitor"
164         || key == "frontendSettings" || key == "debuggerEnabled"
165         || key == "profilerEnabled")
166         return false;
167
168     LOG_VERBOSE(NotYetImplemented, "Unknown key ignored: %s", key.ascii().data());
169     return true;
170 }
171
172 void InspectorClient::populateSetting(const String& key, String* value)
173 {
174     if (shouldIgnoreSetting(key))
175         return;
176
177     GSettings* settings = inspectorGSettings();
178     if (!settings)
179         return;
180
181     PlatformRefPtr<GVariant> variant = adoptPlatformRef(g_settings_get_value(settings, toGSettingName(key).utf8().data()));
182
183     if (key == "resourceTrackingEnabled" || key == "xhrMonitor"
184         || key == "debuggerEnabled" || key == "profilerEnabled")
185         *value = truthStringFromVariant(variant.get());
186     else if (key == "frontendSettings")
187         *value = String(g_variant_get_string(variant.get(), 0));
188 }
189
190 void InspectorClient::storeSetting(const String& key, const String& value)
191 {
192     if (shouldIgnoreSetting(key))
193         return;
194
195     GSettings* settings = inspectorGSettings();
196     if (!settings)
197         return;
198
199     PlatformRefPtr<GVariant> variant(0);
200
201     // Set the key with the appropriate type, and also avoid setting
202     // unknown keys to avoid aborting the execution.
203     if (key == "resourceTrackingEnabled" || key == "xhrMonitor"
204         || key == "debuggerEnabled" || key == "profilerEnabled")
205         variant = adoptPlatformRef(variantFromTruthString(value));
206     else if (key == "frontendSettings")
207         variant = adoptPlatformRef(g_variant_new_string(value.utf8().data()));
208
209     if (!variant)
210         return;
211
212     g_settings_set_value(settings, toGSettingName(key).utf8().data(), variant.get());
213 }
214 #else
215 void InspectorClient::populateSetting(const String&, String*)
216 {
217     notImplemented();
218 }
219
220 void InspectorClient::storeSetting(const String&, const String&)
221 {
222     notImplemented();
223 }
224 #endif // HAVE_GSETTINGS
225
226 bool InspectorClient::sendMessageToFrontend(const String& message)
227 {
228     return doDispatchMessageOnFrontendPage(m_frontendPage, message);
229 }
230
231 const char* InspectorClient::inspectorFilesPath()
232 {
233     if (m_inspectorFilesPath)
234         m_inspectorFilesPath.get();
235
236     const char* environmentPath = getenv("WEBKIT_INSPECTOR_PATH");
237     if (environmentPath && g_file_test(environmentPath, G_FILE_TEST_IS_DIR))
238         m_inspectorFilesPath.set(g_strdup(environmentPath));
239     else
240         m_inspectorFilesPath.set(g_build_filename(DATA_DIR, "webkitgtk-"WEBKITGTK_API_VERSION_STRING, "webinspector", NULL));
241
242     return m_inspectorFilesPath.get();
243 }
244
245 InspectorFrontendClient::InspectorFrontendClient(WebKitWebView* inspectedWebView, WebKitWebView* inspectorWebView, WebKitWebInspector* webInspector, Page* inspectorPage, InspectorClient* inspectorClient)
246     : InspectorFrontendClientLocal(core(inspectedWebView)->inspectorController(), inspectorPage)
247     , m_inspectorWebView(inspectorWebView)
248     , m_inspectedWebView(inspectedWebView)
249     , m_webInspector(webInspector)
250     , m_inspectorClient(inspectorClient)
251 {
252     g_signal_connect(m_inspectorWebView, "destroy",
253                      G_CALLBACK(notifyWebViewDestroyed), (gpointer)this);
254 }
255
256 InspectorFrontendClient::~InspectorFrontendClient()
257 {
258     if (m_inspectorClient) {
259         m_inspectorClient->disconnectFrontendClient();
260         m_inspectorClient = 0;
261     }
262     ASSERT(!m_webInspector);
263 }
264
265 void InspectorFrontendClient::destroyInspectorWindow(bool notifyInspectorController)
266 {
267     if (!m_webInspector)
268         return;
269     WebKitWebInspector* webInspector = m_webInspector;
270     m_webInspector = 0;
271
272     g_signal_handlers_disconnect_by_func(m_inspectorWebView, (gpointer)notifyWebViewDestroyed, (gpointer)this);
273     m_inspectorWebView = 0;
274
275     if (notifyInspectorController)
276         core(m_inspectedWebView)->inspectorController()->disconnectFrontend();
277
278     if (m_inspectorClient)
279         m_inspectorClient->releaseFrontendPage();
280
281     gboolean handled = FALSE;
282     g_signal_emit_by_name(webInspector, "close-window", &handled);
283     ASSERT(handled);
284
285     // Please do not use member variables here because InspectorFrontendClient object pointed by 'this'
286     // has been implicitly deleted by "close-window" function.
287
288     /* we should now dispose our own reference */
289     g_object_unref(webInspector);
290 }
291
292 String InspectorFrontendClient::localizedStringsURL()
293 {
294     GOwnPtr<gchar> stringsPath(g_build_filename(m_inspectorClient->inspectorFilesPath(), "localizedStrings.js", NULL));
295     GOwnPtr<gchar> stringsURI(g_filename_to_uri(stringsPath.get(), 0, 0));
296
297     // FIXME: support l10n of localizedStrings.js
298     return String::fromUTF8(stringsURI.get());
299 }
300
301 String InspectorFrontendClient::hiddenPanels()
302 {
303     notImplemented();
304     return String();
305 }
306
307 void InspectorFrontendClient::bringToFront()
308 {
309     if (!m_inspectorWebView)
310         return;
311
312     gboolean handled = FALSE;
313     g_signal_emit_by_name(m_webInspector, "show-window", &handled);
314 }
315
316 void InspectorFrontendClient::closeWindow()
317 {
318     destroyInspectorWindow(true);
319 }
320
321 void InspectorFrontendClient::disconnectFromBackend()
322 {
323     destroyInspectorWindow(false);
324 }
325
326 void InspectorFrontendClient::attachWindow()
327 {
328     if (!m_inspectorWebView)
329         return;
330
331     gboolean handled = FALSE;
332     g_signal_emit_by_name(m_webInspector, "attach-window", &handled);
333 }
334
335 void InspectorFrontendClient::detachWindow()
336 {
337     if (!m_inspectorWebView)
338         return;
339
340     gboolean handled = FALSE;
341     g_signal_emit_by_name(m_webInspector, "detach-window", &handled);
342 }
343
344 void InspectorFrontendClient::setAttachedWindowHeight(unsigned height)
345 {
346     notImplemented();
347 }
348
349 void InspectorFrontendClient::inspectedURLChanged(const String& newURL)
350 {
351     if (!m_inspectorWebView)
352         return;
353
354     webkit_web_inspector_set_inspected_uri(m_webInspector, newURL.utf8().data());
355 }
356
357 }
358