Web Inspector: query backend for capabilities explicitly.
[WebKit-https.git] / Source / WebCore / inspector / InspectorFrontendClientLocal.cpp
1 /*
2  * Copyright (C) 2010 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32 #include "InspectorFrontendClientLocal.h"
33
34 #if ENABLE(INSPECTOR)
35
36 #include "Chrome.h"
37 #include "FloatRect.h"
38 #include "Frame.h"
39 #include "FrameView.h"
40 #include "InspectorBackendDispatcher.h"
41 #include "InspectorController.h"
42 #include "InspectorFrontendHost.h"
43 #include "Page.h"
44 #include "PlatformString.h"
45 #include "ScriptFunctionCall.h"
46 #include "ScriptObject.h"
47 #include "Settings.h"
48 #include "Timer.h"
49 #include <wtf/Deque.h>
50
51 namespace WebCore {
52
53 static const char* inspectorAttachedHeightSetting = "inspectorAttachedHeight";
54 static const unsigned defaultAttachedHeight = 300;
55 static const float minimumAttachedHeight = 250.0f;
56 static const float maximumAttachedHeightRatio = 0.75f;
57
58 class InspectorBackendDispatchTask {
59 public:
60     InspectorBackendDispatchTask(InspectorController* inspectorController)
61         : m_inspectorController(inspectorController)
62         , m_timer(this, &InspectorBackendDispatchTask::onTimer)
63     {
64     }
65
66     void dispatch(const String& message)
67     {
68         m_messages.append(message);
69         if (!m_timer.isActive())
70             m_timer.startOneShot(0);
71     }
72
73     void reset()
74     {
75         m_messages.clear();
76         m_timer.stop();
77     }
78
79     void onTimer(Timer<InspectorBackendDispatchTask>*)
80     {
81         if (!m_messages.isEmpty()) {
82             // Dispatch can lead to the timer destruction -> schedule the next shot first.
83             m_timer.startOneShot(0);
84             m_inspectorController->dispatchMessageFromFrontend(m_messages.takeFirst());
85         }
86     }
87
88 private:
89     InspectorController* m_inspectorController;
90     Timer<InspectorBackendDispatchTask> m_timer;
91     Deque<String> m_messages;
92 };
93
94 String InspectorFrontendClientLocal::Settings::getProperty(const String&)
95 {
96     return String();
97 }
98
99 void InspectorFrontendClientLocal::Settings::setProperty(const String&, const String&)
100 {
101 }
102
103 InspectorFrontendClientLocal::InspectorFrontendClientLocal(InspectorController* inspectorController, Page* frontendPage, PassOwnPtr<Settings> settings)
104     : m_inspectorController(inspectorController)
105     , m_frontendPage(frontendPage)
106     , m_frontendScriptState(0)
107     , m_settings(settings)
108     , m_frontendLoaded(false)
109 {
110     m_frontendPage->settings()->setAllowFileAccessFromFileURLs(true);
111     m_dispatchTask = adoptPtr(new InspectorBackendDispatchTask(inspectorController));
112 }
113
114 InspectorFrontendClientLocal::~InspectorFrontendClientLocal()
115 {
116     if (m_frontendHost)
117         m_frontendHost->disconnectClient();
118     m_frontendPage = 0;
119     m_frontendScriptState = 0;
120     m_inspectorController = 0;
121 }
122
123 void InspectorFrontendClientLocal::windowObjectCleared()
124 {
125     if (m_frontendHost)
126         m_frontendHost->disconnectClient();
127     // FIXME: don't keep reference to the script state
128     m_frontendScriptState = scriptStateFromPage(debuggerWorld(), m_frontendPage);
129     m_frontendHost = InspectorFrontendHost::create(this, m_frontendPage);
130     ScriptGlobalObject::set(m_frontendScriptState, "InspectorFrontendHost", m_frontendHost.get());
131 }
132
133 void InspectorFrontendClientLocal::frontendLoaded()
134 {
135     bringToFront();
136     m_frontendLoaded = true;
137     if (!m_evaluateOnLoad.isEmpty())
138         evaluateOnLoad(m_evaluateOnLoad);
139 }
140
141 void InspectorFrontendClientLocal::requestAttachWindow()
142 {
143     if (!canAttachWindow())
144         return;
145     attachWindow();
146     setAttachedWindow(true);
147 }
148
149 void InspectorFrontendClientLocal::requestDetachWindow()
150 {
151     detachWindow();
152     setAttachedWindow(false);
153 }
154
155 bool InspectorFrontendClientLocal::canAttachWindow()
156 {
157     unsigned inspectedPageHeight = m_inspectorController->inspectedPage()->mainFrame()->view()->visibleHeight();
158
159     // Don't allow the attach if the window would be too small to accommodate the minimum inspector height.
160     return minimumAttachedHeight <= inspectedPageHeight * maximumAttachedHeightRatio;
161 }
162
163 void InspectorFrontendClientLocal::changeAttachedWindowHeight(unsigned height)
164 {
165     unsigned totalHeight = m_frontendPage->mainFrame()->view()->visibleHeight() + m_inspectorController->inspectedPage()->mainFrame()->view()->visibleHeight();
166     unsigned attachedHeight = constrainedAttachedWindowHeight(height, totalHeight);
167     m_settings->setProperty(inspectorAttachedHeightSetting, String::number(attachedHeight));
168     setAttachedWindowHeight(attachedHeight);
169 }
170
171 void InspectorFrontendClientLocal::moveWindowBy(float x, float y)
172 {
173     FloatRect frameRect = m_frontendPage->chrome()->windowRect();
174     frameRect.move(x, y);
175     m_frontendPage->chrome()->setWindowRect(frameRect);
176 }
177
178 void InspectorFrontendClientLocal::setAttachedWindow(bool attached)
179 {
180     evaluateAsBoolean(String::format("InspectorFrontendAPI.setAttachedWindow(%s)", attached ? "true" : "false"));
181 }
182
183 void InspectorFrontendClientLocal::restoreAttachedWindowHeight()
184 {
185     unsigned inspectedPageHeight = m_inspectorController->inspectedPage()->mainFrame()->view()->visibleHeight();
186     String value = m_settings->getProperty(inspectorAttachedHeightSetting);
187     unsigned preferredHeight = value.isEmpty() ? defaultAttachedHeight : value.toUInt();
188     
189     // This call might not go through (if the window starts out detached), but if the window is initially created attached,
190     // InspectorController::attachWindow is never called, so we need to make sure to set the attachedWindowHeight.
191     // FIXME: Clean up code so we only have to call setAttachedWindowHeight in InspectorController::attachWindow
192     setAttachedWindowHeight(constrainedAttachedWindowHeight(preferredHeight, inspectedPageHeight));
193 }
194
195 bool InspectorFrontendClientLocal::isDebuggingEnabled()
196 {
197     if (m_frontendLoaded)
198         return evaluateAsBoolean("InspectorFrontendAPI.isDebuggingEnabled()");
199     return false;
200 }
201
202 void InspectorFrontendClientLocal::setDebuggingEnabled(bool enabled)
203 {
204     evaluateOnLoad(String::format("InspectorFrontendAPI.setDebuggingEnabled(%s)", enabled ? "true" : "false"));
205 }
206
207 bool InspectorFrontendClientLocal::isTimelineProfilingEnabled()
208 {
209     if (m_frontendLoaded)
210         return evaluateAsBoolean("InspectorFrontendAPI.isTimelineProfilingEnabled()");
211     return false;
212 }
213
214 void InspectorFrontendClientLocal::setTimelineProfilingEnabled(bool enabled)
215 {
216     evaluateOnLoad(String::format("InspectorFrontendAPI.setTimelineProfilingEnabled(%s)", enabled ? "true" : "false"));
217 }
218
219 bool InspectorFrontendClientLocal::isProfilingJavaScript()
220 {
221     if (m_frontendLoaded)
222         return evaluateAsBoolean("InspectorFrontendAPI.isProfilingJavaScript()");
223     return false;
224 }
225
226 void InspectorFrontendClientLocal::startProfilingJavaScript()
227 {
228     evaluateOnLoad("InspectorFrontendAPI.startProfilingJavaScript()");
229 }
230
231 void InspectorFrontendClientLocal::stopProfilingJavaScript()
232 {
233     evaluateOnLoad("InspectorFrontendAPI.stopProfilingJavaScript()");
234 }
235
236 void InspectorFrontendClientLocal::showConsole()
237 {
238     evaluateOnLoad("InspectorFrontendAPI.showConsole()");
239 }
240
241 unsigned InspectorFrontendClientLocal::constrainedAttachedWindowHeight(unsigned preferredHeight, unsigned totalWindowHeight)
242 {
243     using namespace std;
244     return roundf(max(minimumAttachedHeight, min<float>(preferredHeight, totalWindowHeight * maximumAttachedHeightRatio)));
245 }
246
247 void InspectorFrontendClientLocal::sendMessageToBackend(const String& message)
248 {
249     m_dispatchTask->dispatch(message);
250 }
251
252 bool InspectorFrontendClientLocal::evaluateAsBoolean(const String& expression)
253 {
254     if (!m_frontendPage->mainFrame())
255         return false;
256     ScriptValue value = m_frontendPage->mainFrame()->script()->executeScript(expression);
257     return value.toString(mainWorldScriptState(m_frontendPage->mainFrame())) == "true";
258 }
259
260 void InspectorFrontendClientLocal::evaluateOnLoad(const String& expression)
261 {
262     if (m_frontendLoaded)
263         m_frontendPage->mainFrame()->script()->executeScript(expression);
264     else
265         m_evaluateOnLoad = "setTimeout(function() { " + expression + "; }, 0)";
266 }
267
268 } // namespace WebCore
269
270 #endif