Web Inspector: introduce InspectorFrontendAPI for actions initiated from the applicat...
[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_frontendScriptState = 0;
119     m_frontendPage = 0;
120     m_inspectorController = 0;
121 }
122
123 void InspectorFrontendClientLocal::windowObjectCleared()
124 {
125     // FIXME: don't keep reference to the script state
126     m_frontendScriptState = scriptStateFromPage(debuggerWorld(), m_frontendPage);
127     m_frontendHost = InspectorFrontendHost::create(this, m_frontendPage);
128     ScriptGlobalObject::set(m_frontendScriptState, "InspectorFrontendHost", m_frontendHost.get());
129 }
130
131 void InspectorFrontendClientLocal::frontendLoaded()
132 {
133     bringToFront();
134     m_inspectorController->connectFrontend();
135     m_frontendLoaded = true;
136     if (!m_evaluateOnLoad.isEmpty())
137         evaluateOnLoad(m_evaluateOnLoad);
138 }
139
140 void InspectorFrontendClientLocal::requestAttachWindow()
141 {
142     if (!canAttachWindow())
143         return;
144     attachWindow();
145     setAttachedWindow(true);
146 }
147
148 void InspectorFrontendClientLocal::requestDetachWindow()
149 {
150     detachWindow();
151     setAttachedWindow(false);
152 }
153
154 bool InspectorFrontendClientLocal::canAttachWindow()
155 {
156     unsigned inspectedPageHeight = m_inspectorController->inspectedPage()->mainFrame()->view()->visibleHeight();
157
158     // Don't allow the attach if the window would be too small to accommodate the minimum inspector height.
159     return minimumAttachedHeight <= inspectedPageHeight * maximumAttachedHeightRatio;
160 }
161
162 void InspectorFrontendClientLocal::changeAttachedWindowHeight(unsigned height)
163 {
164     unsigned totalHeight = m_frontendPage->mainFrame()->view()->visibleHeight() + m_inspectorController->inspectedPage()->mainFrame()->view()->visibleHeight();
165     unsigned attachedHeight = constrainedAttachedWindowHeight(height, totalHeight);
166     m_settings->setProperty(inspectorAttachedHeightSetting, String::number(attachedHeight));
167     setAttachedWindowHeight(attachedHeight);
168 }
169
170 void InspectorFrontendClientLocal::moveWindowBy(float x, float y)
171 {
172     FloatRect frameRect = m_frontendPage->chrome()->windowRect();
173     frameRect.move(x, y);
174     m_frontendPage->chrome()->setWindowRect(frameRect);
175 }
176
177 void InspectorFrontendClientLocal::setAttachedWindow(bool attached)
178 {
179     evaluateAsBoolean(String::format("InspectorFrontendAPI.setAttachedWindow(%s)", attached ? "true" : "false"));
180 }
181
182 void InspectorFrontendClientLocal::restoreAttachedWindowHeight()
183 {
184     unsigned inspectedPageHeight = m_inspectorController->inspectedPage()->mainFrame()->view()->visibleHeight();
185     String value = m_settings->getProperty(inspectorAttachedHeightSetting);
186     unsigned preferredHeight = value.isEmpty() ? defaultAttachedHeight : value.toUInt();
187     
188     // This call might not go through (if the window starts out detached), but if the window is initially created attached,
189     // InspectorController::attachWindow is never called, so we need to make sure to set the attachedWindowHeight.
190     // FIXME: Clean up code so we only have to call setAttachedWindowHeight in InspectorController::attachWindow
191     setAttachedWindowHeight(constrainedAttachedWindowHeight(preferredHeight, inspectedPageHeight));
192 }
193
194 bool InspectorFrontendClientLocal::isDebuggingEnabled()
195 {
196     if (m_frontendLoaded)
197         return evaluateAsBoolean("InspectorFrontendAPI.isDebuggingEnabled()");
198     return false;
199 }
200
201 void InspectorFrontendClientLocal::setDebuggingEnabled(bool enabled)
202 {
203     evaluateOnLoad(String::format("InspectorFrontendAPI.setDebuggingEnabled(%s)", enabled ? "true" : "false"));
204 }
205
206 bool InspectorFrontendClientLocal::isTimelineProfilingEnabled()
207 {
208     if (m_frontendLoaded)
209         return evaluateAsBoolean("InspectorFrontendAPI.isTimelineProfilingEnabled()");
210     return false;
211 }
212
213 void InspectorFrontendClientLocal::setTimelineProfilingEnabled(bool enabled)
214 {
215     evaluateOnLoad(String::format("InspectorFrontendAPI.setTimelineProfilingEnabled(%s)", enabled ? "true" : "false"));
216 }
217
218 bool InspectorFrontendClientLocal::isProfilingJavaScript()
219 {
220     if (m_frontendLoaded)
221         return evaluateAsBoolean("InspectorFrontendAPI.isProfilingJavaScript()");
222     return false;
223 }
224
225 void InspectorFrontendClientLocal::startProfilingJavaScript()
226 {
227     evaluateOnLoad("InspectorFrontendAPI.startProfilingJavaScript()");
228 }
229
230 void InspectorFrontendClientLocal::stopProfilingJavaScript()
231 {
232     evaluateOnLoad("InspectorFrontendAPI.stopProfilingJavaScript()");
233 }
234
235 void InspectorFrontendClientLocal::showConsole()
236 {
237     evaluateOnLoad("InspectorFrontendAPI.showConsole()");
238 }
239
240 unsigned InspectorFrontendClientLocal::constrainedAttachedWindowHeight(unsigned preferredHeight, unsigned totalWindowHeight)
241 {
242     using namespace std;
243     return roundf(max(minimumAttachedHeight, min<float>(preferredHeight, totalWindowHeight * maximumAttachedHeightRatio)));
244 }
245
246 void InspectorFrontendClientLocal::sendMessageToBackend(const String& message)
247 {
248     m_dispatchTask->dispatch(message);
249 }
250
251 bool InspectorFrontendClientLocal::evaluateAsBoolean(const String& expression)
252 {
253     if (!m_frontendPage->mainFrame())
254         return false;
255     ScriptValue value = m_frontendPage->mainFrame()->script()->executeScript(expression);
256     return value.toString(mainWorldScriptState(m_frontendPage->mainFrame())) == "true";
257 }
258
259 void InspectorFrontendClientLocal::evaluateOnLoad(const String& expression)
260 {
261     if (m_frontendLoaded)
262         m_frontendPage->mainFrame()->script()->executeScript(expression);
263     else
264         m_evaluateOnLoad = "setTimeout(function() { " + expression + "; }, 0)";
265 }
266
267 } // namespace WebCore
268
269 #endif