Source/WebCore: Web Inspector: Add the ability to show the resources panel on launch
[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 "FrameLoadRequest.h"
40 #include "FrameLoader.h"
41 #include "FrameView.h"
42 #include "InspectorBackendDispatcher.h"
43 #include "InspectorController.h"
44 #include "InspectorFrontendHost.h"
45 #include "InspectorPageAgent.h"
46 #include "Page.h"
47 #include "PlatformString.h"
48 #include "ScriptFunctionCall.h"
49 #include "ScriptObject.h"
50 #include "Settings.h"
51 #include "Timer.h"
52 #include "UserGestureIndicator.h"
53 #include "WindowFeatures.h"
54 #include <wtf/Deque.h>
55
56 namespace WebCore {
57
58 static const char* inspectorAttachedHeightSetting = "inspectorAttachedHeight";
59 static const unsigned defaultAttachedHeight = 300;
60 static const float minimumAttachedHeight = 250.0f;
61 static const float maximumAttachedHeightRatio = 0.75f;
62
63 class InspectorBackendDispatchTask {
64 public:
65     InspectorBackendDispatchTask(InspectorController* inspectorController)
66         : m_inspectorController(inspectorController)
67         , m_timer(this, &InspectorBackendDispatchTask::onTimer)
68     {
69     }
70
71     void dispatch(const String& message)
72     {
73         m_messages.append(message);
74         if (!m_timer.isActive())
75             m_timer.startOneShot(0);
76     }
77
78     void reset()
79     {
80         m_messages.clear();
81         m_timer.stop();
82     }
83
84     void onTimer(Timer<InspectorBackendDispatchTask>*)
85     {
86         if (!m_messages.isEmpty()) {
87             // Dispatch can lead to the timer destruction -> schedule the next shot first.
88             m_timer.startOneShot(0);
89             m_inspectorController->dispatchMessageFromFrontend(m_messages.takeFirst());
90         }
91     }
92
93 private:
94     InspectorController* m_inspectorController;
95     Timer<InspectorBackendDispatchTask> m_timer;
96     Deque<String> m_messages;
97 };
98
99 String InspectorFrontendClientLocal::Settings::getProperty(const String&)
100 {
101     return String();
102 }
103
104 void InspectorFrontendClientLocal::Settings::setProperty(const String&, const String&)
105 {
106 }
107
108 InspectorFrontendClientLocal::InspectorFrontendClientLocal(InspectorController* inspectorController, Page* frontendPage, PassOwnPtr<Settings> settings)
109     : m_inspectorController(inspectorController)
110     , m_frontendPage(frontendPage)
111     , m_frontendScriptState(0)
112     , m_settings(settings)
113     , m_frontendLoaded(false)
114 {
115     m_frontendPage->settings()->setAllowFileAccessFromFileURLs(true);
116     m_dispatchTask = adoptPtr(new InspectorBackendDispatchTask(inspectorController));
117 }
118
119 InspectorFrontendClientLocal::~InspectorFrontendClientLocal()
120 {
121     if (m_frontendHost)
122         m_frontendHost->disconnectClient();
123     m_frontendPage = 0;
124     m_frontendScriptState = 0;
125     m_inspectorController = 0;
126 }
127
128 void InspectorFrontendClientLocal::windowObjectCleared()
129 {
130     if (m_frontendHost)
131         m_frontendHost->disconnectClient();
132     // FIXME: don't keep reference to the script state
133     m_frontendScriptState = scriptStateFromPage(debuggerWorld(), m_frontendPage);
134     m_frontendHost = InspectorFrontendHost::create(this, m_frontendPage);
135     ScriptGlobalObject::set(m_frontendScriptState, "InspectorFrontendHost", m_frontendHost.get());
136 }
137
138 void InspectorFrontendClientLocal::frontendLoaded()
139 {
140     bringToFront();
141     m_frontendLoaded = true;
142     for (Vector<String>::iterator it = m_evaluateOnLoad.begin(); it != m_evaluateOnLoad.end(); ++it)
143         evaluateOnLoad(*it);
144     m_evaluateOnLoad.clear();
145 }
146
147 void InspectorFrontendClientLocal::requestAttachWindow()
148 {
149     if (!canAttachWindow())
150         return;
151     attachWindow();
152     setAttachedWindow(true);
153 }
154
155 void InspectorFrontendClientLocal::requestDetachWindow()
156 {
157     detachWindow();
158     setAttachedWindow(false);
159 }
160
161 bool InspectorFrontendClientLocal::canAttachWindow()
162 {
163     // Don't allow the attach if the window would be too small to accommodate the minimum inspector height.
164     // Also don't allow attaching to another inspector -- two inspectors in one window is too much!
165     bool isInspectorPage = m_inspectorController->inspectedPage()->inspectorController()->hasInspectorFrontendClient();
166     unsigned inspectedPageHeight = m_inspectorController->inspectedPage()->mainFrame()->view()->visibleHeight();
167     unsigned maximumAttachedHeight = inspectedPageHeight * maximumAttachedHeightRatio;
168     return minimumAttachedHeight <= maximumAttachedHeight && !isInspectorPage;
169 }
170
171 void InspectorFrontendClientLocal::changeAttachedWindowHeight(unsigned height)
172 {
173     unsigned totalHeight = m_frontendPage->mainFrame()->view()->visibleHeight() + m_inspectorController->inspectedPage()->mainFrame()->view()->visibleHeight();
174     unsigned attachedHeight = constrainedAttachedWindowHeight(height, totalHeight);
175     m_settings->setProperty(inspectorAttachedHeightSetting, String::number(attachedHeight));
176     setAttachedWindowHeight(attachedHeight);
177 }
178
179 void InspectorFrontendClientLocal::openInNewTab(const String& url)
180 {
181     UserGestureIndicator indicator(DefinitelyProcessingUserGesture);
182     Page* page = m_inspectorController->inspectedPage();
183     Frame* mainFrame = page->mainFrame();
184     FrameLoadRequest request(mainFrame->document()->securityOrigin(), ResourceRequest(), "_blank");
185
186     bool created;
187     WindowFeatures windowFeatures;
188     Frame* frame = WebCore::createWindow(mainFrame, mainFrame, request, windowFeatures, created);
189     if (!frame)
190         return;
191
192     frame->loader()->setOpener(mainFrame);
193     frame->page()->setOpenedByDOM();
194
195     // FIXME: Why does one use mainFrame and the other frame?
196     frame->loader()->changeLocation(mainFrame->document()->securityOrigin(), frame->document()->completeURL(url), "", false, false);
197 }
198
199 void InspectorFrontendClientLocal::moveWindowBy(float x, float y)
200 {
201     FloatRect frameRect = m_frontendPage->chrome()->windowRect();
202     frameRect.move(x, y);
203     m_frontendPage->chrome()->setWindowRect(frameRect);
204 }
205
206 void InspectorFrontendClientLocal::setAttachedWindow(bool attached)
207 {
208     evaluateAsBoolean(String::format("InspectorFrontendAPI.setAttachedWindow(%s)", attached ? "true" : "false"));
209 }
210
211 void InspectorFrontendClientLocal::restoreAttachedWindowHeight()
212 {
213     unsigned inspectedPageHeight = m_inspectorController->inspectedPage()->mainFrame()->view()->visibleHeight();
214     String value = m_settings->getProperty(inspectorAttachedHeightSetting);
215     unsigned preferredHeight = value.isEmpty() ? defaultAttachedHeight : value.toUInt();
216     
217     // This call might not go through (if the window starts out detached), but if the window is initially created attached,
218     // InspectorController::attachWindow is never called, so we need to make sure to set the attachedWindowHeight.
219     // FIXME: Clean up code so we only have to call setAttachedWindowHeight in InspectorController::attachWindow
220     setAttachedWindowHeight(constrainedAttachedWindowHeight(preferredHeight, inspectedPageHeight));
221 }
222
223 bool InspectorFrontendClientLocal::isDebuggingEnabled()
224 {
225     if (m_frontendLoaded)
226         return evaluateAsBoolean("[\"isDebuggingEnabled\"]");
227     return false;
228 }
229
230 void InspectorFrontendClientLocal::setDebuggingEnabled(bool enabled)
231 {
232     evaluateOnLoad(String::format("[\"setDebuggingEnabled\", %s]", enabled ? "true" : "false"));
233 }
234
235 bool InspectorFrontendClientLocal::isTimelineProfilingEnabled()
236 {
237     if (m_frontendLoaded)
238         return evaluateAsBoolean("[\"isTimelineProfilingEnabled\"]");
239     return false;
240 }
241
242 void InspectorFrontendClientLocal::setTimelineProfilingEnabled(bool enabled)
243 {
244     evaluateOnLoad(String::format("[\"setTimelineProfilingEnabled\", %s]", enabled ? "true" : "false"));
245 }
246
247 bool InspectorFrontendClientLocal::isProfilingJavaScript()
248 {
249     if (m_frontendLoaded)
250         return evaluateAsBoolean("[\"isProfilingJavaScript\"]");
251     return false;
252 }
253
254 void InspectorFrontendClientLocal::startProfilingJavaScript()
255 {
256     evaluateOnLoad("[\"startProfilingJavaScript\"]");
257 }
258
259 void InspectorFrontendClientLocal::stopProfilingJavaScript()
260 {
261     evaluateOnLoad("[\"stopProfilingJavaScript\"]");
262 }
263
264 void InspectorFrontendClientLocal::showConsole()
265 {
266     evaluateOnLoad("[\"showConsole\"]");
267 }
268
269 void InspectorFrontendClientLocal::showResources()
270 {
271     evaluateOnLoad("[\"showResources\"]");
272 }
273
274 void InspectorFrontendClientLocal::showMainResourceForFrame(Frame* frame)
275 {
276     String frameId = m_inspectorController->pageAgent()->frameId(frame);
277     evaluateOnLoad(String::format("[\"showMainResourceForFrame\", \"%s\"]", frameId.ascii().data()));
278 }
279
280 unsigned InspectorFrontendClientLocal::constrainedAttachedWindowHeight(unsigned preferredHeight, unsigned totalWindowHeight)
281 {
282     using namespace std;
283     return roundf(max(minimumAttachedHeight, min<float>(preferredHeight, totalWindowHeight * maximumAttachedHeightRatio)));
284 }
285
286 void InspectorFrontendClientLocal::sendMessageToBackend(const String& message)
287 {
288     m_dispatchTask->dispatch(message);
289 }
290
291 bool InspectorFrontendClientLocal::evaluateAsBoolean(const String& expression)
292 {
293     if (!m_frontendPage->mainFrame())
294         return false;
295     ScriptValue value = m_frontendPage->mainFrame()->script()->executeScript(expression);
296     return value.toString(mainWorldScriptState(m_frontendPage->mainFrame())) == "true";
297 }
298
299 void InspectorFrontendClientLocal::evaluateOnLoad(const String& expression)
300 {
301     if (m_frontendLoaded)
302         m_frontendPage->mainFrame()->script()->executeScript("InspectorFrontendAPI.dispatch(" + expression + ")");
303     else
304         m_evaluateOnLoad.append(expression);
305 }
306
307 } // namespace WebCore
308
309 #endif