Web Inspector: Cleanup Inspector classes be more consistent about using fast malloc...
[WebKit-https.git] / Source / WebCore / inspector / InspectorFrontendHost.cpp
1 /*
2  * Copyright (C) 2007-2017 Apple Inc. All rights reserved.
3  * Copyright (C) 2008 Matt Lilek <webkit@mattlilek.com>
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1.  Redistributions of source code must retain the above copyright
10  *     notice, this list of conditions and the following disclaimer.
11  * 2.  Redistributions in binary form must reproduce the above copyright
12  *     notice, this list of conditions and the following disclaimer in the
13  *     documentation and/or other materials provided with the distribution.
14  * 3.  Neither the name of Apple Inc. ("Apple") nor the names of
15  *     its contributors may be used to endorse or promote products derived
16  *     from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include "config.h"
31 #include "InspectorFrontendHost.h"
32
33 #include "ContextMenu.h"
34 #include "ContextMenuController.h"
35 #include "ContextMenuItem.h"
36 #include "ContextMenuProvider.h"
37 #include "DOMWrapperWorld.h"
38 #include "Document.h"
39 #include "Editor.h"
40 #include "Event.h"
41 #include "FocusController.h"
42 #include "HitTestResult.h"
43 #include "InspectorController.h"
44 #include "InspectorFrontendClient.h"
45 #include "JSDOMConvertInterface.h"
46 #include "JSDOMExceptionHandling.h"
47 #include "JSInspectorFrontendHost.h"
48 #include "JSMainThreadExecState.h"
49 #include "MainFrame.h"
50 #include "MouseEvent.h"
51 #include "Node.h"
52 #include "Page.h"
53 #include "Pasteboard.h"
54 #include "ScriptState.h"
55 #include "UserGestureIndicator.h"
56 #include <bindings/ScriptFunctionCall.h>
57 #include <pal/system/Sound.h>
58 #include <wtf/StdLibExtras.h>
59
60
61 namespace WebCore {
62
63 using namespace Inspector;
64
65 #if ENABLE(CONTEXT_MENUS)
66 class FrontendMenuProvider : public ContextMenuProvider {
67 public:
68     static Ref<FrontendMenuProvider> create(InspectorFrontendHost* frontendHost, Deprecated::ScriptObject frontendApiObject, const Vector<ContextMenuItem>& items)
69     {
70         return adoptRef(*new FrontendMenuProvider(frontendHost, frontendApiObject, items));
71     }
72     
73     void disconnect()
74     {
75         m_frontendApiObject = { };
76         m_frontendHost = nullptr;
77     }
78     
79 private:
80     FrontendMenuProvider(InspectorFrontendHost* frontendHost, Deprecated::ScriptObject frontendApiObject, const Vector<ContextMenuItem>& items)
81         : m_frontendHost(frontendHost)
82         , m_frontendApiObject(frontendApiObject)
83         , m_items(items)
84     {
85     }
86
87     virtual ~FrontendMenuProvider()
88     {
89         contextMenuCleared();
90     }
91     
92     void populateContextMenu(ContextMenu* menu) override
93     {
94         for (auto& item : m_items)
95             menu->appendItem(item);
96     }
97     
98     void contextMenuItemSelected(ContextMenuAction action, const String&) override
99     {
100         if (m_frontendHost) {
101             UserGestureIndicator gestureIndicator(ProcessingUserGesture);
102             int itemNumber = action - ContextMenuItemBaseCustomTag;
103
104             Deprecated::ScriptFunctionCall function(m_frontendApiObject, "contextMenuItemSelected", WebCore::functionCallHandlerFromAnyThread);
105             function.appendArgument(itemNumber);
106             function.call();
107         }
108     }
109     
110     void contextMenuCleared() override
111     {
112         if (m_frontendHost) {
113             Deprecated::ScriptFunctionCall function(m_frontendApiObject, "contextMenuCleared", WebCore::functionCallHandlerFromAnyThread);
114             function.call();
115
116             m_frontendHost->m_menuProvider = nullptr;
117         }
118         m_items.clear();
119     }
120
121     InspectorFrontendHost* m_frontendHost;
122     Deprecated::ScriptObject m_frontendApiObject;
123     Vector<ContextMenuItem> m_items;
124 };
125 #endif
126
127 InspectorFrontendHost::InspectorFrontendHost(InspectorFrontendClient* client, Page* frontendPage)
128     : m_client(client)
129     , m_frontendPage(frontendPage)
130 #if ENABLE(CONTEXT_MENUS)
131     , m_menuProvider(nullptr)
132 #endif
133 {
134 }
135
136 InspectorFrontendHost::~InspectorFrontendHost()
137 {
138     ASSERT(!m_client);
139 }
140
141 void InspectorFrontendHost::disconnectClient()
142 {
143     m_client = nullptr;
144 #if ENABLE(CONTEXT_MENUS)
145     if (m_menuProvider)
146         m_menuProvider->disconnect();
147 #endif
148     m_frontendPage = nullptr;
149 }
150
151 void InspectorFrontendHost::addSelfToGlobalObjectInWorld(DOMWrapperWorld& world)
152 {
153     auto& state = *execStateFromPage(world, m_frontendPage);
154     auto& vm = state.vm();
155     JSC::JSLockHolder lock(vm);
156     auto scope = DECLARE_CATCH_SCOPE(vm);
157
158     auto& globalObject = *JSC::jsCast<JSDOMGlobalObject*>(state.lexicalGlobalObject());
159     globalObject.putDirect(vm, JSC::Identifier::fromString(&vm, "InspectorFrontendHost"), toJS<IDLInterface<InspectorFrontendHost>>(state, globalObject, *this));
160     if (UNLIKELY(scope.exception()))
161         reportException(&state, scope.exception());
162 }
163
164 void InspectorFrontendHost::loaded()
165 {
166     if (m_client)
167         m_client->frontendLoaded();
168 }
169
170 void InspectorFrontendHost::requestSetDockSide(const String& side)
171 {
172     if (!m_client)
173         return;
174     if (side == "undocked")
175         m_client->requestSetDockSide(InspectorFrontendClient::DockSide::Undocked);
176     else if (side == "right")
177         m_client->requestSetDockSide(InspectorFrontendClient::DockSide::Right);
178     else if (side == "left")
179         m_client->requestSetDockSide(InspectorFrontendClient::DockSide::Left);
180     else if (side == "bottom")
181         m_client->requestSetDockSide(InspectorFrontendClient::DockSide::Bottom);
182 }
183
184 void InspectorFrontendHost::closeWindow()
185 {
186     if (m_client) {
187         m_client->closeWindow();
188         disconnectClient(); // Disconnect from client.
189     }
190 }
191
192 void InspectorFrontendHost::bringToFront()
193 {
194     if (m_client)
195         m_client->bringToFront();
196 }
197
198 void InspectorFrontendHost::inspectedURLChanged(const String& newURL)
199 {
200     if (m_client)
201         m_client->inspectedURLChanged(newURL);
202 }
203
204 void InspectorFrontendHost::setZoomFactor(float zoom)
205 {
206     if (m_frontendPage)
207         m_frontendPage->mainFrame().setPageAndTextZoomFactors(zoom, 1);
208 }
209
210 float InspectorFrontendHost::zoomFactor()
211 {
212     if (m_frontendPage)
213         return m_frontendPage->mainFrame().pageZoomFactor();
214
215     return 1.0;
216 }
217
218 String InspectorFrontendHost::userInterfaceLayoutDirection()
219 {
220     if (m_client && m_client->userInterfaceLayoutDirection() == UserInterfaceLayoutDirection::RTL)
221         return ASCIILiteral("rtl");
222
223     return ASCIILiteral("ltr");
224 }
225
226 void InspectorFrontendHost::setAttachedWindowHeight(unsigned height)
227 {
228     if (m_client)
229         m_client->changeAttachedWindowHeight(height);
230 }
231
232 void InspectorFrontendHost::setAttachedWindowWidth(unsigned width)
233 {
234     if (m_client)
235         m_client->changeAttachedWindowWidth(width);
236 }
237
238 void InspectorFrontendHost::startWindowDrag()
239 {
240     if (m_client)
241         m_client->startWindowDrag();
242 }
243
244 void InspectorFrontendHost::moveWindowBy(float x, float y) const
245 {
246     if (m_client)
247         m_client->moveWindowBy(x, y);
248 }
249
250 String InspectorFrontendHost::localizedStringsURL()
251 {
252     return m_client ? m_client->localizedStringsURL() : String();
253 }
254
255 String InspectorFrontendHost::backendCommandsURL()
256 {
257     return m_client ? m_client->backendCommandsURL() : String();
258 }
259
260 String InspectorFrontendHost::debuggableType()
261 {
262     return m_client ? m_client->debuggableType() : String();
263 }
264
265 unsigned InspectorFrontendHost::inspectionLevel()
266 {
267     return m_client ? m_client->inspectionLevel() : 1;
268 }
269
270 String InspectorFrontendHost::platform()
271 {
272 #if PLATFORM(MAC) || PLATFORM(IOS)
273     return ASCIILiteral("mac");
274 #elif OS(WINDOWS)
275     return ASCIILiteral("windows");
276 #elif OS(LINUX)
277     return ASCIILiteral("linux");
278 #elif OS(FREEBSD)
279     return ASCIILiteral("freebsd");
280 #elif OS(OPENBSD)
281     return ASCIILiteral("openbsd");
282 #else
283     return ASCIILiteral("unknown");
284 #endif
285 }
286
287 String InspectorFrontendHost::port()
288 {
289 #if PLATFORM(GTK)
290     return ASCIILiteral("gtk");
291 #else
292     return ASCIILiteral("unknown");
293 #endif
294 }
295
296 void InspectorFrontendHost::copyText(const String& text)
297 {
298     Pasteboard::createForCopyAndPaste()->writePlainText(text, Pasteboard::CannotSmartReplace);
299 }
300
301 void InspectorFrontendHost::killText(const String& text, bool shouldPrependToKillRing, bool shouldStartNewSequence)
302 {
303     if (!m_frontendPage)
304         return;
305
306     Editor& editor = m_frontendPage->focusController().focusedOrMainFrame().editor();
307     editor.setStartNewKillRingSequence(shouldStartNewSequence);
308     Editor::KillRingInsertionMode insertionMode = shouldPrependToKillRing ? Editor::KillRingInsertionMode::PrependText : Editor::KillRingInsertionMode::AppendText;
309     editor.addTextToKillRing(text, insertionMode);
310 }
311
312 void InspectorFrontendHost::openInNewTab(const String& url)
313 {
314     if (WebCore::protocolIsJavaScript(url))
315         return;
316
317     if (m_client)
318         m_client->openInNewTab(url);
319 }
320
321 bool InspectorFrontendHost::canSave()
322 {
323     if (m_client)
324         return m_client->canSave();
325     return false;
326 }
327
328 void InspectorFrontendHost::save(const String& url, const String& content, bool base64Encoded, bool forceSaveAs)
329 {
330     if (m_client)
331         m_client->save(url, content, base64Encoded, forceSaveAs);
332 }
333
334 void InspectorFrontendHost::append(const String& url, const String& content)
335 {
336     if (m_client)
337         m_client->append(url, content);
338 }
339
340 void InspectorFrontendHost::close(const String&)
341 {
342 }
343
344 void InspectorFrontendHost::sendMessageToBackend(const String& message)
345 {
346     if (m_client)
347         m_client->sendMessageToBackend(message);
348 }
349
350 #if ENABLE(CONTEXT_MENUS)
351
352 static void populateContextMenu(Vector<InspectorFrontendHost::ContextMenuItem>&& items, ContextMenu& menu)
353 {
354     for (auto& item : items) {
355         if (item.type == "separator") {
356             menu.appendItem({ SeparatorType, ContextMenuItemTagNoAction, { } });
357             continue;
358         }
359
360         if (item.type == "subMenu" && item.subItems) {
361             ContextMenu subMenu;
362             populateContextMenu(WTFMove(*item.subItems), subMenu);
363
364             menu.appendItem({ SubmenuType, ContextMenuItemTagNoAction, item.label, &subMenu });
365             continue;
366         }
367
368         auto type = item.type == "checkbox" ? CheckableActionType : ActionType;
369         auto action = static_cast<ContextMenuAction>(ContextMenuItemBaseCustomTag + item.id.value_or(0));
370         ContextMenuItem menuItem = { type, action, item.label };
371         if (item.enabled)
372             menuItem.setEnabled(*item.enabled);
373         if (item.checked)
374             menuItem.setChecked(*item.checked);
375         menu.appendItem(menuItem);
376     }
377 }
378 #endif
379
380 void InspectorFrontendHost::showContextMenu(Event& event, Vector<ContextMenuItem>&& items)
381 {
382 #if ENABLE(CONTEXT_MENUS)
383     ASSERT(m_frontendPage);
384
385     auto& state = *execStateFromPage(debuggerWorld(), m_frontendPage);
386     auto value = state.lexicalGlobalObject()->get(&state, JSC::Identifier::fromString(&state.vm(), "InspectorFrontendAPI"));
387     ASSERT(value);
388     ASSERT(value.isObject());
389     auto* frontendAPIObject = asObject(value);
390     
391     ContextMenu menu;
392     populateContextMenu(WTFMove(items), menu);
393
394     auto menuProvider = FrontendMenuProvider::create(this, { &state, frontendAPIObject }, menu.items());
395     m_menuProvider = menuProvider.ptr();
396     m_frontendPage->contextMenuController().showContextMenu(event, menuProvider);
397 #else
398     UNUSED_PARAM(event);
399     UNUSED_PARAM(items);
400 #endif
401 }
402
403 void InspectorFrontendHost::dispatchEventAsContextMenuEvent(Event& event)
404 {
405 #if ENABLE(CONTEXT_MENUS) && USE(ACCESSIBILITY_CONTEXT_MENUS)
406     if (!is<MouseEvent>(event))
407         return;
408
409     auto& mouseEvent = downcast<MouseEvent>(event);
410     IntPoint mousePoint { mouseEvent.clientX(), mouseEvent.clientY() };
411     auto& frame = *downcast<Node>(mouseEvent.target())->document().frame();
412
413     m_frontendPage->contextMenuController().showContextMenuAt(frame, mousePoint);
414 #else
415     UNUSED_PARAM(event);
416 #endif
417 }
418
419 bool InspectorFrontendHost::isUnderTest()
420 {
421     return m_client && m_client->isUnderTest();
422 }
423
424 void InspectorFrontendHost::unbufferedLog(const String& message)
425 {
426     // This is used only for debugging inspector tests.
427     WTFLogAlways("%s", message.utf8().data());
428 }
429
430 void InspectorFrontendHost::beep()
431 {
432     PAL::systemBeep();
433 }
434
435 void InspectorFrontendHost::inspectInspector()
436 {
437     if (m_frontendPage)
438         m_frontendPage->inspectorController().show();
439 }
440
441 } // namespace WebCore