[Cleanup] Remove ScriptGlobalObject
[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 "ScriptState.h"
49 #include "Settings.h"
50 #include "Timer.h"
51 #include "UserGestureIndicator.h"
52 #include "WindowFeatures.h"
53 #include <inspector/InspectorBackendDispatchers.h>
54 #include <wtf/Deque.h>
55 #include <wtf/text/CString.h>
56
57 using namespace Inspector;
58
59 namespace WebCore {
60
61 static const char* inspectorAttachedHeightSetting = "inspectorAttachedHeight";
62 static const unsigned defaultAttachedHeight = 300;
63 static const float minimumAttachedHeight = 250.0f;
64 static const float maximumAttachedHeightRatio = 0.75f;
65 static const float minimumAttachedWidth = 500.0f;
66 static const float minimumAttachedInspectedWidth = 320.0f;
67
68 class InspectorBackendDispatchTask : public RefCounted<InspectorBackendDispatchTask> {
69     WTF_MAKE_FAST_ALLOCATED;
70 public:
71     static Ref<InspectorBackendDispatchTask> create(InspectorController* inspectedPageController)
72     {
73         return adoptRef(*new InspectorBackendDispatchTask(inspectedPageController));
74     }
75
76     void dispatch(const String& message)
77     {
78         ASSERT_ARG(message, !message.isEmpty());
79
80         m_messages.append(message);
81         if (!m_timer.isActive())
82             m_timer.startOneShot(0_s);
83     }
84
85     void reset()
86     {
87         m_messages.clear();
88         m_timer.stop();
89         m_inspectedPageController = nullptr;
90     }
91
92     void timerFired()
93     {
94         ASSERT(m_inspectedPageController);
95
96         // Dispatching a message can possibly close the frontend and destroy
97         // the owning frontend client, so keep a protector reference here.
98         Ref<InspectorBackendDispatchTask> protectedThis(*this);
99
100         if (!m_messages.isEmpty())
101             m_inspectedPageController->dispatchMessageFromFrontend(m_messages.takeFirst());
102
103         if (!m_messages.isEmpty() && m_inspectedPageController)
104             m_timer.startOneShot(0_s);
105     }
106
107 private:
108     InspectorBackendDispatchTask(InspectorController* inspectedPageController)
109         : m_inspectedPageController(inspectedPageController)
110         , m_timer(*this, &InspectorBackendDispatchTask::timerFired)
111     {
112         ASSERT_ARG(inspectedPageController, inspectedPageController);
113     }
114
115     InspectorController* m_inspectedPageController { nullptr };
116     Timer m_timer;
117     Deque<String> m_messages;
118 };
119
120 String InspectorFrontendClientLocal::Settings::getProperty(const String&)
121 {
122     return String();
123 }
124
125 void InspectorFrontendClientLocal::Settings::setProperty(const String&, const String&)
126 {
127 }
128
129 InspectorFrontendClientLocal::InspectorFrontendClientLocal(InspectorController* inspectedPageController, Page* frontendPage, std::unique_ptr<Settings> settings)
130     : m_inspectedPageController(inspectedPageController)
131     , m_frontendPage(frontendPage)
132     , m_settings(WTFMove(settings))
133     , m_dockSide(DockSide::Undocked)
134     , m_dispatchTask(InspectorBackendDispatchTask::create(inspectedPageController))
135 {
136     m_frontendPage->settings().setAllowFileAccessFromFileURLs(true);
137 }
138
139 InspectorFrontendClientLocal::~InspectorFrontendClientLocal()
140 {
141     if (m_frontendHost)
142         m_frontendHost->disconnectClient();
143     m_frontendPage = nullptr;
144     m_inspectedPageController = nullptr;
145     m_dispatchTask->reset();
146 }
147
148 void InspectorFrontendClientLocal::windowObjectCleared()
149 {
150     if (m_frontendHost)
151         m_frontendHost->disconnectClient();
152     
153     m_frontendHost = InspectorFrontendHost::create(this, m_frontendPage);
154     m_frontendHost->addSelfToGlobalObjectInWorld(debuggerWorld());
155 }
156
157 void InspectorFrontendClientLocal::frontendLoaded()
158 {
159     // Call setDockingUnavailable before bringToFront. If we display the inspector window via bringToFront first it causes
160     // the call to canAttachWindow to return the wrong result on Windows.
161     // Calling bringToFront first causes the visibleHeight of the inspected page to always return 0 immediately after.
162     // Thus if we call canAttachWindow first we can avoid this problem. This change does not cause any regressions on Mac.
163     setDockingUnavailable(!canAttachWindow());
164     bringToFront();
165     m_frontendLoaded = true;
166     for (auto& evaluate : m_evaluateOnLoad)
167         evaluateOnLoad(evaluate);
168     m_evaluateOnLoad.clear();
169 }
170
171 UserInterfaceLayoutDirection InspectorFrontendClientLocal::userInterfaceLayoutDirection() const
172 {
173     return m_frontendPage->userInterfaceLayoutDirection();
174 }
175
176 void InspectorFrontendClientLocal::requestSetDockSide(DockSide dockSide)
177 {
178     if (dockSide == DockSide::Undocked) {
179         detachWindow();
180         setAttachedWindow(dockSide);
181     } else if (canAttachWindow()) {
182         attachWindow(dockSide);
183         setAttachedWindow(dockSide);
184     }
185 }
186
187 bool InspectorFrontendClientLocal::canAttachWindow()
188 {
189     // Don't allow attaching to another inspector -- two inspectors in one window is too much!
190     bool isInspectorPage = m_inspectedPageController->inspectionLevel() > 0;
191     if (isInspectorPage)
192         return false;
193
194     // If we are already attached, allow attaching again to allow switching sides.
195     if (m_dockSide != DockSide::Undocked)
196         return true;
197
198     // Don't allow the attach if the window would be too small to accommodate the minimum inspector size.
199     unsigned inspectedPageHeight = m_inspectedPageController->inspectedPage().mainFrame().view()->visibleHeight();
200     unsigned inspectedPageWidth = m_inspectedPageController->inspectedPage().mainFrame().view()->visibleWidth();
201     unsigned maximumAttachedHeight = inspectedPageHeight * maximumAttachedHeightRatio;
202     return minimumAttachedHeight <= maximumAttachedHeight && minimumAttachedWidth <= inspectedPageWidth;
203 }
204
205 void InspectorFrontendClientLocal::setDockingUnavailable(bool unavailable)
206 {
207     evaluateOnLoad(String::format("[\"setDockingUnavailable\", %s]", unavailable ? "true" : "false"));
208 }
209
210 void InspectorFrontendClientLocal::changeAttachedWindowHeight(unsigned height)
211 {
212     unsigned totalHeight = m_frontendPage->mainFrame().view()->visibleHeight() + m_inspectedPageController->inspectedPage().mainFrame().view()->visibleHeight();
213     unsigned attachedHeight = constrainedAttachedWindowHeight(height, totalHeight);
214     m_settings->setProperty(inspectorAttachedHeightSetting, String::number(attachedHeight));
215     setAttachedWindowHeight(attachedHeight);
216 }
217
218 void InspectorFrontendClientLocal::changeAttachedWindowWidth(unsigned width)
219 {
220     unsigned totalWidth = m_frontendPage->mainFrame().view()->visibleWidth() + m_inspectedPageController->inspectedPage().mainFrame().view()->visibleWidth();
221     unsigned attachedWidth = constrainedAttachedWindowWidth(width, totalWidth);
222     setAttachedWindowWidth(attachedWidth);
223 }
224
225 void InspectorFrontendClientLocal::openInNewTab(const String& url)
226 {
227     UserGestureIndicator indicator { ProcessingUserGesture };
228     Frame& mainFrame = m_inspectedPageController->inspectedPage().mainFrame();
229     FrameLoadRequest frameLoadRequest { *mainFrame.document(), mainFrame.document()->securityOrigin(), { }, ASCIILiteral("_blank"), LockHistory::No, LockBackForwardList::No, MaybeSendReferrer, AllowNavigationToInvalidURL::Yes, NewFrameOpenerPolicy::Allow, ShouldOpenExternalURLsPolicy::ShouldNotAllow, InitiatedByMainFrame::Unknown };
230
231     bool created;
232     RefPtr<Frame> frame = WebCore::createWindow(mainFrame, mainFrame, WTFMove(frameLoadRequest), { }, created);
233     if (!frame)
234         return;
235
236     frame->loader().setOpener(&mainFrame);
237     frame->page()->setOpenedByDOM();
238
239     // FIXME: Why do we compute the absolute URL with respect to |frame| instead of |mainFrame|?
240     ResourceRequest resourceRequest { frame->document()->completeURL(url) };
241     FrameLoadRequest frameLoadRequest2 { *mainFrame.document(), mainFrame.document()->securityOrigin(), resourceRequest, ASCIILiteral("_self"), LockHistory::No, LockBackForwardList::No, MaybeSendReferrer, AllowNavigationToInvalidURL::Yes, NewFrameOpenerPolicy::Allow, ShouldOpenExternalURLsPolicy::ShouldNotAllow, InitiatedByMainFrame::Unknown };
242     frame->loader().changeLocation(WTFMove(frameLoadRequest2));
243 }
244
245 void InspectorFrontendClientLocal::moveWindowBy(float x, float y)
246 {
247     FloatRect frameRect = m_frontendPage->chrome().windowRect();
248     frameRect.move(x, y);
249     m_frontendPage->chrome().setWindowRect(frameRect);
250 }
251
252 void InspectorFrontendClientLocal::setAttachedWindow(DockSide dockSide)
253 {
254     const char* side = "undocked";
255     switch (dockSide) {
256     case DockSide::Undocked:
257         side = "undocked";
258         break;
259     case DockSide::Right:
260         side = "right";
261         break;
262     case DockSide::Left:
263         side = "left";
264         break;
265     case DockSide::Bottom:
266         side = "bottom";
267         break;
268     }
269
270     m_dockSide = dockSide;
271
272     evaluateOnLoad(String::format("[\"setDockSide\", \"%s\"]", side));
273 }
274
275 void InspectorFrontendClientLocal::restoreAttachedWindowHeight()
276 {
277     unsigned inspectedPageHeight = m_inspectedPageController->inspectedPage().mainFrame().view()->visibleHeight();
278     String value = m_settings->getProperty(inspectorAttachedHeightSetting);
279     unsigned preferredHeight = value.isEmpty() ? defaultAttachedHeight : value.toUInt();
280     
281     // This call might not go through (if the window starts out detached), but if the window is initially created attached,
282     // InspectorController::attachWindow is never called, so we need to make sure to set the attachedWindowHeight.
283     // FIXME: Clean up code so we only have to call setAttachedWindowHeight in InspectorController::attachWindow
284     setAttachedWindowHeight(constrainedAttachedWindowHeight(preferredHeight, inspectedPageHeight));
285 }
286
287 bool InspectorFrontendClientLocal::isDebuggingEnabled()
288 {
289     if (m_frontendLoaded)
290         return evaluateAsBoolean("[\"isDebuggingEnabled\"]");
291     return false;
292 }
293
294 void InspectorFrontendClientLocal::setDebuggingEnabled(bool enabled)
295 {
296     evaluateOnLoad(String::format("[\"setDebuggingEnabled\", %s]", enabled ? "true" : "false"));
297 }
298
299 bool InspectorFrontendClientLocal::isTimelineProfilingEnabled()
300 {
301     if (m_frontendLoaded)
302         return evaluateAsBoolean("[\"isTimelineProfilingEnabled\"]");
303     return false;
304 }
305
306 void InspectorFrontendClientLocal::setTimelineProfilingEnabled(bool enabled)
307 {
308     evaluateOnLoad(String::format("[\"setTimelineProfilingEnabled\", %s]", enabled ? "true" : "false"));
309 }
310
311 bool InspectorFrontendClientLocal::isProfilingJavaScript()
312 {
313     if (m_frontendLoaded)
314         return evaluateAsBoolean("[\"isProfilingJavaScript\"]");
315     return false;
316 }
317
318 void InspectorFrontendClientLocal::startProfilingJavaScript()
319 {
320     evaluateOnLoad("[\"startProfilingJavaScript\"]");
321 }
322
323 void InspectorFrontendClientLocal::stopProfilingJavaScript()
324 {
325     evaluateOnLoad("[\"stopProfilingJavaScript\"]");
326 }
327
328 void InspectorFrontendClientLocal::showConsole()
329 {
330     evaluateOnLoad("[\"showConsole\"]");
331 }
332
333 void InspectorFrontendClientLocal::showResources()
334 {
335     evaluateOnLoad("[\"showResources\"]");
336 }
337
338 void InspectorFrontendClientLocal::showMainResourceForFrame(Frame* frame)
339 {
340     String frameId = m_inspectedPageController->pageAgent()->frameId(frame);
341     evaluateOnLoad(String::format("[\"showMainResourceForFrame\", \"%s\"]", frameId.ascii().data()));
342 }
343
344 unsigned InspectorFrontendClientLocal::constrainedAttachedWindowHeight(unsigned preferredHeight, unsigned totalWindowHeight)
345 {
346     return roundf(std::max(minimumAttachedHeight, std::min<float>(preferredHeight, totalWindowHeight * maximumAttachedHeightRatio)));
347 }
348
349 unsigned InspectorFrontendClientLocal::constrainedAttachedWindowWidth(unsigned preferredWidth, unsigned totalWindowWidth)
350 {
351     return roundf(std::max(minimumAttachedWidth, std::min<float>(preferredWidth, totalWindowWidth - minimumAttachedInspectedWidth)));
352 }
353
354 void InspectorFrontendClientLocal::sendMessageToBackend(const String& message)
355 {
356     m_dispatchTask->dispatch(message);
357 }
358
359 bool InspectorFrontendClientLocal::isUnderTest()
360 {
361     return m_inspectedPageController->isUnderTest();
362 }
363
364 unsigned InspectorFrontendClientLocal::inspectionLevel() const
365 {
366     return m_inspectedPageController->inspectionLevel() + 1;
367 }
368
369 bool InspectorFrontendClientLocal::evaluateAsBoolean(const String& expression)
370 {
371     auto& state = *mainWorldExecState(&m_frontendPage->mainFrame());
372     return m_frontendPage->mainFrame().script().executeScript(expression).toWTFString(&state) == "true";
373 }
374
375 void InspectorFrontendClientLocal::evaluateOnLoad(const String& expression)
376 {
377     if (m_frontendLoaded)
378         m_frontendPage->mainFrame().script().executeScript("if (InspectorFrontendAPI) InspectorFrontendAPI.dispatch(" + expression + ")");
379     else
380         m_evaluateOnLoad.append(expression);
381 }
382
383 Page* InspectorFrontendClientLocal::inspectedPage() const
384 {
385     if (!m_inspectedPageController)
386         return nullptr;
387
388     return &m_inspectedPageController->inspectedPage();
389 }
390
391 } // namespace WebCore