Web Inspector: use more C++ keywords for defining agents
[WebKit-https.git] / Source / WebCore / inspector / InspectorFrontendHost.cpp
1 /*
2  * Copyright (C) 2007-2019 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 "CertificateInfo.h"
34 #include "ContextMenu.h"
35 #include "ContextMenuController.h"
36 #include "ContextMenuItem.h"
37 #include "ContextMenuProvider.h"
38 #include "DOMWrapperWorld.h"
39 #include "Document.h"
40 #include "Editor.h"
41 #include "Event.h"
42 #include "FloatRect.h"
43 #include "FocusController.h"
44 #include "Frame.h"
45 #include "HitTestResult.h"
46 #include "InspectorController.h"
47 #include "InspectorFrontendClient.h"
48 #include "JSDOMConvertInterface.h"
49 #include "JSDOMExceptionHandling.h"
50 #include "JSExecState.h"
51 #include "JSInspectorFrontendHost.h"
52 #include "MouseEvent.h"
53 #include "Node.h"
54 #include "Page.h"
55 #include "Pasteboard.h"
56 #include "ScriptState.h"
57 #include "UserGestureIndicator.h"
58 #include <JavaScriptCore/ScriptFunctionCall.h>
59 #include <pal/system/Sound.h>
60 #include <wtf/StdLibExtras.h>
61 #include <wtf/text/Base64.h>
62
63 namespace WebCore {
64
65 using namespace Inspector;
66
67 #if ENABLE(CONTEXT_MENUS)
68 class FrontendMenuProvider : public ContextMenuProvider {
69 public:
70     static Ref<FrontendMenuProvider> create(InspectorFrontendHost* frontendHost, Deprecated::ScriptObject frontendApiObject, const Vector<ContextMenuItem>& items)
71     {
72         return adoptRef(*new FrontendMenuProvider(frontendHost, frontendApiObject, items));
73     }
74     
75     void disconnect()
76     {
77         m_frontendApiObject = { };
78         m_frontendHost = nullptr;
79     }
80     
81 private:
82     FrontendMenuProvider(InspectorFrontendHost* frontendHost, Deprecated::ScriptObject frontendApiObject, const Vector<ContextMenuItem>& items)
83         : m_frontendHost(frontendHost)
84         , m_frontendApiObject(frontendApiObject)
85         , m_items(items)
86     {
87     }
88
89     ~FrontendMenuProvider() override
90     {
91         contextMenuCleared();
92     }
93     
94     void populateContextMenu(ContextMenu* menu) override
95     {
96         for (auto& item : m_items)
97             menu->appendItem(item);
98     }
99     
100     void contextMenuItemSelected(ContextMenuAction action, const String&) override
101     {
102         if (m_frontendHost) {
103             UserGestureIndicator gestureIndicator(ProcessingUserGesture);
104             int itemNumber = action - ContextMenuItemBaseCustomTag;
105
106             Deprecated::ScriptFunctionCall function(m_frontendApiObject, "contextMenuItemSelected", WebCore::functionCallHandlerFromAnyThread);
107             function.appendArgument(itemNumber);
108             function.call();
109         }
110     }
111     
112     void contextMenuCleared() override
113     {
114         if (m_frontendHost) {
115             Deprecated::ScriptFunctionCall function(m_frontendApiObject, "contextMenuCleared", WebCore::functionCallHandlerFromAnyThread);
116             function.call();
117
118             m_frontendHost->m_menuProvider = nullptr;
119         }
120         m_items.clear();
121     }
122
123     InspectorFrontendHost* m_frontendHost;
124     Deprecated::ScriptObject m_frontendApiObject;
125     Vector<ContextMenuItem> m_items;
126 };
127 #endif
128
129 InspectorFrontendHost::InspectorFrontendHost(InspectorFrontendClient* client, Page* frontendPage)
130     : m_client(client)
131     , m_frontendPage(frontendPage)
132 #if ENABLE(CONTEXT_MENUS)
133     , m_menuProvider(nullptr)
134 #endif
135 {
136 }
137
138 InspectorFrontendHost::~InspectorFrontendHost()
139 {
140     ASSERT(!m_client);
141 }
142
143 void InspectorFrontendHost::disconnectClient()
144 {
145     m_client = nullptr;
146 #if ENABLE(CONTEXT_MENUS)
147     if (m_menuProvider)
148         m_menuProvider->disconnect();
149 #endif
150     m_frontendPage = nullptr;
151 }
152
153 void InspectorFrontendHost::addSelfToGlobalObjectInWorld(DOMWrapperWorld& world)
154 {
155     auto& state = *execStateFromPage(world, m_frontendPage);
156     auto& vm = state.vm();
157     JSC::JSLockHolder lock(vm);
158     auto scope = DECLARE_CATCH_SCOPE(vm);
159
160     auto& globalObject = *JSC::jsCast<JSDOMGlobalObject*>(state.lexicalGlobalObject());
161     globalObject.putDirect(vm, JSC::Identifier::fromString(vm, "InspectorFrontendHost"), toJS<IDLInterface<InspectorFrontendHost>>(state, globalObject, *this));
162     if (UNLIKELY(scope.exception()))
163         reportException(&state, scope.exception());
164 }
165
166 void InspectorFrontendHost::loaded()
167 {
168     if (m_client)
169         m_client->frontendLoaded();
170 }
171
172 void InspectorFrontendHost::requestSetDockSide(const String& side)
173 {
174     if (!m_client)
175         return;
176     if (side == "undocked")
177         m_client->requestSetDockSide(InspectorFrontendClient::DockSide::Undocked);
178     else if (side == "right")
179         m_client->requestSetDockSide(InspectorFrontendClient::DockSide::Right);
180     else if (side == "left")
181         m_client->requestSetDockSide(InspectorFrontendClient::DockSide::Left);
182     else if (side == "bottom")
183         m_client->requestSetDockSide(InspectorFrontendClient::DockSide::Bottom);
184 }
185
186 void InspectorFrontendHost::closeWindow()
187 {
188     if (m_client) {
189         m_client->closeWindow();
190         disconnectClient(); // Disconnect from client.
191     }
192 }
193
194 void InspectorFrontendHost::reopen()
195 {
196     if (m_client)
197         m_client->reopen();
198 }
199
200 void InspectorFrontendHost::reset()
201 {
202     if (m_client)
203         m_client->resetState();
204     reopen();
205 }
206
207 void InspectorFrontendHost::bringToFront()
208 {
209     if (m_client)
210         m_client->bringToFront();
211 }
212
213 void InspectorFrontendHost::inspectedURLChanged(const String& newURL)
214 {
215     if (m_client)
216         m_client->inspectedURLChanged(newURL);
217 }
218
219 void InspectorFrontendHost::setZoomFactor(float zoom)
220 {
221     if (m_frontendPage)
222         m_frontendPage->mainFrame().setPageAndTextZoomFactors(zoom, 1);
223 }
224
225 float InspectorFrontendHost::zoomFactor()
226 {
227     if (m_frontendPage)
228         return m_frontendPage->mainFrame().pageZoomFactor();
229
230     return 1.0;
231 }
232
233 String InspectorFrontendHost::userInterfaceLayoutDirection()
234 {
235     if (m_client && m_client->userInterfaceLayoutDirection() == UserInterfaceLayoutDirection::RTL)
236         return "rtl"_s;
237
238     return "ltr"_s;
239 }
240
241 void InspectorFrontendHost::setAttachedWindowHeight(unsigned height)
242 {
243     if (m_client)
244         m_client->changeAttachedWindowHeight(height);
245 }
246
247 void InspectorFrontendHost::setAttachedWindowWidth(unsigned width)
248 {
249     if (m_client)
250         m_client->changeAttachedWindowWidth(width);
251 }
252
253 void InspectorFrontendHost::setSheetRect(float x, float y, unsigned width, unsigned height)
254 {
255     if (m_client)
256         m_client->changeSheetRect(FloatRect(x, y, width, height));
257 }
258
259 void InspectorFrontendHost::startWindowDrag()
260 {
261     if (m_client)
262         m_client->startWindowDrag();
263 }
264
265 void InspectorFrontendHost::moveWindowBy(float x, float y) const
266 {
267     if (m_client)
268         m_client->moveWindowBy(x, y);
269 }
270
271 bool InspectorFrontendHost::isRemote() const
272 {
273     return m_client ? m_client->isRemote() : false;
274 }
275
276 String InspectorFrontendHost::localizedStringsURL()
277 {
278     return m_client ? m_client->localizedStringsURL() : String();
279 }
280
281 String InspectorFrontendHost::backendCommandsURL()
282 {
283     return m_client ? m_client->backendCommandsURL() : String();
284 }
285
286 String InspectorFrontendHost::debuggableType()
287 {
288     return m_client ? m_client->debuggableType() : String();
289 }
290
291 unsigned InspectorFrontendHost::inspectionLevel()
292 {
293     return m_client ? m_client->inspectionLevel() : 1;
294 }
295
296 String InspectorFrontendHost::platform()
297 {
298 #if PLATFORM(COCOA)
299     return "mac"_s;
300 #elif OS(WINDOWS)
301     return "windows"_s;
302 #elif OS(LINUX)
303     return "linux"_s;
304 #elif OS(FREEBSD)
305     return "freebsd"_s;
306 #elif OS(OPENBSD)
307     return "openbsd"_s;
308 #else
309     return "unknown"_s;
310 #endif
311 }
312
313 String InspectorFrontendHost::port()
314 {
315 #if PLATFORM(GTK)
316     return "gtk"_s;
317 #else
318     return "unknown"_s;
319 #endif
320 }
321
322 void InspectorFrontendHost::copyText(const String& text)
323 {
324     Pasteboard::createForCopyAndPaste()->writePlainText(text, Pasteboard::CannotSmartReplace);
325 }
326
327 void InspectorFrontendHost::killText(const String& text, bool shouldPrependToKillRing, bool shouldStartNewSequence)
328 {
329     if (!m_frontendPage)
330         return;
331
332     Editor& editor = m_frontendPage->focusController().focusedOrMainFrame().editor();
333     editor.setStartNewKillRingSequence(shouldStartNewSequence);
334     Editor::KillRingInsertionMode insertionMode = shouldPrependToKillRing ? Editor::KillRingInsertionMode::PrependText : Editor::KillRingInsertionMode::AppendText;
335     editor.addTextToKillRing(text, insertionMode);
336 }
337
338 void InspectorFrontendHost::openInNewTab(const String& url)
339 {
340     if (WTF::protocolIsJavaScript(url))
341         return;
342
343     if (m_client)
344         m_client->openInNewTab(url);
345 }
346
347 bool InspectorFrontendHost::canSave()
348 {
349     if (m_client)
350         return m_client->canSave();
351     return false;
352 }
353
354 void InspectorFrontendHost::save(const String& url, const String& content, bool base64Encoded, bool forceSaveAs)
355 {
356     if (m_client)
357         m_client->save(url, content, base64Encoded, forceSaveAs);
358 }
359
360 void InspectorFrontendHost::append(const String& url, const String& content)
361 {
362     if (m_client)
363         m_client->append(url, content);
364 }
365
366 void InspectorFrontendHost::close(const String&)
367 {
368 }
369
370 void InspectorFrontendHost::sendMessageToBackend(const String& message)
371 {
372     if (m_client)
373         m_client->sendMessageToBackend(message);
374 }
375
376 #if ENABLE(CONTEXT_MENUS)
377
378 static void populateContextMenu(Vector<InspectorFrontendHost::ContextMenuItem>&& items, ContextMenu& menu)
379 {
380     for (auto& item : items) {
381         if (item.type == "separator") {
382             menu.appendItem({ SeparatorType, ContextMenuItemTagNoAction, { } });
383             continue;
384         }
385
386         if (item.type == "subMenu" && item.subItems) {
387             ContextMenu subMenu;
388             populateContextMenu(WTFMove(*item.subItems), subMenu);
389
390             menu.appendItem({ SubmenuType, ContextMenuItemTagNoAction, item.label, &subMenu });
391             continue;
392         }
393
394         auto type = item.type == "checkbox" ? CheckableActionType : ActionType;
395         auto action = static_cast<ContextMenuAction>(ContextMenuItemBaseCustomTag + item.id.valueOr(0));
396         ContextMenuItem menuItem = { type, action, item.label };
397         if (item.enabled)
398             menuItem.setEnabled(*item.enabled);
399         if (item.checked)
400             menuItem.setChecked(*item.checked);
401         menu.appendItem(menuItem);
402     }
403 }
404 #endif
405
406 void InspectorFrontendHost::showContextMenu(Event& event, Vector<ContextMenuItem>&& items)
407 {
408 #if ENABLE(CONTEXT_MENUS)
409     ASSERT(m_frontendPage);
410
411     auto& state = *execStateFromPage(debuggerWorld(), m_frontendPage);
412     auto& vm = state.vm();
413     auto value = state.lexicalGlobalObject()->get(&state, JSC::Identifier::fromString(vm, "InspectorFrontendAPI"));
414     ASSERT(value);
415     ASSERT(value.isObject());
416     auto* frontendAPIObject = asObject(value);
417     
418     ContextMenu menu;
419     populateContextMenu(WTFMove(items), menu);
420
421     auto menuProvider = FrontendMenuProvider::create(this, { &state, frontendAPIObject }, menu.items());
422     m_menuProvider = menuProvider.ptr();
423     m_frontendPage->contextMenuController().showContextMenu(event, menuProvider);
424 #else
425     UNUSED_PARAM(event);
426     UNUSED_PARAM(items);
427 #endif
428 }
429
430 void InspectorFrontendHost::dispatchEventAsContextMenuEvent(Event& event)
431 {
432 #if ENABLE(CONTEXT_MENUS) && USE(ACCESSIBILITY_CONTEXT_MENUS)
433     if (!is<MouseEvent>(event))
434         return;
435
436     auto& mouseEvent = downcast<MouseEvent>(event);
437     auto& frame = *downcast<Node>(mouseEvent.target())->document().frame();
438     m_frontendPage->contextMenuController().showContextMenuAt(frame, roundedIntPoint(mouseEvent.absoluteLocation()));
439 #else
440     UNUSED_PARAM(event);
441 #endif
442 }
443
444 bool InspectorFrontendHost::isUnderTest()
445 {
446     return m_client && m_client->isUnderTest();
447 }
448
449 void InspectorFrontendHost::unbufferedLog(const String& message)
450 {
451     // This is used only for debugging inspector tests.
452     WTFLogAlways("%s", message.utf8().data());
453 }
454
455 void InspectorFrontendHost::beep()
456 {
457     PAL::systemBeep();
458 }
459
460 void InspectorFrontendHost::inspectInspector()
461 {
462     if (m_frontendPage)
463         m_frontendPage->inspectorController().show();
464 }
465
466 bool InspectorFrontendHost::isBeingInspected()
467 {
468     if (!m_frontendPage)
469         return false;
470
471     InspectorController& inspectorController = m_frontendPage->inspectorController();
472     return inspectorController.hasLocalFrontend() || inspectorController.hasRemoteFrontend();
473 }
474
475 bool InspectorFrontendHost::supportsShowCertificate() const
476 {
477 #if PLATFORM(COCOA)
478     return true;
479 #else
480     return false;
481 #endif
482 }
483
484 bool InspectorFrontendHost::showCertificate(const String& serializedCertificate)
485 {
486     if (!m_client)
487         return false;
488
489     Vector<uint8_t> data;
490     if (!base64Decode(serializedCertificate, data))
491         return false;
492
493     CertificateInfo certificateInfo;
494     WTF::Persistence::Decoder decoder(data.data(), data.size());
495     if (!decoder.decode(certificateInfo))
496         return false;
497
498     if (certificateInfo.isEmpty())
499         return false;
500
501     m_client->showCertificate(certificateInfo);
502     return true;
503 }
504
505 } // namespace WebCore