1/*
2 * Copyright (C) 2007, 2008 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
32#if ENABLE(INSPECTOR)
33
34#include "InspectorFrontendHost.h"
35
36#include "ContextMenu.h"
37#include "ContextMenuController.h"
38#include "ContextMenuItem.h"
39#include "ContextMenuProvider.h"
40#include "DOMWrapperWorld.h"
41#include "Document.h"
42#include "Event.h"
43#include "HitTestResult.h"
44#include "InspectorFrontendClient.h"
45#include "JSMainThreadExecState.h"
46#include "MainFrame.h"
47#include "MouseEvent.h"
48#include "Node.h"
49#include "Page.h"
50#include "Pasteboard.h"
51#include "ScriptGlobalObject.h"
52#include "ScriptState.h"
53#include "Sound.h"
54#include "UserGestureIndicator.h"
55#include <bindings/ScriptFunctionCall.h>
56#include <wtf/StdLibExtras.h>
57
58using namespace Inspector;
59
60namespace WebCore {
61
62#if ENABLE(CONTEXT_MENUS)
63class FrontendMenuProvider : public ContextMenuProvider {
64public:
65    static PassRefPtr<FrontendMenuProvider> create(InspectorFrontendHost* frontendHost, Deprecated::ScriptObject frontendApiObject, const Vector<ContextMenuItem>& items)
66    {
67        return adoptRef(new FrontendMenuProvider(frontendHost, frontendApiObject, items));
68    }
69
70    void disconnect()
71    {
72        m_frontendApiObject = Deprecated::ScriptObject();
73        m_frontendHost = nullptr;
74    }
75
76private:
77    FrontendMenuProvider(InspectorFrontendHost* frontendHost, Deprecated::ScriptObject frontendApiObject, const Vector<ContextMenuItem>& items)
78        : m_frontendHost(frontendHost)
79        , m_frontendApiObject(frontendApiObject)
80        , m_items(items)
81    {
82    }
83
84    virtual ~FrontendMenuProvider()
85    {
86        contextMenuCleared();
87    }
88
89    virtual void populateContextMenu(ContextMenu* menu) override
90    {
91        for (size_t i = 0; i < m_items.size(); ++i)
92            menu->appendItem(m_items[i]);
93    }
94
95    virtual void contextMenuItemSelected(ContextMenuItem* item) override
96    {
97        if (m_frontendHost) {
98            UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture);
99            int itemNumber = item->action() - ContextMenuItemBaseCustomTag;
100
101            Deprecated::ScriptFunctionCall function(m_frontendApiObject, "contextMenuItemSelected", WebCore::functionCallHandlerFromAnyThread);
102            function.appendArgument(itemNumber);
103            function.call();
104        }
105    }
106
107    virtual void contextMenuCleared() override
108    {
109        if (m_frontendHost) {
110            Deprecated::ScriptFunctionCall function(m_frontendApiObject, "contextMenuCleared", WebCore::functionCallHandlerFromAnyThread);
111            function.call();
112
113            m_frontendHost->m_menuProvider = nullptr;
114        }
115        m_items.clear();
116    }
117
118    InspectorFrontendHost* m_frontendHost;
119    Deprecated::ScriptObject m_frontendApiObject;
120    Vector<ContextMenuItem> m_items;
121};
122#endif
123
124InspectorFrontendHost::InspectorFrontendHost(InspectorFrontendClient* client, Page* frontendPage)
125    : m_client(client)
126    , m_frontendPage(frontendPage)
127#if ENABLE(CONTEXT_MENUS)
128    , m_menuProvider(nullptr)
129#endif
130{
131}
132
133InspectorFrontendHost::~InspectorFrontendHost()
134{
135    ASSERT(!m_client);
136}
137
138void InspectorFrontendHost::disconnectClient()
139{
140    m_client = nullptr;
141#if ENABLE(CONTEXT_MENUS)
142    if (m_menuProvider)
143        m_menuProvider->disconnect();
144#endif
145    m_frontendPage = nullptr;
146}
147
148void InspectorFrontendHost::loaded()
149{
150    if (m_client)
151        m_client->frontendLoaded();
152}
153
154void InspectorFrontendHost::requestSetDockSide(const String& side)
155{
156    if (!m_client)
157        return;
158    if (side == "undocked")
159        m_client->requestSetDockSide(InspectorFrontendClient::UNDOCKED);
160    else if (side == "right")
161        m_client->requestSetDockSide(InspectorFrontendClient::DOCKED_TO_RIGHT);
162    else if (side == "bottom")
163        m_client->requestSetDockSide(InspectorFrontendClient::DOCKED_TO_BOTTOM);
164}
165
166void InspectorFrontendHost::closeWindow()
167{
168    if (m_client) {
169        m_client->closeWindow();
170        disconnectClient(); // Disconnect from client.
171    }
172}
173
174void InspectorFrontendHost::bringToFront()
175{
176    if (m_client)
177        m_client->bringToFront();
178}
179
180void InspectorFrontendHost::setZoomFactor(float zoom)
181{
182    m_frontendPage->mainFrame().setPageAndTextZoomFactors(zoom, 1);
183}
184
185void InspectorFrontendHost::inspectedURLChanged(const String& newURL)
186{
187    if (m_client)
188        m_client->inspectedURLChanged(newURL);
189}
190
191void InspectorFrontendHost::setAttachedWindowHeight(unsigned height)
192{
193    if (m_client)
194        m_client->changeAttachedWindowHeight(height);
195}
196
197void InspectorFrontendHost::setAttachedWindowWidth(unsigned width)
198{
199    if (m_client)
200        m_client->changeAttachedWindowWidth(width);
201}
202
203void InspectorFrontendHost::setToolbarHeight(unsigned height)
204{
205    if (m_client)
206        m_client->setToolbarHeight(height);
207}
208
209void InspectorFrontendHost::moveWindowBy(float x, float y) const
210{
211    if (m_client)
212        m_client->moveWindowBy(x, y);
213}
214
215String InspectorFrontendHost::localizedStringsURL()
216{
217    return m_client ? m_client->localizedStringsURL() : "";
218}
219
220String InspectorFrontendHost::debuggableType()
221{
222    return ASCIILiteral("web");
223}
224
225void InspectorFrontendHost::copyText(const String& text)
226{
227    Pasteboard::createForCopyAndPaste()->writePlainText(text, Pasteboard::CannotSmartReplace);
228}
229
230void InspectorFrontendHost::openInNewTab(const String& url)
231{
232    if (m_client)
233        m_client->openInNewTab(url);
234}
235
236bool InspectorFrontendHost::canSave()
237{
238    if (m_client)
239        return m_client->canSave();
240    return false;
241}
242
243void InspectorFrontendHost::save(const String& url, const String& content, bool base64Encoded, bool forceSaveAs)
244{
245    if (m_client)
246        m_client->save(url, content, base64Encoded, forceSaveAs);
247}
248
249void InspectorFrontendHost::append(const String& url, const String& content)
250{
251    if (m_client)
252        m_client->append(url, content);
253}
254
255void InspectorFrontendHost::close(const String&)
256{
257}
258
259void InspectorFrontendHost::sendMessageToBackend(const String& message)
260{
261    if (m_client)
262        m_client->sendMessageToBackend(message);
263}
264
265#if ENABLE(CONTEXT_MENUS)
266void InspectorFrontendHost::showContextMenu(Event* event, const Vector<ContextMenuItem>& items)
267{
268    if (!event)
269        return;
270
271    ASSERT(m_frontendPage);
272    JSC::ExecState* frontendExecState = execStateFromPage(debuggerWorld(), m_frontendPage);
273    Deprecated::ScriptObject frontendApiObject;
274    if (!ScriptGlobalObject::get(frontendExecState, "InspectorFrontendAPI", frontendApiObject)) {
275        ASSERT_NOT_REACHED();
276        return;
277    }
278    RefPtr<FrontendMenuProvider> menuProvider = FrontendMenuProvider::create(this, frontendApiObject, items);
279    m_frontendPage->contextMenuController().showContextMenu(event, menuProvider);
280    m_menuProvider = menuProvider.get();
281}
282#endif
283
284void InspectorFrontendHost::dispatchEventAsContextMenuEvent(Event* event)
285{
286#if ENABLE(CONTEXT_MENUS) && USE(ACCESSIBILITY_CONTEXT_MENUS)
287    if (!event || !event->isMouseEvent())
288        return;
289
290    Frame* frame = event->target()->toNode()->document().frame();
291    MouseEvent* mouseEvent = toMouseEvent(event);
292    IntPoint mousePoint = IntPoint(mouseEvent->clientX(), mouseEvent->clientY());
293
294    m_frontendPage->contextMenuController().showContextMenuAt(frame, mousePoint);
295#else
296    UNUSED_PARAM(event);
297#endif
298}
299
300bool InspectorFrontendHost::isUnderTest()
301{
302    return m_client && m_client->isUnderTest();
303}
304
305void InspectorFrontendHost::beep()
306{
307    systemBeep();
308}
309
310bool InspectorFrontendHost::canSaveAs()
311{
312    return false;
313}
314
315bool InspectorFrontendHost::canInspectWorkers()
316{
317    return false;
318}
319
320} // namespace WebCore
321
322#endif // ENABLE(INSPECTOR)
323