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 "InspectorController.h"
25 #include "NotImplemented.h"
26 #include "Page.h"
27 #include "PlatformString.h"
28 #include "webkitversion.h"
29 #include "webkitwebinspector.h"
30 #include "webkitwebinspectorprivate.h"
31 #include "webkitwebview.h"
32 #include "webkitwebviewprivate.h"
33 #include <wtf/text/CString.h>
34
35 using namespace WebCore;
36
37 namespace WebKit {
38
notifyWebViewDestroyed(WebKitWebView * webView,InspectorFrontendClient * inspectorFrontendClient)39 static void notifyWebViewDestroyed(WebKitWebView* webView, InspectorFrontendClient* inspectorFrontendClient)
40 {
41 inspectorFrontendClient->destroyInspectorWindow(true);
42 }
43
44 namespace {
45
46 class InspectorFrontendSettingsGtk : public InspectorFrontendClientLocal::Settings {
47 private:
~InspectorFrontendSettingsGtk()48 virtual ~InspectorFrontendSettingsGtk() { }
49 #ifdef HAVE_GSETTINGS
shouldIgnoreSetting(const String & key)50 static bool shouldIgnoreSetting(const String& key)
51 {
52 // GSettings considers trying to fetch or set a setting that is
53 // not backed by a schema as programmer error, and aborts the
54 // program's execution. We check here to avoid having an unhandled
55 // setting as a fatal error.
56 LOG_VERBOSE(NotYetImplemented, "Unknown key ignored: %s", key.ascii().data());
57 return true;
58 }
59
getProperty(const String & name)60 virtual String getProperty(const String& name)
61 {
62 if (shouldIgnoreSetting(name))
63 return String();
64
65 GSettings* settings = inspectorGSettings();
66 if (!settings)
67 return String();
68
69 GRefPtr<GVariant> variant = adoptGRef(g_settings_get_value(settings, name.utf8().data()));
70 return String(g_variant_get_string(variant.get(), 0));
71 }
72
setProperty(const String & name,const String & value)73 virtual void setProperty(const String& name, const String& value)
74 {
75 // Avoid setting unknown keys to avoid aborting the execution.
76 if (shouldIgnoreSetting(name))
77 return;
78
79 GSettings* settings = inspectorGSettings();
80 if (!settings)
81 return;
82
83 GRefPtr<GVariant> variant = adoptGRef(g_variant_new_string(value.utf8().data()));
84 g_settings_set_value(settings, name.utf8().data(), variant.get());
85 }
86 #else
getProperty(const String &)87 virtual String getProperty(const String&)
88 {
89 notImplemented();
90 return String();
91 }
92
setProperty(const String &,const String &)93 virtual void setProperty(const String&, const String&)
94 {
95 notImplemented();
96 }
97 #endif // HAVE_GSETTINGS
98 };
99
100 } // namespace
101
InspectorClient(WebKitWebView * webView)102 InspectorClient::InspectorClient(WebKitWebView* webView)
103 : m_inspectedWebView(webView)
104 , m_frontendPage(0)
105 , m_frontendClient(0)
106 {}
107
~InspectorClient()108 InspectorClient::~InspectorClient()
109 {
110 if (m_frontendClient) {
111 m_frontendClient->disconnectInspectorClient();
112 m_frontendClient = 0;
113 }
114 }
115
inspectorDestroyed()116 void InspectorClient::inspectorDestroyed()
117 {
118 delete this;
119 }
120
openInspectorFrontend(InspectorController * controller)121 void InspectorClient::openInspectorFrontend(InspectorController* controller)
122 {
123 // This g_object_get will ref the inspector. We're not doing an
124 // unref if this method succeeds because the inspector object must
125 // be alive even after the inspected WebView is destroyed - the
126 // close-window and destroy signals still need to be
127 // emitted.
128 WebKitWebInspector* webInspector = 0;
129 g_object_get(m_inspectedWebView, "web-inspector", &webInspector, NULL);
130 ASSERT(webInspector);
131
132 WebKitWebView* inspectorWebView = 0;
133 g_signal_emit_by_name(webInspector, "inspect-web-view", m_inspectedWebView, &inspectorWebView);
134
135 if (!inspectorWebView) {
136 g_object_unref(webInspector);
137 return;
138 }
139
140 webkit_web_inspector_set_web_view(webInspector, inspectorWebView);
141
142 GOwnPtr<gchar> inspectorPath(g_build_filename(inspectorFilesPath(), "inspector.html", NULL));
143 GOwnPtr<gchar> inspectorURI(g_filename_to_uri(inspectorPath.get(), 0, 0));
144 webkit_web_view_load_uri(inspectorWebView, inspectorURI.get());
145
146 gtk_widget_show(GTK_WIDGET(inspectorWebView));
147
148 m_frontendPage = core(inspectorWebView);
149 m_frontendClient = new InspectorFrontendClient(m_inspectedWebView, inspectorWebView, webInspector, m_frontendPage, this);
150 m_frontendPage->inspectorController()->setInspectorFrontendClient(m_frontendClient);
151
152 // The inspector must be in it's own PageGroup to avoid deadlock while debugging.
153 m_frontendPage->setGroupName("");
154 }
155
releaseFrontendPage()156 void InspectorClient::releaseFrontendPage()
157 {
158 m_frontendPage = 0;
159 }
160
highlight(Node *)161 void InspectorClient::highlight(Node*)
162 {
163 hideHighlight();
164 }
165
hideHighlight()166 void InspectorClient::hideHighlight()
167 {
168 // FIXME: we should be able to only invalidate the actual rects of
169 // the new and old nodes. We need to track the nodes, and take the
170 // actual highlight size into account when calculating the damage
171 // rect.
172 gtk_widget_queue_draw(GTK_WIDGET(m_inspectedWebView));
173 }
174
sendMessageToFrontend(const String & message)175 bool InspectorClient::sendMessageToFrontend(const String& message)
176 {
177 return doDispatchMessageOnFrontendPage(m_frontendPage, message);
178 }
179
inspectorFilesPath()180 const char* InspectorClient::inspectorFilesPath()
181 {
182 if (m_inspectorFilesPath)
183 m_inspectorFilesPath.get();
184
185 const char* environmentPath = getenv("WEBKIT_INSPECTOR_PATH");
186 if (environmentPath && g_file_test(environmentPath, G_FILE_TEST_IS_DIR))
187 m_inspectorFilesPath.set(g_strdup(environmentPath));
188 else
189 m_inspectorFilesPath.set(g_build_filename(DATA_DIR, "webkitgtk-"WEBKITGTK_API_VERSION_STRING, "webinspector", NULL));
190
191 return m_inspectorFilesPath.get();
192 }
193
InspectorFrontendClient(WebKitWebView * inspectedWebView,WebKitWebView * inspectorWebView,WebKitWebInspector * webInspector,Page * inspectorPage,InspectorClient * inspectorClient)194 InspectorFrontendClient::InspectorFrontendClient(WebKitWebView* inspectedWebView, WebKitWebView* inspectorWebView, WebKitWebInspector* webInspector, Page* inspectorPage, InspectorClient* inspectorClient)
195 : InspectorFrontendClientLocal(core(inspectedWebView)->inspectorController(), inspectorPage, new InspectorFrontendSettingsGtk())
196 , m_inspectorWebView(inspectorWebView)
197 , m_inspectedWebView(inspectedWebView)
198 , m_webInspector(webInspector)
199 , m_inspectorClient(inspectorClient)
200 {
201 g_signal_connect(m_inspectorWebView, "destroy",
202 G_CALLBACK(notifyWebViewDestroyed), (gpointer)this);
203 }
204
~InspectorFrontendClient()205 InspectorFrontendClient::~InspectorFrontendClient()
206 {
207 if (m_inspectorClient) {
208 m_inspectorClient->disconnectFrontendClient();
209 m_inspectorClient = 0;
210 }
211 ASSERT(!m_webInspector);
212 }
213
destroyInspectorWindow(bool notifyInspectorController)214 void InspectorFrontendClient::destroyInspectorWindow(bool notifyInspectorController)
215 {
216 if (!m_webInspector)
217 return;
218 WebKitWebInspector* webInspector = m_webInspector;
219 m_webInspector = 0;
220
221 g_signal_handlers_disconnect_by_func(m_inspectorWebView, (gpointer)notifyWebViewDestroyed, (gpointer)this);
222 m_inspectorWebView = 0;
223
224 if (notifyInspectorController)
225 core(m_inspectedWebView)->inspectorController()->disconnectFrontend();
226
227 if (m_inspectorClient)
228 m_inspectorClient->releaseFrontendPage();
229
230 gboolean handled = FALSE;
231 g_signal_emit_by_name(webInspector, "close-window", &handled);
232 ASSERT(handled);
233
234 // Please do not use member variables here because InspectorFrontendClient object pointed by 'this'
235 // has been implicitly deleted by "close-window" function.
236
237 /* we should now dispose our own reference */
238 g_object_unref(webInspector);
239 }
240
localizedStringsURL()241 String InspectorFrontendClient::localizedStringsURL()
242 {
243 GOwnPtr<gchar> stringsPath(g_build_filename(m_inspectorClient->inspectorFilesPath(), "localizedStrings.js", NULL));
244 GOwnPtr<gchar> stringsURI(g_filename_to_uri(stringsPath.get(), 0, 0));
245
246 // FIXME: support l10n of localizedStrings.js
247 return String::fromUTF8(stringsURI.get());
248 }
249
hiddenPanels()250 String InspectorFrontendClient::hiddenPanels()
251 {
252 notImplemented();
253 return String();
254 }
255
bringToFront()256 void InspectorFrontendClient::bringToFront()
257 {
258 if (!m_inspectorWebView)
259 return;
260
261 gboolean handled = FALSE;
262 g_signal_emit_by_name(m_webInspector, "show-window", &handled);
263 }
264
closeWindow()265 void InspectorFrontendClient::closeWindow()
266 {
267 destroyInspectorWindow(true);
268 }
269
disconnectFromBackend()270 void InspectorFrontendClient::disconnectFromBackend()
271 {
272 destroyInspectorWindow(false);
273 }
274
attachWindow()275 void InspectorFrontendClient::attachWindow()
276 {
277 if (!m_inspectorWebView)
278 return;
279
280 gboolean handled = FALSE;
281 g_signal_emit_by_name(m_webInspector, "attach-window", &handled);
282 }
283
detachWindow()284 void InspectorFrontendClient::detachWindow()
285 {
286 if (!m_inspectorWebView)
287 return;
288
289 gboolean handled = FALSE;
290 g_signal_emit_by_name(m_webInspector, "detach-window", &handled);
291 }
292
setAttachedWindowHeight(unsigned height)293 void InspectorFrontendClient::setAttachedWindowHeight(unsigned height)
294 {
295 notImplemented();
296 }
297
inspectedURLChanged(const String & newURL)298 void InspectorFrontendClient::inspectedURLChanged(const String& newURL)
299 {
300 if (!m_inspectorWebView)
301 return;
302
303 webkit_web_inspector_set_inspected_uri(m_webInspector, newURL.utf8().data());
304 }
305
306 }
307
308