2 * Copyright (C) 2007-2017 Apple Inc. All rights reserved.
3 * Copyright (C) 2008 Matt Lilek <webkit@mattlilek.com>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
31 #include "InspectorFrontendHost.h"
33 #include "ContextMenu.h"
34 #include "ContextMenuController.h"
35 #include "ContextMenuItem.h"
36 #include "ContextMenuProvider.h"
37 #include "DOMWrapperWorld.h"
41 #include "FocusController.h"
42 #include "HitTestResult.h"
43 #include "InspectorFrontendClient.h"
44 #include "JSMainThreadExecState.h"
45 #include "MainFrame.h"
46 #include "MouseEvent.h"
49 #include "Pasteboard.h"
50 #include "ScriptGlobalObject.h"
51 #include "ScriptState.h"
52 #include "UserGestureIndicator.h"
53 #include <bindings/ScriptFunctionCall.h>
54 #include <pal/system/Sound.h>
55 #include <wtf/StdLibExtras.h>
57 using namespace Inspector;
61 #if ENABLE(CONTEXT_MENUS)
62 class FrontendMenuProvider : public ContextMenuProvider {
64 static Ref<FrontendMenuProvider> create(InspectorFrontendHost* frontendHost, Deprecated::ScriptObject frontendApiObject, const Vector<ContextMenuItem>& items)
66 return adoptRef(*new FrontendMenuProvider(frontendHost, frontendApiObject, items));
71 m_frontendApiObject = { };
72 m_frontendHost = nullptr;
76 FrontendMenuProvider(InspectorFrontendHost* frontendHost, Deprecated::ScriptObject frontendApiObject, const Vector<ContextMenuItem>& items)
77 : m_frontendHost(frontendHost)
78 , m_frontendApiObject(frontendApiObject)
83 virtual ~FrontendMenuProvider()
88 void populateContextMenu(ContextMenu* menu) override
90 for (auto& item : m_items)
91 menu->appendItem(item);
94 void contextMenuItemSelected(ContextMenuAction action, const String&) override
97 UserGestureIndicator gestureIndicator(ProcessingUserGesture);
98 int itemNumber = action - ContextMenuItemBaseCustomTag;
100 Deprecated::ScriptFunctionCall function(m_frontendApiObject, "contextMenuItemSelected", WebCore::functionCallHandlerFromAnyThread);
101 function.appendArgument(itemNumber);
106 void contextMenuCleared() override
108 if (m_frontendHost) {
109 Deprecated::ScriptFunctionCall function(m_frontendApiObject, "contextMenuCleared", WebCore::functionCallHandlerFromAnyThread);
112 m_frontendHost->m_menuProvider = nullptr;
117 InspectorFrontendHost* m_frontendHost;
118 Deprecated::ScriptObject m_frontendApiObject;
119 Vector<ContextMenuItem> m_items;
123 InspectorFrontendHost::InspectorFrontendHost(InspectorFrontendClient* client, Page* frontendPage)
125 , m_frontendPage(frontendPage)
126 #if ENABLE(CONTEXT_MENUS)
127 , m_menuProvider(nullptr)
132 InspectorFrontendHost::~InspectorFrontendHost()
137 void InspectorFrontendHost::disconnectClient()
140 #if ENABLE(CONTEXT_MENUS)
142 m_menuProvider->disconnect();
144 m_frontendPage = nullptr;
147 void InspectorFrontendHost::loaded()
150 m_client->frontendLoaded();
153 void InspectorFrontendHost::requestSetDockSide(const String& side)
157 if (side == "undocked")
158 m_client->requestSetDockSide(InspectorFrontendClient::DockSide::Undocked);
159 else if (side == "right")
160 m_client->requestSetDockSide(InspectorFrontendClient::DockSide::Right);
161 else if (side == "left")
162 m_client->requestSetDockSide(InspectorFrontendClient::DockSide::Left);
163 else if (side == "bottom")
164 m_client->requestSetDockSide(InspectorFrontendClient::DockSide::Bottom);
167 void InspectorFrontendHost::closeWindow()
170 m_client->closeWindow();
171 disconnectClient(); // Disconnect from client.
175 void InspectorFrontendHost::bringToFront()
178 m_client->bringToFront();
181 void InspectorFrontendHost::inspectedURLChanged(const String& newURL)
184 m_client->inspectedURLChanged(newURL);
187 void InspectorFrontendHost::setZoomFactor(float zoom)
190 m_frontendPage->mainFrame().setPageAndTextZoomFactors(zoom, 1);
193 float InspectorFrontendHost::zoomFactor()
196 return m_frontendPage->mainFrame().pageZoomFactor();
201 String InspectorFrontendHost::userInterfaceLayoutDirection()
203 if (m_client && m_client->userInterfaceLayoutDirection() == UserInterfaceLayoutDirection::RTL)
204 return ASCIILiteral("rtl");
206 return ASCIILiteral("ltr");
209 void InspectorFrontendHost::setAttachedWindowHeight(unsigned height)
212 m_client->changeAttachedWindowHeight(height);
215 void InspectorFrontendHost::setAttachedWindowWidth(unsigned width)
218 m_client->changeAttachedWindowWidth(width);
221 void InspectorFrontendHost::startWindowDrag()
224 m_client->startWindowDrag();
227 void InspectorFrontendHost::moveWindowBy(float x, float y) const
230 m_client->moveWindowBy(x, y);
233 String InspectorFrontendHost::localizedStringsURL()
235 return m_client ? m_client->localizedStringsURL() : String();
238 String InspectorFrontendHost::backendCommandsURL()
240 return m_client ? m_client->backendCommandsURL() : String();
243 String InspectorFrontendHost::debuggableType()
245 return m_client ? m_client->debuggableType() : String();
248 unsigned InspectorFrontendHost::inspectionLevel()
250 return m_client ? m_client->inspectionLevel() : 1;
253 String InspectorFrontendHost::platform()
255 #if PLATFORM(MAC) || PLATFORM(IOS)
256 return ASCIILiteral("mac");
258 return ASCIILiteral("windows");
260 return ASCIILiteral("linux");
262 return ASCIILiteral("freebsd");
264 return ASCIILiteral("openbsd");
266 return ASCIILiteral("solaris");
268 return ASCIILiteral("unknown");
272 String InspectorFrontendHost::port()
275 return ASCIILiteral("gtk");
277 return ASCIILiteral("unknown");
281 void InspectorFrontendHost::copyText(const String& text)
283 Pasteboard::createForCopyAndPaste()->writePlainText(text, Pasteboard::CannotSmartReplace);
286 void InspectorFrontendHost::killText(const String& text, bool shouldPrependToKillRing, bool shouldStartNewSequence)
291 Editor& editor = m_frontendPage->focusController().focusedOrMainFrame().editor();
292 editor.setStartNewKillRingSequence(shouldStartNewSequence);
293 Editor::KillRingInsertionMode insertionMode = shouldPrependToKillRing ? Editor::KillRingInsertionMode::PrependText : Editor::KillRingInsertionMode::AppendText;
294 editor.addTextToKillRing(text, insertionMode);
297 void InspectorFrontendHost::openInNewTab(const String& url)
299 if (WebCore::protocolIsJavaScript(url))
303 m_client->openInNewTab(url);
306 bool InspectorFrontendHost::canSave()
309 return m_client->canSave();
313 void InspectorFrontendHost::save(const String& url, const String& content, bool base64Encoded, bool forceSaveAs)
316 m_client->save(url, content, base64Encoded, forceSaveAs);
319 void InspectorFrontendHost::append(const String& url, const String& content)
322 m_client->append(url, content);
325 void InspectorFrontendHost::close(const String&)
329 void InspectorFrontendHost::sendMessageToBackend(const String& message)
332 m_client->sendMessageToBackend(message);
335 #if ENABLE(CONTEXT_MENUS)
337 static void populateContextMenu(Vector<InspectorFrontendHost::ContextMenuItem>&& items, ContextMenu& menu)
339 for (auto& item : items) {
340 if (item.type == "separator") {
341 menu.appendItem({ SeparatorType, ContextMenuItemTagNoAction, { } });
345 if (item.type == "subMenu" && item.subItems) {
347 populateContextMenu(WTFMove(*item.subItems), subMenu);
349 menu.appendItem({ SubmenuType, ContextMenuItemTagNoAction, item.label, &subMenu });
353 auto type = item.type == "checkbox" ? CheckableActionType : ActionType;
354 auto action = static_cast<ContextMenuAction>(ContextMenuItemBaseCustomTag + item.id.value_or(0));
355 ContextMenuItem menuItem = { type, action, item.label };
357 menuItem.setEnabled(*item.enabled);
359 menuItem.setChecked(*item.checked);
360 menu.appendItem(menuItem);
365 void InspectorFrontendHost::showContextMenu(Event& event, Vector<ContextMenuItem>&& items)
367 #if ENABLE(CONTEXT_MENUS)
368 ASSERT(m_frontendPage);
369 auto& state = *execStateFromPage(debuggerWorld(), m_frontendPage);
370 JSC::JSObject* frontendApiObject;
371 if (!ScriptGlobalObject::get(state, "InspectorFrontendAPI", frontendApiObject)) {
372 ASSERT_NOT_REACHED();
377 populateContextMenu(WTFMove(items), menu);
379 auto menuProvider = FrontendMenuProvider::create(this, { &state, frontendApiObject }, menu.items());
380 m_menuProvider = menuProvider.ptr();
381 m_frontendPage->contextMenuController().showContextMenu(event, menuProvider);
388 void InspectorFrontendHost::dispatchEventAsContextMenuEvent(Event& event)
390 #if ENABLE(CONTEXT_MENUS) && USE(ACCESSIBILITY_CONTEXT_MENUS)
391 if (!is<MouseEvent>(event))
394 auto& mouseEvent = downcast<MouseEvent>(event);
395 IntPoint mousePoint { mouseEvent.clientX(), mouseEvent.clientY() };
396 auto& frame = *mouseEvent.target()->toNode()->document().frame();
398 m_frontendPage->contextMenuController().showContextMenuAt(frame, mousePoint);
404 bool InspectorFrontendHost::isUnderTest()
406 return m_client && m_client->isUnderTest();
409 void InspectorFrontendHost::unbufferedLog(const String& message)
411 // This is used only for debugging inspector tests.
412 WTFLogAlways("%s", message.utf8().data());
415 void InspectorFrontendHost::beep()
420 } // namespace WebCore