Replace uses of PassOwnPtr/OwnPtr with std::unique_ptr in WebCore/inspector
[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
33 #if ENABLE(INSPECTOR)
34
35 #include "InspectorFrontendClientLocal.h"
36
37 #include "Chrome.h"
38 #include "DOMWrapperWorld.h"
39 #include "Document.h"
40 #include "FloatRect.h"
41 #include "FrameLoadRequest.h"
42 #include "FrameLoader.h"
43 #include "FrameView.h"
44 #include "InspectorController.h"
45 #include "InspectorFrontendHost.h"
46 #include "InspectorPageAgent.h"
47 #include "InspectorWebBackendDispatchers.h"
48 #include "MainFrame.h"
49 #include "Page.h"
50 #include "ScriptController.h"
51 #include "ScriptGlobalObject.h"
52 #include "ScriptState.h"
53 #include "Settings.h"
54 #include "Timer.h"
55 #include "UserGestureIndicator.h"
56 #include "WindowFeatures.h"
57 #include <bindings/ScriptValue.h>
58 #include <wtf/Deque.h>
59 #include <wtf/text/CString.h>
60 #include <wtf/text/WTFString.h>
61
62 using namespace Inspector;
63
64 namespace WebCore {
65
66 static const char* inspectorAttachedHeightSetting = "inspectorAttachedHeight";
67 static const unsigned defaultAttachedHeight = 300;
68 static const float minimumAttachedHeight = 250.0f;
69 static const float maximumAttachedHeightRatio = 0.75f;
70 static const float minimumAttachedWidth = 750.0f;
71 static const float minimumAttachedInspectedWidth = 320.0f;
72
73 class InspectorBackendDispatchTask {
74     WTF_MAKE_FAST_ALLOCATED;
75 public:
76     InspectorBackendDispatchTask(InspectorController* inspectorController)
77         : m_inspectorController(inspectorController)
78         , m_timer(this, &InspectorBackendDispatchTask::timerFired)
79     {
80     }
81
82     void dispatch(const String& message)
83     {
84         m_messages.append(message);
85         if (!m_timer.isActive())
86             m_timer.startOneShot(0);
87     }
88
89     void reset()
90     {
91         m_messages.clear();
92         m_timer.stop();
93     }
94
95     void timerFired(Timer<InspectorBackendDispatchTask>&)
96     {
97         if (!m_messages.isEmpty()) {
98             // Dispatch can lead to the timer destruction -> schedule the next shot first.
99             m_timer.startOneShot(0);
100             m_inspectorController->dispatchMessageFromFrontend(m_messages.takeFirst());
101         }
102     }
103
104 private:
105     InspectorController* m_inspectorController;
106     Timer<InspectorBackendDispatchTask> m_timer;
107     Deque<String> m_messages;
108 };
109
110 String InspectorFrontendClientLocal::Settings::getProperty(const String&)
111 {
112     return String();
113 }
114
115 void InspectorFrontendClientLocal::Settings::setProperty(const String&, const String&)
116 {
117 }
118
119 InspectorFrontendClientLocal::InspectorFrontendClientLocal(InspectorController* inspectorController, Page* frontendPage, std::unique_ptr<Settings> settings)
120     : m_inspectorController(inspectorController)
121     , m_frontendPage(frontendPage)
122     , m_settings(std::move(settings))
123     , m_frontendLoaded(false)
124     , m_dockSide(UNDOCKED)
125 {
126     m_frontendPage->settings().setAllowFileAccessFromFileURLs(true);
127     m_dispatchTask = std::make_unique<InspectorBackendDispatchTask>(inspectorController);
128 }
129
130 InspectorFrontendClientLocal::~InspectorFrontendClientLocal()
131 {
132     if (m_frontendHost)
133         m_frontendHost->disconnectClient();
134     m_frontendPage = 0;
135     m_inspectorController = 0;
136 }
137
138 void InspectorFrontendClientLocal::windowObjectCleared()
139 {
140     if (m_frontendHost)
141         m_frontendHost->disconnectClient();
142     
143     JSC::ExecState* frontendExecState = execStateFromPage(debuggerWorld(), m_frontendPage);
144     m_frontendHost = InspectorFrontendHost::create(this, m_frontendPage);
145     ScriptGlobalObject::set(frontendExecState, "InspectorFrontendHost", m_frontendHost.get());
146 }
147
148 void InspectorFrontendClientLocal::frontendLoaded()
149 {
150     // Call setDockingUnavailable before bringToFront. If we display the inspector window via bringToFront first it causes
151     // the call to canAttachWindow to return the wrong result on Windows.
152     // Calling bringToFront first causes the visibleHeight of the inspected page to always return 0 immediately after. 
153     // Thus if we call canAttachWindow first we can avoid this problem. This change does not cause any regressions on Mac.
154     setDockingUnavailable(!canAttachWindow());
155     bringToFront();
156     m_frontendLoaded = true;
157     for (Vector<String>::iterator it = m_evaluateOnLoad.begin(); it != m_evaluateOnLoad.end(); ++it)
158         evaluateOnLoad(*it);
159     m_evaluateOnLoad.clear();
160 }
161
162 void InspectorFrontendClientLocal::requestSetDockSide(DockSide dockSide)
163 {
164     if (dockSide == UNDOCKED) {
165         detachWindow();
166         setAttachedWindow(dockSide);
167     } else if (canAttachWindow()) {
168         attachWindow(dockSide);
169         setAttachedWindow(dockSide);
170     }
171 }
172
173 bool InspectorFrontendClientLocal::canAttachWindow()
174 {
175     // Don't allow attaching to another inspector -- two inspectors in one window is too much!
176     bool isInspectorPage = m_inspectorController->hasInspectorFrontendClient();
177     if (isInspectorPage)
178         return false;
179
180     // If we are already attached, allow attaching again to allow switching sides.
181     if (m_dockSide != UNDOCKED)
182         return true;
183
184     // Don't allow the attach if the window would be too small to accommodate the minimum inspector size.
185     unsigned inspectedPageHeight = m_inspectorController->inspectedPage().mainFrame().view()->visibleHeight();
186     unsigned inspectedPageWidth = m_inspectorController->inspectedPage().mainFrame().view()->visibleWidth();
187     unsigned maximumAttachedHeight = inspectedPageHeight * maximumAttachedHeightRatio;
188     return minimumAttachedHeight <= maximumAttachedHeight && minimumAttachedWidth <= inspectedPageWidth;
189 }
190
191 void InspectorFrontendClientLocal::setDockingUnavailable(bool unavailable)
192 {
193     evaluateOnLoad(String::format("[\"setDockingUnavailable\", %s]", unavailable ? "true" : "false"));
194 }
195
196 void InspectorFrontendClientLocal::changeAttachedWindowHeight(unsigned height)
197 {
198     unsigned totalHeight = m_frontendPage->mainFrame().view()->visibleHeight() + m_inspectorController->inspectedPage().mainFrame().view()->visibleHeight();
199     unsigned attachedHeight = constrainedAttachedWindowHeight(height, totalHeight);
200     m_settings->setProperty(inspectorAttachedHeightSetting, String::number(attachedHeight));
201     setAttachedWindowHeight(attachedHeight);
202 }
203
204 void InspectorFrontendClientLocal::changeAttachedWindowWidth(unsigned width)
205 {
206     unsigned totalWidth = m_frontendPage->mainFrame().view()->visibleWidth() + m_inspectorController->inspectedPage().mainFrame().view()->visibleWidth();
207     unsigned attachedWidth = constrainedAttachedWindowWidth(width, totalWidth);
208     setAttachedWindowWidth(attachedWidth);
209 }
210
211 void InspectorFrontendClientLocal::openInNewTab(const String& url)
212 {
213     UserGestureIndicator indicator(DefinitelyProcessingUserGesture);
214     Frame& mainFrame = m_inspectorController->inspectedPage().mainFrame();
215     FrameLoadRequest request(mainFrame.document()->securityOrigin(), ResourceRequest(), "_blank");
216
217     bool created;
218     WindowFeatures windowFeatures;
219     RefPtr<Frame> frame = WebCore::createWindow(&mainFrame, &mainFrame, request, windowFeatures, created);
220     if (!frame)
221         return;
222
223     frame->loader().setOpener(&mainFrame);
224     frame->page()->setOpenedByDOM();
225
226     // FIXME: Why does one use mainFrame and the other frame?
227     frame->loader().changeLocation(mainFrame.document()->securityOrigin(), frame->document()->completeURL(url), "", false, false);
228 }
229
230 void InspectorFrontendClientLocal::moveWindowBy(float x, float y)
231 {
232     FloatRect frameRect = m_frontendPage->chrome().windowRect();
233     frameRect.move(x, y);
234     m_frontendPage->chrome().setWindowRect(frameRect);
235 }
236
237 void InspectorFrontendClientLocal::setAttachedWindow(DockSide dockSide)
238 {
239     const char* side = "undocked";
240     switch (dockSide) {
241     case UNDOCKED:
242         side = "undocked";
243         break;
244     case DOCKED_TO_RIGHT:
245         side = "right";
246         break;
247     case DOCKED_TO_BOTTOM:
248         side = "bottom";
249         break;
250     }
251
252     m_dockSide = dockSide;
253
254     evaluateOnLoad(String::format("[\"setDockSide\", \"%s\"]", side));
255 }
256
257 void InspectorFrontendClientLocal::restoreAttachedWindowHeight()
258 {
259     unsigned inspectedPageHeight = m_inspectorController->inspectedPage().mainFrame().view()->visibleHeight();
260     String value = m_settings->getProperty(inspectorAttachedHeightSetting);
261     unsigned preferredHeight = value.isEmpty() ? defaultAttachedHeight : value.toUInt();
262     
263     // This call might not go through (if the window starts out detached), but if the window is initially created attached,
264     // InspectorController::attachWindow is never called, so we need to make sure to set the attachedWindowHeight.
265     // FIXME: Clean up code so we only have to call setAttachedWindowHeight in InspectorController::attachWindow
266     setAttachedWindowHeight(constrainedAttachedWindowHeight(preferredHeight, inspectedPageHeight));
267 }
268
269 bool InspectorFrontendClientLocal::isDebuggingEnabled()
270 {
271     if (m_frontendLoaded)
272         return evaluateAsBoolean("[\"isDebuggingEnabled\"]");
273     return false;
274 }
275
276 void InspectorFrontendClientLocal::setDebuggingEnabled(bool enabled)
277 {
278     evaluateOnLoad(String::format("[\"setDebuggingEnabled\", %s]", enabled ? "true" : "false"));
279 }
280
281 bool InspectorFrontendClientLocal::isTimelineProfilingEnabled()
282 {
283     if (m_frontendLoaded)
284         return evaluateAsBoolean("[\"isTimelineProfilingEnabled\"]");
285     return false;
286 }
287
288 void InspectorFrontendClientLocal::setTimelineProfilingEnabled(bool enabled)
289 {
290     evaluateOnLoad(String::format("[\"setTimelineProfilingEnabled\", %s]", enabled ? "true" : "false"));
291 }
292
293 bool InspectorFrontendClientLocal::isProfilingJavaScript()
294 {
295     if (m_frontendLoaded)
296         return evaluateAsBoolean("[\"isProfilingJavaScript\"]");
297     return false;
298 }
299
300 void InspectorFrontendClientLocal::startProfilingJavaScript()
301 {
302     evaluateOnLoad("[\"startProfilingJavaScript\"]");
303 }
304
305 void InspectorFrontendClientLocal::stopProfilingJavaScript()
306 {
307     evaluateOnLoad("[\"stopProfilingJavaScript\"]");
308 }
309
310 void InspectorFrontendClientLocal::showConsole()
311 {
312     evaluateOnLoad("[\"showConsole\"]");
313 }
314
315 void InspectorFrontendClientLocal::showResources()
316 {
317     evaluateOnLoad("[\"showResources\"]");
318 }
319
320 void InspectorFrontendClientLocal::showMainResourceForFrame(Frame* frame)
321 {
322     String frameId = m_inspectorController->pageAgent()->frameId(frame);
323     evaluateOnLoad(String::format("[\"showMainResourceForFrame\", \"%s\"]", frameId.ascii().data()));
324 }
325
326 unsigned InspectorFrontendClientLocal::constrainedAttachedWindowHeight(unsigned preferredHeight, unsigned totalWindowHeight)
327 {
328     return roundf(std::max(minimumAttachedHeight, std::min<float>(preferredHeight, totalWindowHeight * maximumAttachedHeightRatio)));
329 }
330
331 unsigned InspectorFrontendClientLocal::constrainedAttachedWindowWidth(unsigned preferredWidth, unsigned totalWindowWidth)
332 {
333     return roundf(std::max(minimumAttachedWidth, std::min<float>(preferredWidth, totalWindowWidth - minimumAttachedInspectedWidth)));
334 }
335
336 void InspectorFrontendClientLocal::sendMessageToBackend(const String& message)
337 {
338     m_dispatchTask->dispatch(message);
339 }
340
341 bool InspectorFrontendClientLocal::isUnderTest()
342 {
343     return m_inspectorController->isUnderTest();
344 }
345
346 bool InspectorFrontendClientLocal::evaluateAsBoolean(const String& expression)
347 {
348     Deprecated::ScriptValue value = m_frontendPage->mainFrame().script().executeScript(expression);
349     return value.toString(mainWorldExecState(&m_frontendPage->mainFrame())) == "true";
350 }
351
352 void InspectorFrontendClientLocal::evaluateOnLoad(const String& expression)
353 {
354     if (m_frontendLoaded)
355         m_frontendPage->mainFrame().script().executeScript("InspectorFrontendAPI.dispatch(" + expression + ")");
356     else
357         m_evaluateOnLoad.append(expression);
358 }
359
360 } // namespace WebCore
361
362 #endif