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