1/*
2 * Copyright (C) 2011 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 *     * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *     * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 *     * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "config.h"
32
33#include "core/inspector/InspectorWorkerAgent.h"
34
35#include "core/InspectorFrontend.h"
36#include "core/inspector/InspectorState.h"
37#include "core/inspector/InstrumentingAgents.h"
38#include "core/inspector/JSONParser.h"
39#include "core/workers/WorkerInspectorProxy.h"
40#include "platform/JSONValues.h"
41#include "platform/weborigin/KURL.h"
42#include "wtf/PassOwnPtr.h"
43#include "wtf/RefPtr.h"
44#include "wtf/text/WTFString.h"
45
46namespace blink {
47
48namespace WorkerAgentState {
49static const char workerInspectionEnabled[] = "workerInspectionEnabled";
50static const char autoconnectToWorkers[] = "autoconnectToWorkers";
51};
52
53class InspectorWorkerAgent::WorkerFrontendChannel FINAL : public WorkerInspectorProxy::PageInspector {
54    WTF_MAKE_FAST_ALLOCATED;
55public:
56    explicit WorkerFrontendChannel(InspectorFrontend::Worker* frontend, WorkerInspectorProxy* proxy)
57        : m_frontend(frontend)
58        , m_proxy(proxy)
59        , m_id(s_nextId++)
60        , m_connected(false)
61    {
62        ASSERT(!proxy->pageInspector());
63    }
64    virtual ~WorkerFrontendChannel()
65    {
66        disconnectFromWorker();
67    }
68
69    int id() const { return m_id; }
70    WorkerInspectorProxy* proxy() const { return m_proxy; }
71
72    void connectToWorker()
73    {
74        if (m_connected)
75            return;
76        m_connected = true;
77        m_proxy->connectToInspector(this);
78    }
79
80    void disconnectFromWorker()
81    {
82        if (!m_connected)
83            return;
84        m_connected = false;
85        m_proxy->disconnectFromInspector();
86    }
87
88private:
89    // WorkerInspectorProxy::PageInspector implementation
90    virtual void dispatchMessageFromWorker(const String& message) OVERRIDE
91    {
92        RefPtr<JSONValue> value = parseJSON(message);
93        if (!value)
94            return;
95        RefPtr<JSONObject> messageObject = value->asObject();
96        if (!messageObject)
97            return;
98        m_frontend->dispatchMessageFromWorker(m_id, messageObject);
99    }
100
101    InspectorFrontend::Worker* m_frontend;
102    WorkerInspectorProxy* m_proxy;
103    int m_id;
104    bool m_connected;
105    static int s_nextId;
106};
107
108int InspectorWorkerAgent::WorkerFrontendChannel::s_nextId = 1;
109
110PassOwnPtrWillBeRawPtr<InspectorWorkerAgent> InspectorWorkerAgent::create()
111{
112    return adoptPtrWillBeNoop(new InspectorWorkerAgent());
113}
114
115InspectorWorkerAgent::InspectorWorkerAgent()
116    : InspectorBaseAgent<InspectorWorkerAgent>("Worker")
117    , m_frontend(0)
118{
119}
120
121InspectorWorkerAgent::~InspectorWorkerAgent()
122{
123#if !ENABLE(OILPAN)
124    m_instrumentingAgents->setInspectorWorkerAgent(0);
125#endif
126}
127
128void InspectorWorkerAgent::init()
129{
130    m_instrumentingAgents->setInspectorWorkerAgent(this);
131}
132
133void InspectorWorkerAgent::setFrontend(InspectorFrontend* frontend)
134{
135    m_frontend = frontend->worker();
136}
137
138void InspectorWorkerAgent::restore()
139{
140    if (m_state->getBoolean(WorkerAgentState::workerInspectionEnabled))
141        createWorkerFrontendChannelsForExistingWorkers();
142}
143
144void InspectorWorkerAgent::clearFrontend()
145{
146    m_state->setBoolean(WorkerAgentState::autoconnectToWorkers, false);
147    disable(0);
148    m_frontend = 0;
149}
150
151void InspectorWorkerAgent::enable(ErrorString*)
152{
153    m_state->setBoolean(WorkerAgentState::workerInspectionEnabled, true);
154    if (!m_frontend)
155        return;
156    createWorkerFrontendChannelsForExistingWorkers();
157}
158
159void InspectorWorkerAgent::disable(ErrorString*)
160{
161    m_state->setBoolean(WorkerAgentState::workerInspectionEnabled, false);
162    if (!m_frontend)
163        return;
164    destroyWorkerFrontendChannels();
165}
166
167void InspectorWorkerAgent::canInspectWorkers(ErrorString*, bool* result)
168{
169    *result = true;
170}
171
172void InspectorWorkerAgent::connectToWorker(ErrorString* error, int workerId)
173{
174    WorkerFrontendChannel* channel = m_idToChannel.get(workerId);
175    if (channel)
176        channel->connectToWorker();
177    else
178        *error = "Worker is gone";
179}
180
181void InspectorWorkerAgent::disconnectFromWorker(ErrorString* error, int workerId)
182{
183    WorkerFrontendChannel* channel = m_idToChannel.get(workerId);
184    if (channel)
185        channel->disconnectFromWorker();
186    else
187        *error = "Worker is gone";
188}
189
190void InspectorWorkerAgent::sendMessageToWorker(ErrorString* error, int workerId, const RefPtr<JSONObject>& message)
191{
192    WorkerFrontendChannel* channel = m_idToChannel.get(workerId);
193    if (channel)
194        channel->proxy()->sendMessageToInspector(message->toJSONString());
195    else
196        *error = "Worker is gone";
197}
198
199void InspectorWorkerAgent::setAutoconnectToWorkers(ErrorString*, bool value)
200{
201    m_state->setBoolean(WorkerAgentState::autoconnectToWorkers, value);
202}
203
204void InspectorWorkerAgent::setTracingSessionId(const String& sessionId)
205{
206    m_tracingSessionId = sessionId;
207    if (sessionId.isEmpty())
208        return;
209    for (WorkerIds::iterator it = m_workerIds.begin(); it != m_workerIds.end(); ++it)
210        it->key->writeTimelineStartedEvent(sessionId);
211}
212
213bool InspectorWorkerAgent::shouldPauseDedicatedWorkerOnStart()
214{
215    return m_state->getBoolean(WorkerAgentState::autoconnectToWorkers);
216}
217
218void InspectorWorkerAgent::didStartWorker(WorkerInspectorProxy* workerInspectorProxy, const KURL& url)
219{
220    m_workerIds.set(workerInspectorProxy, url.string());
221    if (m_frontend && m_state->getBoolean(WorkerAgentState::workerInspectionEnabled))
222        createWorkerFrontendChannel(workerInspectorProxy, url.string());
223    if (!m_tracingSessionId.isEmpty())
224        workerInspectorProxy->writeTimelineStartedEvent(m_tracingSessionId);
225}
226
227void InspectorWorkerAgent::workerTerminated(WorkerInspectorProxy* proxy)
228{
229    m_workerIds.remove(proxy);
230    for (WorkerChannels::iterator it = m_idToChannel.begin(); it != m_idToChannel.end(); ++it) {
231        if (proxy == it->value->proxy()) {
232            m_frontend->workerTerminated(it->key);
233            delete it->value;
234            m_idToChannel.remove(it);
235            return;
236        }
237    }
238}
239
240void InspectorWorkerAgent::createWorkerFrontendChannelsForExistingWorkers()
241{
242    for (WorkerIds::iterator it = m_workerIds.begin(); it != m_workerIds.end(); ++it)
243        createWorkerFrontendChannel(it->key, it->value);
244}
245
246void InspectorWorkerAgent::destroyWorkerFrontendChannels()
247{
248    for (WorkerChannels::iterator it = m_idToChannel.begin(); it != m_idToChannel.end(); ++it) {
249        it->value->disconnectFromWorker();
250        delete it->value;
251    }
252    m_idToChannel.clear();
253}
254
255void InspectorWorkerAgent::createWorkerFrontendChannel(WorkerInspectorProxy* workerInspectorProxy, const String& url)
256{
257    WorkerFrontendChannel* channel = new WorkerFrontendChannel(m_frontend, workerInspectorProxy);
258    m_idToChannel.set(channel->id(), channel);
259
260    ASSERT(m_frontend);
261    bool autoconnectToWorkers = m_state->getBoolean(WorkerAgentState::autoconnectToWorkers);
262    if (autoconnectToWorkers)
263        channel->connectToWorker();
264    m_frontend->workerCreated(channel->id(), url, autoconnectToWorkers);
265}
266
267} // namespace blink
268