2011-02-03 Yury Semikhatsky <yurys@chromium.org>
[WebKit-https.git] / Source / WebKit / qt / WebCoreSupport / InspectorClientQt.cpp
1 /*
2  * Copyright (C) 2007 Apple Inc.  All rights reserved.
3  * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
4  * Copyright (C) 2008 Holger Hans Peter Freyther
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1.  Redistributions of source code must retain the above copyright
11  *     notice, this list of conditions and the following disclaimer. 
12  * 2.  Redistributions in binary form must reproduce the above copyright
13  *     notice, this list of conditions and the following disclaimer in the
14  *     documentation and/or other materials provided with the distribution. 
15  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
16  *     its contributors may be used to endorse or promote products derived
17  *     from this software without specific prior written permission. 
18  *
19  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
20  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
23  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32 #include "InspectorClientQt.h"
33
34 #include "Frame.h"
35 #include "InspectorBackendDispatcher.h"
36 #include "InspectorController.h"
37 #include "InspectorFrontend.h"
38 #include "InspectorServerQt.h"
39 #include "NotImplemented.h"
40 #include "Page.h"
41 #include "PlatformString.h"
42 #include "ScriptDebugServer.h"
43 #include "qwebinspector.h"
44 #include "qwebinspector_p.h"
45 #include "qwebpage.h"
46 #include "qwebpage_p.h"
47 #include "qwebview.h"
48 #include <QtCore/QCoreApplication>
49 #include <QtCore/QFile>
50 #include <QtCore/QSettings>
51 #include <QtCore/QVariant>
52
53 namespace WebCore {
54
55 static const QLatin1String settingStoragePrefix("Qt/QtWebKit/QWebInspector/");
56 static const QLatin1String settingStorageTypeSuffix(".type");
57
58 class InspectorClientWebPage : public QWebPage {
59     Q_OBJECT
60     friend class InspectorClientQt;
61 public:
62     InspectorClientWebPage(QObject* parent = 0)
63         : QWebPage(parent)
64     {
65         connect(mainFrame(), SIGNAL(javaScriptWindowObjectCleared()), SLOT(javaScriptWindowObjectCleared()));
66     }
67
68     QWebPage* createWindow(QWebPage::WebWindowType)
69     {
70         QWebView* view = new QWebView;
71         QWebPage* page = new QWebPage;
72         view->setPage(page);
73         view->setAttribute(Qt::WA_DeleteOnClose);
74         return page;
75     }
76
77 public slots:
78     void javaScriptWindowObjectCleared() 
79     {
80 #ifndef QT_NO_PROPERTIES
81         QVariant inspectorJavaScriptWindowObjects = property("_q_inspectorJavaScriptWindowObjects");
82         if (!inspectorJavaScriptWindowObjects.isValid())
83             return;
84         QMap<QString, QVariant> javaScriptNameObjectMap = inspectorJavaScriptWindowObjects.toMap();
85         QWebFrame* frame = mainFrame();
86         QMap<QString, QVariant>::const_iterator it = javaScriptNameObjectMap.constBegin();
87         for ( ; it != javaScriptNameObjectMap.constEnd(); ++it) {
88             QString name = it.key();
89             QVariant value = it.value();
90             QObject* obj = value.value<QObject*>();
91             frame->addToJavaScriptWindowObject(name, obj);
92         }
93 #endif
94     }
95 };
96
97 namespace {
98
99 #if ENABLE(INSPECTOR)
100 class InspectorFrontendSettingsQt : public InspectorFrontendClientLocal::Settings {
101 public:
102     virtual ~InspectorFrontendSettingsQt() { }
103     virtual String getProperty(const String& name)
104     {
105 #ifdef QT_NO_SETTINGS
106         Q_UNUSED(name)
107         Q_UNUSED(value)
108         qWarning("QWebInspector: QSettings is not supported by Qt.");
109         return String();
110 #else
111         QSettings qsettings;
112         if (qsettings.status() == QSettings::AccessError) {
113             // QCoreApplication::setOrganizationName and QCoreApplication::setApplicationName haven't been called
114             qWarning("QWebInspector: QSettings couldn't read configuration setting [%s].",
115                      qPrintable(static_cast<QString>(name)));
116             return String();
117         }
118
119         QString settingKey(settingStoragePrefix + QString(name));
120         QString storedValueType = qsettings.value(settingKey + settingStorageTypeSuffix).toString();
121         QVariant storedValue = qsettings.value(settingKey);
122         storedValue.convert(QVariant::nameToType(storedValueType.toAscii().data()));
123         return variantToSetting(storedValue);
124 #endif // QT_NO_SETTINGS
125     }
126
127     virtual void setProperty(const String& name, const String& value)
128     {
129 #ifdef QT_NO_SETTINGS
130         Q_UNUSED(name)
131         Q_UNUSED(value)
132         qWarning("QWebInspector: QSettings is not supported by Qt.");
133 #else
134         QSettings qsettings;
135         if (qsettings.status() == QSettings::AccessError) {
136             qWarning("QWebInspector: QSettings couldn't persist configuration setting [%s].",
137                      qPrintable(static_cast<QString>(name)));
138             return;
139         }
140
141         QVariant valueToStore = settingToVariant(value);
142         QString settingKey(settingStoragePrefix + QString(name));
143         qsettings.setValue(settingKey, valueToStore);
144         qsettings.setValue(settingKey + settingStorageTypeSuffix, QVariant::typeToName(valueToStore.type()));
145 #endif // QT_NO_SETTINGS
146     }
147
148 private:
149     static String variantToSetting(const QVariant& qvariant)
150     {
151         String retVal;
152
153         switch (qvariant.type()) {
154         case QVariant::Bool:
155             retVal = qvariant.toBool() ? "true" : "false";
156         case QVariant::String:
157             retVal = qvariant.toString();
158         default:
159             break;
160         }
161
162         return retVal;
163     }
164
165     static QVariant settingToVariant(const String& setting)
166     {
167         QVariant retVal;
168         retVal.setValue(static_cast<QString>(setting));
169         return retVal;
170     }
171 };
172 #endif // ENABLE(INSPECTOR)
173
174 }
175
176 #if USE(V8)
177 static void ensureDebuggerScriptLoaded()
178 {
179     static bool scriptLoaded = false;
180     if (scriptLoaded)
181         return;
182
183     QFile debuggerScriptFile(":/webkit/inspector/DebuggerScript.js");
184     if (debuggerScriptFile.open(QIODevice::ReadOnly)) {
185         QByteArray ba = debuggerScriptFile.readAll();
186         ScriptDebugServer::shared().setDebuggerScriptSource(String(ba.constData(), ba.length()));
187         scriptLoaded = true;
188     }
189 }
190 #endif
191
192 InspectorClientQt::InspectorClientQt(QWebPage* page)
193     : m_inspectedWebPage(page)
194     , m_frontendWebPage(0)
195     , m_frontendClient(0)
196 {
197     InspectorServerQt* webInspectorServer = InspectorServerQt::server();
198     if (webInspectorServer)
199         webInspectorServer->registerClient(this);
200 }
201
202 void InspectorClientQt::inspectorDestroyed()
203 {
204 #if ENABLE(INSPECTOR)
205     if (m_frontendClient)
206         m_frontendClient->inspectorClientDestroyed();
207
208     InspectorServerQt* webInspectorServer = InspectorServerQt::server();
209     if (webInspectorServer)
210         webInspectorServer->unregisterClient(this);
211
212     delete this;
213 #endif
214 }
215
216     
217 void InspectorClientQt::openInspectorFrontend(WebCore::InspectorController* inspectorController)
218 {
219 #if ENABLE(INSPECTOR)
220 #if USE(V8)
221     ensureDebuggerScriptLoaded();
222 #endif
223
224     QWebView* inspectorView = new QWebView;
225     InspectorClientWebPage* inspectorPage = new InspectorClientWebPage(inspectorView);
226     inspectorView->setPage(inspectorPage);
227
228     QWebInspector* inspector = m_inspectedWebPage->d->getOrCreateInspector();
229     // Remote frontend was attached.
230     if (m_inspectedWebPage->d->inspector->d->remoteFrontend)
231         return;
232
233     // This is a known hook that allows changing the default URL for the
234     // Web inspector. This is used for SDK purposes. Please keep this hook
235     // around and don't remove it.
236     // https://bugs.webkit.org/show_bug.cgi?id=35340
237     QUrl inspectorUrl;
238 #ifndef QT_NO_PROPERTIES
239     inspectorUrl = inspector->property("_q_inspectorUrl").toUrl();
240 #endif
241     if (!inspectorUrl.isValid())
242         inspectorUrl = QUrl("qrc:/webkit/inspector/inspector.html");
243
244 #ifndef QT_NO_PROPERTIES
245     QVariant inspectorJavaScriptWindowObjects = inspector->property("_q_inspectorJavaScriptWindowObjects");
246     if (inspectorJavaScriptWindowObjects.isValid())
247         inspectorPage->setProperty("_q_inspectorJavaScriptWindowObjects", inspectorJavaScriptWindowObjects);
248 #endif
249     inspectorView->page()->mainFrame()->load(inspectorUrl);
250     m_inspectedWebPage->d->inspectorFrontend = inspectorView;
251     inspector->d->setFrontend(inspectorView);
252
253     m_frontendClient = new InspectorFrontendClientQt(m_inspectedWebPage, inspectorView, this);
254     inspectorView->page()->d->page->inspectorController()->setInspectorFrontendClient(m_frontendClient);
255     m_frontendWebPage = inspectorPage;
256 #endif
257 }
258
259 void InspectorClientQt::releaseFrontendPage()
260 {
261     m_frontendWebPage = 0;
262     m_frontendClient = 0;
263 }
264
265 void InspectorClientQt::attachAndReplaceRemoteFrontend(RemoteFrontendChannel* channel)
266 {
267 #if ENABLE(INSPECTOR)
268     // Channel was allocated by InspectorServerQt. Here we transfer ownership to inspector.
269     m_inspectedWebPage->d->inspector->d->attachAndReplaceRemoteFrontend(channel);
270     m_inspectedWebPage->d->inspectorController()->connectFrontend();
271 #endif
272 }
273
274 void InspectorClientQt::detachRemoteFrontend()
275 {
276 #if ENABLE(INSPECTOR)
277     m_inspectedWebPage->d->inspector->d->detachRemoteFrontend();
278     m_inspectedWebPage->d->inspectorController()->disconnectFrontend();
279 #endif
280 }
281
282 void InspectorClientQt::highlight(Node*)
283 {
284     notImplemented();
285 }
286
287 void InspectorClientQt::hideHighlight()
288 {
289     notImplemented();
290 }
291
292 bool InspectorClientQt::sendMessageToFrontend(const String& message)
293 {
294 #if ENABLE(INSPECTOR)
295     if (m_inspectedWebPage->d->inspector->d->remoteFrontend) {
296         RemoteFrontendChannel* session = qobject_cast<RemoteFrontendChannel*>(m_inspectedWebPage->d->inspector->d->remoteFrontend);
297         if (session)
298             session->sendMessageToFrontend(message);
299         return true;
300     }
301     if (!m_frontendWebPage)
302         return false;
303
304     Page* frontendPage = QWebPagePrivate::core(m_frontendWebPage);
305     return doDispatchMessageOnFrontendPage(frontendPage, message);
306 #else
307     return false;
308 #endif
309 }
310
311 #if ENABLE(INSPECTOR)
312 InspectorFrontendClientQt::InspectorFrontendClientQt(QWebPage* inspectedWebPage, PassOwnPtr<QWebView> inspectorView, InspectorClientQt* inspectorClient)
313     : InspectorFrontendClientLocal(inspectedWebPage->d->page->inspectorController(), inspectorView->page()->d->page, new InspectorFrontendSettingsQt())
314     , m_inspectedWebPage(inspectedWebPage)
315     , m_inspectorView(inspectorView)
316     , m_destroyingInspectorView(false)
317     , m_inspectorClient(inspectorClient)
318 {
319 }
320
321 InspectorFrontendClientQt::~InspectorFrontendClientQt()
322 {
323     ASSERT(m_destroyingInspectorView);
324     if (m_inspectorClient)
325         m_inspectorClient->releaseFrontendPage();
326 }
327
328 void InspectorFrontendClientQt::frontendLoaded()
329 {
330     InspectorFrontendClientLocal::frontendLoaded();
331     setAttachedWindow(true);
332 }
333
334 String InspectorFrontendClientQt::localizedStringsURL()
335 {
336     notImplemented();
337     return String();
338 }
339
340 String InspectorFrontendClientQt::hiddenPanels()
341 {
342     notImplemented();
343     return String();
344 }
345
346 void InspectorFrontendClientQt::bringToFront()
347 {
348     updateWindowTitle();
349 }
350
351 void InspectorFrontendClientQt::closeWindow()
352 {
353     destroyInspectorView(true);
354 }
355
356 void InspectorFrontendClientQt::disconnectFromBackend()
357 {
358     destroyInspectorView(false);
359 }
360
361 void InspectorFrontendClientQt::attachWindow()
362 {
363     notImplemented();
364 }
365
366 void InspectorFrontendClientQt::detachWindow()
367 {
368     notImplemented();
369 }
370
371 void InspectorFrontendClientQt::setAttachedWindowHeight(unsigned)
372 {
373     notImplemented();
374 }
375
376 void InspectorFrontendClientQt::inspectedURLChanged(const String& newURL)
377 {
378     m_inspectedURL = newURL;
379     updateWindowTitle();
380 }
381
382 void InspectorFrontendClientQt::updateWindowTitle()
383 {
384     if (m_inspectedWebPage->d->inspector) {
385         QString caption = QCoreApplication::translate("QWebPage", "Web Inspector - %2").arg(m_inspectedURL);
386         m_inspectedWebPage->d->inspector->setWindowTitle(caption);
387     }
388 }
389
390 void InspectorFrontendClientQt::destroyInspectorView(bool notifyInspectorController)
391 {
392     if (m_destroyingInspectorView)
393         return;
394     m_destroyingInspectorView = true;
395
396     // Inspected page may have already been destroyed.
397     if (m_inspectedWebPage) {
398         // Clear reference from QWebInspector to the frontend view.
399         m_inspectedWebPage->d->getOrCreateInspector()->d->setFrontend(0);
400     }
401
402 #if ENABLE(INSPECTOR)
403     if (notifyInspectorController)
404         m_inspectedWebPage->d->inspectorController()->disconnectFrontend();
405 #endif
406     if (m_inspectorClient)
407         m_inspectorClient->releaseFrontendPage();
408
409     // Clear pointer before deleting WebView to avoid recursive calls to its destructor.
410     OwnPtr<QWebView> inspectorView = m_inspectorView.release();
411 }
412
413 void InspectorFrontendClientQt::inspectorClientDestroyed()
414 {
415     m_inspectorClient = 0;
416     m_inspectedWebPage = 0;
417 }
418 #endif
419 }
420
421 #include "InspectorClientQt.moc"