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 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: 48 virtual ~InspectorFrontendSettingsGtk() { } 49 #ifdef HAVE_GSETTINGS 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 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 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 87 virtual String getProperty(const String&) 88 { 89 notImplemented(); 90 return String(); 91 } 92 93 virtual void setProperty(const String&, const String&) 94 { 95 notImplemented(); 96 } 97 #endif // HAVE_GSETTINGS 98 }; 99 100 } // namespace 101 102 InspectorClient::InspectorClient(WebKitWebView* webView) 103 : m_inspectedWebView(webView) 104 , m_frontendPage(0) 105 , m_frontendClient(0) 106 {} 107 108 InspectorClient::~InspectorClient() 109 { 110 if (m_frontendClient) { 111 m_frontendClient->disconnectInspectorClient(); 112 m_frontendClient = 0; 113 } 114 } 115 116 void InspectorClient::inspectorDestroyed() 117 { 118 delete this; 119 } 120 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 156 void InspectorClient::releaseFrontendPage() 157 { 158 m_frontendPage = 0; 159 } 160 161 void InspectorClient::highlight(Node*) 162 { 163 hideHighlight(); 164 } 165 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 175 bool InspectorClient::sendMessageToFrontend(const String& message) 176 { 177 return doDispatchMessageOnFrontendPage(m_frontendPage, message); 178 } 179 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 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 205 InspectorFrontendClient::~InspectorFrontendClient() 206 { 207 if (m_inspectorClient) { 208 m_inspectorClient->disconnectFrontendClient(); 209 m_inspectorClient = 0; 210 } 211 ASSERT(!m_webInspector); 212 } 213 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 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 250 String InspectorFrontendClient::hiddenPanels() 251 { 252 notImplemented(); 253 return String(); 254 } 255 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 265 void InspectorFrontendClient::closeWindow() 266 { 267 destroyInspectorWindow(true); 268 } 269 270 void InspectorFrontendClient::disconnectFromBackend() 271 { 272 destroyInspectorWindow(false); 273 } 274 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 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 293 void InspectorFrontendClient::setAttachedWindowHeight(unsigned height) 294 { 295 notImplemented(); 296 } 297 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