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#include "WorkerDebuggerAgent.h"
33
34#if ENABLE(INSPECTOR)
35
36#include "WorkerGlobalScope.h"
37#include "WorkerThread.h"
38#include <inspector/InjectedScript.h>
39#include <inspector/InjectedScriptManager.h>
40#include <inspector/ScriptDebugServer.h>
41#include <mutex>
42#include <wtf/MessageQueue.h>
43#include <wtf/NeverDestroyed.h>
44
45using namespace Inspector;
46
47namespace WebCore {
48
49namespace {
50
51std::mutex& workerDebuggerAgentsMutex()
52{
53    static std::once_flag onceFlag;
54    static LazyNeverDestroyed<std::mutex> mutex;
55
56    std::call_once(onceFlag, []{
57        mutex.construct();
58    });
59
60    return mutex;
61}
62
63typedef HashMap<WorkerThread*, WorkerDebuggerAgent*> WorkerDebuggerAgents;
64
65WorkerDebuggerAgents& workerDebuggerAgents()
66{
67    static NeverDestroyed<WorkerDebuggerAgents> agents;
68
69    return agents;
70}
71
72class RunInspectorCommandsTask : public ScriptDebugServer::Task {
73public:
74    RunInspectorCommandsTask(WorkerThread* thread, WorkerGlobalScope* workerGlobalScope)
75        : m_thread(thread)
76        , m_workerGlobalScope(workerGlobalScope) { }
77    virtual ~RunInspectorCommandsTask() { }
78    virtual void run() override
79    {
80        // Process all queued debugger commands. It is safe to use m_workerGlobalScope here
81        // because it is alive if RunWorkerLoop is not terminated, otherwise it will
82        // just be ignored. WorkerThread is certainly alive if this task is being executed.
83        while (MessageQueueMessageReceived == m_thread->runLoop().runInMode(m_workerGlobalScope, WorkerDebuggerAgent::debuggerTaskMode, WorkerRunLoop::DontWaitForMessage)) { }
84    }
85
86private:
87    WorkerThread* m_thread;
88    WorkerGlobalScope* m_workerGlobalScope;
89};
90
91} // namespace
92
93const char* WorkerDebuggerAgent::debuggerTaskMode = "debugger";
94
95WorkerDebuggerAgent::WorkerDebuggerAgent(InjectedScriptManager* injectedScriptManager, InstrumentingAgents* instrumentingAgents, WorkerGlobalScope* inspectedWorkerGlobalScope)
96    : WebDebuggerAgent(injectedScriptManager, instrumentingAgents)
97    , m_scriptDebugServer(inspectedWorkerGlobalScope, WorkerDebuggerAgent::debuggerTaskMode)
98    , m_inspectedWorkerGlobalScope(inspectedWorkerGlobalScope)
99{
100    std::lock_guard<std::mutex> lock(workerDebuggerAgentsMutex());
101    workerDebuggerAgents().set(&inspectedWorkerGlobalScope->thread(), this);
102}
103
104WorkerDebuggerAgent::~WorkerDebuggerAgent()
105{
106    std::lock_guard<std::mutex> lock(workerDebuggerAgentsMutex());
107
108    ASSERT(workerDebuggerAgents().contains(&m_inspectedWorkerGlobalScope->thread()));
109    workerDebuggerAgents().remove(&m_inspectedWorkerGlobalScope->thread());
110}
111
112void WorkerDebuggerAgent::interruptAndDispatchInspectorCommands(WorkerThread* thread)
113{
114    std::lock_guard<std::mutex> lock(workerDebuggerAgentsMutex());
115
116    if (WorkerDebuggerAgent* agent = workerDebuggerAgents().get(thread))
117        agent->m_scriptDebugServer.interruptAndRunTask(adoptPtr(new RunInspectorCommandsTask(thread, agent->m_inspectedWorkerGlobalScope)));
118}
119
120void WorkerDebuggerAgent::startListeningScriptDebugServer()
121{
122    scriptDebugServer().addListener(this);
123}
124
125void WorkerDebuggerAgent::stopListeningScriptDebugServer(bool isBeingDestroyed)
126{
127    scriptDebugServer().removeListener(this, isBeingDestroyed);
128}
129
130void WorkerDebuggerAgent::breakpointActionLog(JSC::ExecState*, const String& message)
131{
132    m_inspectedWorkerGlobalScope->addConsoleMessage(MessageSource::JS, MessageLevel::Log, message);
133}
134
135WorkerScriptDebugServer& WorkerDebuggerAgent::scriptDebugServer()
136{
137    return m_scriptDebugServer;
138}
139
140InjectedScript WorkerDebuggerAgent::injectedScriptForEval(ErrorString* error, const int* executionContextId)
141{
142    if (executionContextId) {
143        *error = ASCIILiteral("Execution context id is not supported for workers as there is only one execution context.");
144        return InjectedScript();
145    }
146
147    JSC::ExecState* scriptState = execStateFromWorkerGlobalScope(m_inspectedWorkerGlobalScope);
148    return injectedScriptManager()->injectedScriptFor(scriptState);
149}
150
151void WorkerDebuggerAgent::muteConsole()
152{
153    // We don't need to mute console for workers.
154}
155
156void WorkerDebuggerAgent::unmuteConsole()
157{
158    // We don't need to mute console for workers.
159}
160
161} // namespace WebCore
162
163#endif // ENABLE(INSPECTOR)
164