2010-08-16 Ilya Tikhonovsky <loislo@chromium.org>
[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();
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> inspectorURI;
83
84     // Make the Web Inspector work when running tests
85     // FixMe: it is not working. It should be not the subdirectory of the current directory, but a subdirectory of $(WEBKITOUTPUTDIR).
86     if (g_file_test("resources/inspector/inspector.html", G_FILE_TEST_EXISTS)) {
87         GOwnPtr<gchar> currentDirectory(g_get_current_dir());
88         GOwnPtr<gchar> fullPath(g_strdup_printf("%s/resources/inspector/inspector.html", currentDirectory.get()));
89         inspectorURI.set(g_filename_to_uri(fullPath.get(), NULL, NULL));
90     } else {
91         GOwnPtr<gchar> dataPath(g_strdup_printf(DATA_DIR"/webkitgtk-%.1f/webinspector/inspector.html", WEBKITGTK_API_VERSION));
92         inspectorURI.set(g_filename_to_uri(dataPath.get(), NULL, NULL));
93     }
94
95     webkit_web_view_load_uri(inspectorWebView, inspectorURI.get());
96
97     gtk_widget_show(GTK_WIDGET(inspectorWebView));
98
99     m_frontendPage = core(inspectorWebView);
100     m_frontendClient = new InspectorFrontendClient(m_inspectedWebView, inspectorWebView, webInspector, m_frontendPage, this);
101     m_frontendPage->inspectorController()->setInspectorFrontendClient(m_frontendClient);
102 }
103
104 void InspectorClient::releaseFrontendPage()
105 {
106     m_frontendPage = 0;
107 }
108
109 void InspectorClient::highlight(Node*)
110 {
111     hideHighlight();
112 }
113
114 void InspectorClient::hideHighlight()
115 {
116     // FIXME: we should be able to only invalidate the actual rects of
117     // the new and old nodes. We need to track the nodes, and take the
118     // actual highlight size into account when calculating the damage
119     // rect.
120     gtk_widget_queue_draw(GTK_WIDGET(m_inspectedWebView));
121 }
122
123 #ifdef HAVE_GSETTINGS
124 static String toGSettingName(String inspectorSettingName)
125 {
126     if (inspectorSettingName == "resourceTrackingEnabled")
127         return String("resource-tracking-enabled");
128
129     if (inspectorSettingName == "xhrMonitor")
130         return String("xhr-monitor-enabled");
131
132     if (inspectorSettingName == "frontendSettings")
133         return String("frontend-settings");
134
135     if (inspectorSettingName == "debuggerEnabled")
136         return String("debugger-enabled");
137
138     if (inspectorSettingName == "profilerEnabled")
139         return String("profiler-enabled");
140
141     return inspectorSettingName;
142 }
143
144 static String truthStringFromVariant(GVariant* variant)
145 {
146     if (g_variant_get_boolean(variant))
147         return String("true");
148
149     return String("false");
150 }
151
152 static GVariant* variantFromTruthString(const String& truth)
153 {
154     if (truth == "true")
155         return g_variant_new_boolean(TRUE);
156
157     return g_variant_new_boolean(FALSE);
158 }
159
160 static bool shouldIgnoreSetting(const String& key)
161 {
162     // Ignore this setting for now, it doesn't seem to be used for
163     // anything right now.
164     if (key == "lastActivePanel")
165         return true;
166
167     // GSettings considers trying to fetch or set a setting that is
168     // not backed by a schema as programmer error, and aborts the
169     // program's execution. We check here to avoid having an unhandled
170     // setting as a fatal error.
171     if (key == "resourceTrackingEnabled" || key == "xhrMonitor"
172         || key == "frontendSettings" || key == "debuggerEnabled"
173         || key == "profilerEnabled")
174         return false;
175
176     LOG_VERBOSE(NotYetImplemented, "Unknown key ignored: %s", key.ascii().data());
177     return true;
178 }
179
180 void InspectorClient::populateSetting(const String& key, String* value)
181 {
182     if (shouldIgnoreSetting(key))
183         return;
184
185     GSettings* settings = inspectorGSettings();
186     if (!settings)
187         return;
188
189     GRefPtr<GVariant> variant = adoptGRef(g_settings_get_value(settings, toGSettingName(key).utf8().data()));
190
191     if (key == "resourceTrackingEnabled" || key == "xhrMonitor"
192         || key == "debuggerEnabled" || key == "profilerEnabled")
193         *value = truthStringFromVariant(variant.get());
194     else if (key == "frontendSettings")
195         *value = String(g_variant_get_string(variant.get(), 0));
196 }
197
198 void InspectorClient::storeSetting(const String& key, const String& value)
199 {
200     if (shouldIgnoreSetting(key))
201         return;
202
203     GSettings* settings = inspectorGSettings();
204     if (!settings)
205         return;
206
207     GRefPtr<GVariant> variant(0);
208
209     // Set the key with the appropriate type, and also avoid setting
210     // unknown keys to avoid aborting the execution.
211     if (key == "resourceTrackingEnabled" || key == "xhrMonitor"
212         || key == "debuggerEnabled" || key == "profilerEnabled")
213         variant = adoptGRef(variantFromTruthString(value));
214     else if (key == "frontendSettings")
215         variant = adoptGRef(g_variant_new_string(value.utf8().data()));
216
217     if (!variant)
218         return;
219
220     g_settings_set_value(settings, toGSettingName(key).utf8().data(), variant.get());
221 }
222 #else
223 void InspectorClient::populateSetting(const String&, String*)
224 {
225     notImplemented();
226 }
227
228 void InspectorClient::storeSetting(const String&, const String&)
229 {
230     notImplemented();
231 }
232 #endif // HAVE_GSETTINGS
233
234 bool InspectorClient::sendMessageToFrontend(const String& message)
235 {
236     if (!m_frontendPage)
237         return false;
238
239     Frame* frame = m_frontendPage->mainFrame();
240     if (!frame)
241         return false;
242
243     ScriptController* scriptController = frame->script();
244     if (!scriptController)
245         return false;
246
247     String dispatchToFrontend("WebInspector.dispatchMessageFromBackend(");
248     dispatchToFrontend += message;
249     dispatchToFrontend += ");";
250     scriptController->executeScript(dispatchToFrontend);
251     return true;
252 }
253
254 bool destroyed = TRUE;
255
256 InspectorFrontendClient::InspectorFrontendClient(WebKitWebView* inspectedWebView, WebKitWebView* inspectorWebView, WebKitWebInspector* webInspector, Page* inspectorPage, InspectorClient* inspectorClient)
257     : InspectorFrontendClientLocal(core(inspectedWebView)->inspectorController(), inspectorPage)
258     , m_inspectorWebView(inspectorWebView)
259     , m_inspectedWebView(inspectedWebView)
260     , m_webInspector(webInspector)
261     , m_inspectorClient(inspectorClient)
262 {
263     g_signal_connect(m_inspectorWebView, "destroy",
264                      G_CALLBACK(notifyWebViewDestroyed), (gpointer)this);
265 }
266
267 InspectorFrontendClient::~InspectorFrontendClient()
268 {
269     if (m_inspectorClient) {
270         m_inspectorClient->disconnectFrontendClient();
271         m_inspectorClient = 0;
272     }
273     ASSERT(!m_webInspector);
274 }
275
276 void InspectorFrontendClient::destroyInspectorWindow()
277 {
278     if (!m_webInspector)
279         return;
280     WebKitWebInspector* webInspector = m_webInspector;
281     m_webInspector = 0;
282
283     g_signal_handlers_disconnect_by_func(m_inspectorWebView, (gpointer)notifyWebViewDestroyed, (gpointer)this);
284     m_inspectorWebView = 0;
285
286     core(m_inspectedWebView)->inspectorController()->disconnectFrontend();
287
288     if (m_inspectorClient)
289         m_inspectorClient->releaseFrontendPage();
290
291     gboolean handled = FALSE;
292     g_signal_emit_by_name(webInspector, "close-window", &handled);
293     ASSERT(handled);
294
295     // Please do not use member variables here because InspectorFrontendClient object pointed by 'this'
296     // has been implicitly deleted by "close-window" function.
297
298     /* we should now dispose our own reference */
299     g_object_unref(webInspector);
300 }
301
302 String InspectorFrontendClient::localizedStringsURL()
303 {
304     GOwnPtr<gchar> URL;
305
306     // Make the Web Inspector work when running tests
307     if (g_file_test("WebCore/English.lproj/localizedStrings.js", G_FILE_TEST_EXISTS)) {
308         GOwnPtr<gchar> currentDirectory(g_get_current_dir());
309         GOwnPtr<gchar> fullPath(g_strdup_printf("%s/WebCore/English.lproj/localizedStrings.js", currentDirectory.get()));
310         URL.set(g_filename_to_uri(fullPath.get(), NULL, NULL));
311     } else {
312         GOwnPtr<gchar> localizedStringsPath(g_strdup_printf(DATA_DIR"/webkitgtk-%.1f/webinspector/localizedStrings.js", WEBKITGTK_API_VERSION));
313         URL.set(g_filename_to_uri(localizedStringsPath.get(), NULL, NULL));
314     }
315
316     // FIXME: support l10n of localizedStrings.js
317     return String::fromUTF8(URL.get());
318 }
319
320 String InspectorFrontendClient::hiddenPanels()
321 {
322     notImplemented();
323     return String();
324 }
325
326 void InspectorFrontendClient::bringToFront()
327 {
328     if (!m_inspectorWebView)
329         return;
330
331     gboolean handled = FALSE;
332     g_signal_emit_by_name(m_webInspector, "show-window", &handled);
333 }
334
335 void InspectorFrontendClient::closeWindow()
336 {
337     destroyInspectorWindow();
338 }
339
340 void InspectorFrontendClient::attachWindow()
341 {
342     if (!m_inspectorWebView)
343         return;
344
345     gboolean handled = FALSE;
346     g_signal_emit_by_name(m_webInspector, "attach-window", &handled);
347 }
348
349 void InspectorFrontendClient::detachWindow()
350 {
351     if (!m_inspectorWebView)
352         return;
353
354     gboolean handled = FALSE;
355     g_signal_emit_by_name(m_webInspector, "detach-window", &handled);
356 }
357
358 void InspectorFrontendClient::setAttachedWindowHeight(unsigned height)
359 {
360     notImplemented();
361 }
362
363 void InspectorFrontendClient::inspectedURLChanged(const String& newURL)
364 {
365     if (!m_inspectorWebView)
366         return;
367
368     webkit_web_inspector_set_inspected_uri(m_webInspector, newURL.utf8().data());
369 }
370
371 }
372