Replace WTF::move with WTFMove
[WebKit-https.git] / Source / WebCore / inspector / InspectorFrontendClientLocal.cpp
1 /*
2  * Copyright (C) 2010 Google Inc. All rights reserved.
3  * Copyright (C) 2015 Apple Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following disclaimer
13  * in the documentation and/or other materials provided with the
14  * distribution.
15  *     * Neither the name of Google Inc. nor the names of its
16  * contributors may be used to endorse or promote products derived from
17  * this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 #include "config.h"
33 #include "InspectorFrontendClientLocal.h"
34
35 #include "Chrome.h"
36 #include "DOMWrapperWorld.h"
37 #include "Document.h"
38 #include "FloatRect.h"
39 #include "FrameLoadRequest.h"
40 #include "FrameLoader.h"
41 #include "FrameView.h"
42 #include "InspectorController.h"
43 #include "InspectorFrontendHost.h"
44 #include "InspectorPageAgent.h"
45 #include "MainFrame.h"
46 #include "Page.h"
47 #include "ScriptController.h"
48 #include "ScriptGlobalObject.h"
49 #include "ScriptState.h"
50 #include "Settings.h"
51 #include "Timer.h"
52 #include "UserGestureIndicator.h"
53 #include "WindowFeatures.h"
54 #include <bindings/ScriptValue.h>
55 #include <inspector/InspectorBackendDispatchers.h>
56 #include <wtf/Deque.h>
57 #include <wtf/text/CString.h>
58
59 using namespace Inspector;
60
61 namespace WebCore {
62
63 static const char* inspectorAttachedHeightSetting = "inspectorAttachedHeight";
64 static const unsigned defaultAttachedHeight = 300;
65 static const float minimumAttachedHeight = 250.0f;
66 static const float maximumAttachedHeightRatio = 0.75f;
67 static const float minimumAttachedWidth = 500.0f;
68 static const float minimumAttachedInspectedWidth = 320.0f;
69
70 class InspectorBackendDispatchTask : public RefCounted<InspectorBackendDispatchTask> {
71     WTF_MAKE_FAST_ALLOCATED;
72 public:
73     static Ref<InspectorBackendDispatchTask> create(InspectorController* inspectedPageController)
74     {
75         return adoptRef(*new InspectorBackendDispatchTask(inspectedPageController));
76     }
77
78     void dispatch(const String& message)
79     {
80         ASSERT_ARG(message, !message.isEmpty());
81
82         m_messages.append(message);
83         if (!m_timer.isActive())
84             m_timer.startOneShot(0);
85     }
86
87     void reset()
88     {
89         m_messages.clear();
90         m_timer.stop();
91         m_inspectedPageController = nullptr;
92     }
93
94     void timerFired()
95     {
96         ASSERT(m_inspectedPageController);
97
98         // Dispatching a message can possibly close the frontend and destroy
99         // the owning frontend client, so keep a protector reference here.
100         Ref<InspectorBackendDispatchTask> protect(*this);
101
102         if (!m_messages.isEmpty())
103             m_inspectedPageController->dispatchMessageFromFrontend(m_messages.takeFirst());
104
105         if (!m_messages.isEmpty() && m_inspectedPageController)
106             m_timer.startOneShot(0);
107     }
108
109 private:
110     InspectorBackendDispatchTask(InspectorController* inspectedPageController)
111         : m_inspectedPageController(inspectedPageController)
112         , m_timer(*this, &InspectorBackendDispatchTask::timerFired)
113     {
114         ASSERT_ARG(inspectedPageController, inspectedPageController);
115     }
116
117     InspectorController* m_inspectedPageController { nullptr };
118     Timer m_timer;
119     Deque<String> m_messages;
120 };
121
122 String InspectorFrontendClientLocal::Settings::getProperty(const String&)
123 {
124     return String();
125 }
126
127 void InspectorFrontendClientLocal::Settings::setProperty(const String&, const String&)
128 {
129 }
130
131 InspectorFrontendClientLocal::InspectorFrontendClientLocal(InspectorController* inspectedPageController, Page* frontendPage, std::unique_ptr<Settings> settings)
132     : m_inspectedPageController(inspectedPageController)
133     , m_frontendPage(frontendPage)
134     , m_settings(WTFMove(settings))
135     , m_dockSide(DockSide::Undocked)
136     , m_dispatchTask(InspectorBackendDispatchTask::create(inspectedPageController))
137 {
138     m_frontendPage->settings().setAllowFileAccessFromFileURLs(true);
139 }
140
141 InspectorFrontendClientLocal::~InspectorFrontendClientLocal()
142 {
143     if (m_frontendHost)
144         m_frontendHost->disconnectClient();
145     m_frontendPage = nullptr;
146     m_inspectedPageController = nullptr;
147     m_dispatchTask->reset();
148 }
149
150 void InspectorFrontendClientLocal::windowObjectCleared()
151 {
152     if (m_frontendHost)
153         m_frontendHost->disconnectClient();
154     
155     JSC::ExecState* frontendExecState = execStateFromPage(debuggerWorld(), m_frontendPage);
156     m_frontendHost = InspectorFrontendHost::create(this, m_frontendPage);
157     ScriptGlobalObject::set(frontendExecState, "InspectorFrontendHost", m_frontendHost.get());
158 }
159
160 void InspectorFrontendClientLocal::frontendLoaded()
161 {
162     // Call setDockingUnavailable before bringToFront. If we display the inspector window via bringToFront first it causes
163     // the call to canAttachWindow to return the wrong result on Windows.
164     // Calling bringToFront first causes the visibleHeight of the inspected page to always return 0 immediately after. 
165     // Thus if we call canAttachWindow first we can avoid this problem. This change does not cause any regressions on Mac.
166     setDockingUnavailable(!canAttachWindow());
167     bringToFront();
168     m_frontendLoaded = true;
169     for (auto& evaluate : m_evaluateOnLoad)
170         evaluateOnLoad(evaluate);
171     m_evaluateOnLoad.clear();
172 }
173
174 void InspectorFrontendClientLocal::requestSetDockSide(DockSide dockSide)
175 {
176     if (dockSide == DockSide::Undocked) {
177         detachWindow();
178         setAttachedWindow(dockSide);
179     } else if (canAttachWindow()) {
180         attachWindow(dockSide);
181         setAttachedWindow(dockSide);
182     }
183 }
184
185 bool InspectorFrontendClientLocal::canAttachWindow()
186 {
187     // Don't allow attaching to another inspector -- two inspectors in one window is too much!
188     bool isInspectorPage = m_inspectedPageController->inspectionLevel() > 0;
189     if (isInspectorPage)
190         return false;
191
192     // If we are already attached, allow attaching again to allow switching sides.
193     if (m_dockSide != DockSide::Undocked)
194         return true;
195
196     // Don't allow the attach if the window would be too small to accommodate the minimum inspector size.
197     unsigned inspectedPageHeight = m_inspectedPageController->inspectedPage().mainFrame().view()->visibleHeight();
198     unsigned inspectedPageWidth = m_inspectedPageController->inspectedPage().mainFrame().view()->visibleWidth();
199     unsigned maximumAttachedHeight = inspectedPageHeight * maximumAttachedHeightRatio;
200     return minimumAttachedHeight <= maximumAttachedHeight && minimumAttachedWidth <= inspectedPageWidth;
201 }
202
203 void InspectorFrontendClientLocal::setDockingUnavailable(bool unavailable)
204 {
205     evaluateOnLoad(String::format("[\"setDockingUnavailable\", %s]", unavailable ? "true" : "false"));
206 }
207
208 void InspectorFrontendClientLocal::changeAttachedWindowHeight(unsigned height)
209 {
210     unsigned totalHeight = m_frontendPage->mainFrame().view()->visibleHeight() + m_inspectedPageController->inspectedPage().mainFrame().view()->visibleHeight();
211     unsigned attachedHeight = constrainedAttachedWindowHeight(height, totalHeight);
212     m_settings->setProperty(inspectorAttachedHeightSetting, String::number(attachedHeight));
213     setAttachedWindowHeight(attachedHeight);
214 }
215
216 void InspectorFrontendClientLocal::changeAttachedWindowWidth(unsigned width)
217 {
218     unsigned totalWidth = m_frontendPage->mainFrame().view()->visibleWidth() + m_inspectedPageController->inspectedPage().mainFrame().view()->visibleWidth();
219     unsigned attachedWidth = constrainedAttachedWindowWidth(width, totalWidth);
220     setAttachedWindowWidth(attachedWidth);
221 }
222
223 void InspectorFrontendClientLocal::openInNewTab(const String& url)
224 {
225     UserGestureIndicator indicator(DefinitelyProcessingUserGesture);
226     Frame& mainFrame = m_inspectedPageController->inspectedPage().mainFrame();
227     FrameLoadRequest request(mainFrame.document()->securityOrigin(), ResourceRequest(), "_blank", LockHistory::No, LockBackForwardList::No, MaybeSendReferrer, AllowNavigationToInvalidURL::Yes, NewFrameOpenerPolicy::Allow, ReplaceDocumentIfJavaScriptURL, ShouldOpenExternalURLsPolicy::ShouldNotAllow);
228
229     bool created;
230     WindowFeatures windowFeatures;
231     RefPtr<Frame> frame = WebCore::createWindow(mainFrame, mainFrame, request, windowFeatures, created);
232     if (!frame)
233         return;
234
235     frame->loader().setOpener(&mainFrame);
236     frame->page()->setOpenedByDOM();
237
238     // FIXME: Why does one use mainFrame and the other frame?
239     ResourceRequest resourceRequest(frame->document()->completeURL(url));
240     FrameLoadRequest frameRequest(mainFrame.document()->securityOrigin(), resourceRequest, "_self", LockHistory::No, LockBackForwardList::No, MaybeSendReferrer, AllowNavigationToInvalidURL::Yes, NewFrameOpenerPolicy::Allow, ReplaceDocumentIfJavaScriptURL, ShouldOpenExternalURLsPolicy::ShouldNotAllow);
241     frame->loader().changeLocation(frameRequest);
242 }
243
244 void InspectorFrontendClientLocal::moveWindowBy(float x, float y)
245 {
246     FloatRect frameRect = m_frontendPage->chrome().windowRect();
247     frameRect.move(x, y);
248     m_frontendPage->chrome().setWindowRect(frameRect);
249 }
250
251 void InspectorFrontendClientLocal::setAttachedWindow(DockSide dockSide)
252 {
253     const char* side = "undocked";
254     switch (dockSide) {
255     case DockSide::Undocked:
256         side = "undocked";
257         break;
258     case DockSide::Right:
259         side = "right";
260         break;
261     case DockSide::Bottom:
262         side = "bottom";
263         break;
264     }
265
266     m_dockSide = dockSide;
267
268     evaluateOnLoad(String::format("[\"setDockSide\", \"%s\"]", side));
269 }
270
271 void InspectorFrontendClientLocal::restoreAttachedWindowHeight()
272 {
273     unsigned inspectedPageHeight = m_inspectedPageController->inspectedPage().mainFrame().view()->visibleHeight();
274     String value = m_settings->getProperty(inspectorAttachedHeightSetting);
275     unsigned preferredHeight = value.isEmpty() ? defaultAttachedHeight : value.toUInt();
276     
277     // This call might not go through (if the window starts out detached), but if the window is initially created attached,
278     // InspectorController::attachWindow is never called, so we need to make sure to set the attachedWindowHeight.
279     // FIXME: Clean up code so we only have to call setAttachedWindowHeight in InspectorController::attachWindow
280     setAttachedWindowHeight(constrainedAttachedWindowHeight(preferredHeight, inspectedPageHeight));
281 }
282
283 bool InspectorFrontendClientLocal::isDebuggingEnabled()
284 {
285     if (m_frontendLoaded)
286         return evaluateAsBoolean("[\"isDebuggingEnabled\"]");
287     return false;
288 }
289
290 void InspectorFrontendClientLocal::setDebuggingEnabled(bool enabled)
291 {
292     evaluateOnLoad(String::format("[\"setDebuggingEnabled\", %s]", enabled ? "true" : "false"));
293 }
294
295 bool InspectorFrontendClientLocal::isTimelineProfilingEnabled()
296 {
297     if (m_frontendLoaded)
298         return evaluateAsBoolean("[\"isTimelineProfilingEnabled\"]");
299     return false;
300 }
301
302 void InspectorFrontendClientLocal::setTimelineProfilingEnabled(bool enabled)
303 {
304     evaluateOnLoad(String::format("[\"setTimelineProfilingEnabled\", %s]", enabled ? "true" : "false"));
305 }
306
307 bool InspectorFrontendClientLocal::isProfilingJavaScript()
308 {
309     if (m_frontendLoaded)
310         return evaluateAsBoolean("[\"isProfilingJavaScript\"]");
311     return false;
312 }
313
314 void InspectorFrontendClientLocal::startProfilingJavaScript()
315 {
316     evaluateOnLoad("[\"startProfilingJavaScript\"]");
317 }
318
319 void InspectorFrontendClientLocal::stopProfilingJavaScript()
320 {
321     evaluateOnLoad("[\"stopProfilingJavaScript\"]");
322 }
323
324 void InspectorFrontendClientLocal::showConsole()
325 {
326     evaluateOnLoad("[\"showConsole\"]");
327 }
328
329 void InspectorFrontendClientLocal::showResources()
330 {
331     evaluateOnLoad("[\"showResources\"]");
332 }
333
334 void InspectorFrontendClientLocal::showMainResourceForFrame(Frame* frame)
335 {
336     String frameId = m_inspectedPageController->pageAgent()->frameId(frame);
337     evaluateOnLoad(String::format("[\"showMainResourceForFrame\", \"%s\"]", frameId.ascii().data()));
338 }
339
340 unsigned InspectorFrontendClientLocal::constrainedAttachedWindowHeight(unsigned preferredHeight, unsigned totalWindowHeight)
341 {
342     return roundf(std::max(minimumAttachedHeight, std::min<float>(preferredHeight, totalWindowHeight * maximumAttachedHeightRatio)));
343 }
344
345 unsigned InspectorFrontendClientLocal::constrainedAttachedWindowWidth(unsigned preferredWidth, unsigned totalWindowWidth)
346 {
347     return roundf(std::max(minimumAttachedWidth, std::min<float>(preferredWidth, totalWindowWidth - minimumAttachedInspectedWidth)));
348 }
349
350 void InspectorFrontendClientLocal::sendMessageToBackend(const String& message)
351 {
352     m_dispatchTask->dispatch(message);
353 }
354
355 bool InspectorFrontendClientLocal::isUnderTest()
356 {
357     return m_inspectedPageController->isUnderTest();
358 }
359
360 unsigned InspectorFrontendClientLocal::inspectionLevel() const
361 {
362     return m_inspectedPageController->inspectionLevel() + 1;
363 }
364
365 bool InspectorFrontendClientLocal::evaluateAsBoolean(const String& expression)
366 {
367     Deprecated::ScriptValue value = m_frontendPage->mainFrame().script().executeScript(expression);
368     return value.toString(mainWorldExecState(&m_frontendPage->mainFrame())) == "true";
369 }
370
371 void InspectorFrontendClientLocal::evaluateOnLoad(const String& expression)
372 {
373     if (m_frontendLoaded)
374         m_frontendPage->mainFrame().script().executeScript("if (InspectorFrontendAPI) InspectorFrontendAPI.dispatch(" + expression + ")");
375     else
376         m_evaluateOnLoad.append(expression);
377 }
378
379 Page* InspectorFrontendClientLocal::inspectedPage() const
380 {
381     if (!m_inspectedPageController)
382         return nullptr;
383
384     return &m_inspectedPageController->inspectedPage();
385 }
386
387 } // namespace WebCore