1/*
2 * Copyright (C) 2013 Apple 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
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#import "config.h"
27#import "RemoteInspectorDebuggableConnection.h"
28
29#if ENABLE(REMOTE_INSPECTOR)
30
31#import "EventLoop.h"
32#import "RemoteInspector.h"
33#import <wtf/Vector.h>
34
35#if PLATFORM(IOS)
36#import <wtf/ios/WebCoreThread.h>
37#endif
38
39namespace Inspector {
40
41static std::mutex* rwiQueueMutex;
42static CFRunLoopSourceRef rwiRunLoopSource;
43static RemoteInspectorQueue* rwiQueue;
44
45static void RemoteInspectorHandleRunSourceGlobal(void*)
46{
47    ASSERT(CFRunLoopGetCurrent() == CFRunLoopGetMain());
48    ASSERT(rwiQueueMutex);
49    ASSERT(rwiRunLoopSource);
50    ASSERT(rwiQueue);
51
52    RemoteInspectorQueue queueCopy;
53    {
54        std::lock_guard<std::mutex> lock(*rwiQueueMutex);
55        queueCopy = *rwiQueue;
56        rwiQueue->clear();
57    }
58
59    for (const auto& block : queueCopy)
60        block();
61}
62
63static void RemoteInspectorQueueTaskOnGlobalQueue(void (^task)())
64{
65    ASSERT(rwiQueueMutex);
66    ASSERT(rwiRunLoopSource);
67    ASSERT(rwiQueue);
68
69    {
70        std::lock_guard<std::mutex> lock(*rwiQueueMutex);
71        rwiQueue->append(RemoteInspectorBlock(task));
72    }
73
74    CFRunLoopSourceSignal(rwiRunLoopSource);
75    CFRunLoopWakeUp(CFRunLoopGetMain());
76}
77
78static void RemoteInspectorInitializeGlobalQueue()
79{
80    static dispatch_once_t pred;
81    dispatch_once(&pred, ^{
82        rwiQueue = new RemoteInspectorQueue;
83        rwiQueueMutex = std::make_unique<std::mutex>().release();
84
85        CFRunLoopSourceContext runLoopSourceContext = {0, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, RemoteInspectorHandleRunSourceGlobal};
86        rwiRunLoopSource = CFRunLoopSourceCreate(nullptr, 1, &runLoopSourceContext);
87
88        // Add to the default run loop mode for default handling, and the JSContext remote inspector run loop mode when paused.
89        CFRunLoopAddSource(CFRunLoopGetMain(), rwiRunLoopSource, kCFRunLoopDefaultMode);
90        CFRunLoopAddSource(CFRunLoopGetMain(), rwiRunLoopSource, EventLoop::remoteInspectorRunLoopMode());
91    });
92}
93
94static void RemoteInspectorHandleRunSourceWithInfo(void* info)
95{
96    RemoteInspectorDebuggableConnection *debuggableConnection = static_cast<RemoteInspectorDebuggableConnection*>(info);
97
98    RemoteInspectorQueue queueCopy;
99    {
100        std::lock_guard<std::mutex> lock(debuggableConnection->queueMutex());
101        queueCopy = debuggableConnection->queue();
102        debuggableConnection->clearQueue();
103    }
104
105    for (const auto& block : queueCopy)
106        block();
107}
108
109
110RemoteInspectorDebuggableConnection::RemoteInspectorDebuggableConnection(RemoteInspectorDebuggable* debuggable, NSString *connectionIdentifier, NSString *destination, RemoteInspectorDebuggable::DebuggableType)
111    : m_debuggable(debuggable)
112    , m_connectionIdentifier(connectionIdentifier)
113    , m_destination(destination)
114    , m_identifier(debuggable->identifier())
115    , m_connected(false)
116{
117    setupRunLoop();
118}
119
120RemoteInspectorDebuggableConnection::~RemoteInspectorDebuggableConnection()
121{
122    teardownRunLoop();
123}
124
125NSString *RemoteInspectorDebuggableConnection::destination() const
126{
127    return [[m_destination copy] autorelease];
128}
129
130NSString *RemoteInspectorDebuggableConnection::connectionIdentifier() const
131{
132    return [[m_connectionIdentifier copy] autorelease];
133}
134
135void RemoteInspectorDebuggableConnection::dispatchAsyncOnDebuggable(void (^block)())
136{
137    if (m_runLoop) {
138        queueTaskOnPrivateRunLoop(block);
139        return;
140    }
141
142#if PLATFORM(IOS)
143    if (WebCoreWebThreadIsEnabled && WebCoreWebThreadIsEnabled()) {
144        WebCoreWebThreadRun(block);
145        return;
146    }
147#endif
148
149    RemoteInspectorQueueTaskOnGlobalQueue(block);
150}
151
152bool RemoteInspectorDebuggableConnection::setup()
153{
154    std::lock_guard<std::mutex> lock(m_debuggableMutex);
155
156    if (!m_debuggable)
157        return false;
158
159    ref();
160    dispatchAsyncOnDebuggable(^{
161        {
162            std::lock_guard<std::mutex> lock(m_debuggableMutex);
163            if (!m_debuggable || !m_debuggable->remoteDebuggingAllowed() || m_debuggable->hasLocalDebugger()) {
164                RemoteInspector::shared().setupFailed(identifier());
165                m_debuggable = nullptr;
166            } else {
167                m_debuggable->connect(this);
168                m_connected = true;
169            }
170        }
171        deref();
172    });
173
174    return true;
175}
176
177void RemoteInspectorDebuggableConnection::closeFromDebuggable()
178{
179    std::lock_guard<std::mutex> lock(m_debuggableMutex);
180
181    m_debuggable = nullptr;
182}
183
184void RemoteInspectorDebuggableConnection::close()
185{
186    ref();
187    dispatchAsyncOnDebuggable(^{
188        {
189            std::lock_guard<std::mutex> lock(m_debuggableMutex);
190
191            if (m_debuggable) {
192                if (m_connected)
193                    m_debuggable->disconnect();
194
195                m_debuggable = nullptr;
196            }
197        }
198        deref();
199    });
200}
201
202void RemoteInspectorDebuggableConnection::sendMessageToBackend(NSString *message)
203{
204    ref();
205    dispatchAsyncOnDebuggable(^{
206        {
207            RemoteInspectorDebuggable* debuggable = nullptr;
208            {
209                std::lock_guard<std::mutex> lock(m_debuggableMutex);
210                if (!m_debuggable)
211                    return;
212                debuggable = m_debuggable;
213            }
214
215            debuggable->dispatchMessageFromRemoteFrontend(message);
216        }
217        deref();
218    });
219}
220
221bool RemoteInspectorDebuggableConnection::sendMessageToFrontend(const String& message)
222{
223    RemoteInspector::shared().sendMessageToRemoteFrontend(identifier(), message);
224
225    return true;
226}
227
228void RemoteInspectorDebuggableConnection::setupRunLoop()
229{
230    CFRunLoopRef debuggerRunLoop = m_debuggable->debuggerRunLoop();
231    if (!debuggerRunLoop) {
232        RemoteInspectorInitializeGlobalQueue();
233        return;
234    }
235
236    m_runLoop = debuggerRunLoop;
237
238    CFRunLoopSourceContext runLoopSourceContext = {0, this, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, RemoteInspectorHandleRunSourceWithInfo};
239    m_runLoopSource = CFRunLoopSourceCreate(nullptr, 1, &runLoopSourceContext);
240
241    CFRunLoopAddSource(m_runLoop.get(), m_runLoopSource.get(), kCFRunLoopDefaultMode);
242    CFRunLoopAddSource(m_runLoop.get(), m_runLoopSource.get(), EventLoop::remoteInspectorRunLoopMode());
243}
244
245void RemoteInspectorDebuggableConnection::teardownRunLoop()
246{
247    if (!m_runLoop)
248        return;
249
250    CFRunLoopRemoveSource(m_runLoop.get(), m_runLoopSource.get(), kCFRunLoopDefaultMode);
251    CFRunLoopRemoveSource(m_runLoop.get(), m_runLoopSource.get(), EventLoop::remoteInspectorRunLoopMode());
252
253    m_runLoop = nullptr;
254    m_runLoopSource = nullptr;
255}
256
257void RemoteInspectorDebuggableConnection::queueTaskOnPrivateRunLoop(void (^block)())
258{
259    ASSERT(m_runLoop);
260
261    {
262        std::lock_guard<std::mutex> lock(m_queueMutex);
263        m_queue.append(RemoteInspectorBlock(block));
264    }
265
266    CFRunLoopSourceSignal(m_runLoopSource.get());
267    CFRunLoopWakeUp(m_runLoop.get());
268}
269
270} // namespace Inspector
271
272#endif // ENABLE(REMOTE_INSPECTOR)
273