1/*
2 * Copyright (C) 2013-2016 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#include "config.h"
27#include "RemoteInspector.h"
28
29#if ENABLE(REMOTE_INSPECTOR)
30
31#include "RemoteAutomationTarget.h"
32#include "RemoteConnectionToTarget.h"
33#include "RemoteInspectionTarget.h"
34#include "RemoteInspectorConstants.h"
35#include <wtf/MainThread.h>
36#include <wtf/text/WTFString.h>
37
38namespace Inspector {
39
40bool RemoteInspector::startEnabled = true;
41
42void RemoteInspector::startDisabled()
43{
44 RemoteInspector::startEnabled = false;
45}
46
47TargetID RemoteInspector::nextAvailableTargetIdentifier()
48{
49 TargetID nextValidTargetIdentifier;
50 do {
51 nextValidTargetIdentifier = m_nextAvailableTargetIdentifier++;
52 } while (!nextValidTargetIdentifier || nextValidTargetIdentifier == std::numeric_limits<TargetID>::max() || m_targetMap.contains(nextValidTargetIdentifier));
53 return nextValidTargetIdentifier;
54}
55
56void RemoteInspector::registerTarget(RemoteControllableTarget* target)
57{
58 ASSERT_ARG(target, target);
59
60 LockHolder lock(m_mutex);
61
62 auto targetIdentifier = nextAvailableTargetIdentifier();
63 target->setTargetIdentifier(targetIdentifier);
64
65 {
66 auto result = m_targetMap.set(targetIdentifier, target);
67 ASSERT_UNUSED(result, result.isNewEntry);
68 }
69
70 // If remote control is not allowed, a null listing is returned.
71 if (auto targetListing = listingForTarget(*target)) {
72 auto result = m_targetListingMap.set(targetIdentifier, targetListing);
73 ASSERT_UNUSED(result, result.isNewEntry);
74 }
75
76 pushListingsSoon();
77}
78
79void RemoteInspector::unregisterTarget(RemoteControllableTarget* target)
80{
81 ASSERT_ARG(target, target);
82
83 LockHolder lock(m_mutex);
84
85 auto targetIdentifier = target->targetIdentifier();
86 if (!targetIdentifier)
87 return;
88
89 bool wasRemoved = m_targetMap.remove(targetIdentifier);
90 ASSERT_UNUSED(wasRemoved, wasRemoved);
91
92 // The listing may never have been added if remote control isn't allowed.
93 m_targetListingMap.remove(targetIdentifier);
94
95 if (auto connectionToTarget = m_targetConnectionMap.take(targetIdentifier))
96 connectionToTarget->targetClosed();
97
98 pushListingsSoon();
99}
100
101void RemoteInspector::updateTarget(RemoteControllableTarget* target)
102{
103 ASSERT_ARG(target, target);
104
105 LockHolder lock(m_mutex);
106
107 if (!updateTargetMap(target))
108 return;
109
110 pushListingsSoon();
111}
112
113bool RemoteInspector::updateTargetMap(RemoteControllableTarget* target)
114{
115 ASSERT(m_mutex.isLocked());
116
117 auto targetIdentifier = target->targetIdentifier();
118 if (!targetIdentifier)
119 return false;
120
121 auto result = m_targetMap.set(targetIdentifier, target);
122 ASSERT_UNUSED(result, !result.isNewEntry);
123
124 // If the target has just allowed remote control, then the listing won't exist yet.
125 // If the target has no identifier remove the old listing.
126 if (auto targetListing = listingForTarget(*target))
127 m_targetListingMap.set(targetIdentifier, targetListing);
128 else
129 m_targetListingMap.remove(targetIdentifier);
130
131 return true;
132}
133
134#if !PLATFORM(COCOA)
135void RemoteInspector::updateAutomaticInspectionCandidate(RemoteInspectionTarget* target)
136{
137 // FIXME: Implement automatic inspection.
138 updateTarget(target);
139}
140#endif
141
142void RemoteInspector::updateClientCapabilities()
143{
144 ASSERT(isMainThread());
145
146 LockHolder lock(m_mutex);
147
148 if (!m_client)
149 m_clientCapabilities = WTF::nullopt;
150 else {
151 RemoteInspector::Client::Capabilities updatedCapabilities = {
152 m_client->remoteAutomationAllowed(),
153 m_client->browserName(),
154 m_client->browserVersion()
155 };
156
157 m_clientCapabilities = updatedCapabilities;
158 }
159}
160
161void RemoteInspector::setClient(RemoteInspector::Client* client)
162{
163 ASSERT((m_client && !client) || (!m_client && client));
164
165 {
166 LockHolder lock(m_mutex);
167 m_client = client;
168 }
169
170 // Send an updated listing that includes whether the client allows remote automation.
171 updateClientCapabilities();
172 pushListingsSoon();
173}
174
175void RemoteInspector::setupFailed(TargetID targetIdentifier)
176{
177 LockHolder lock(m_mutex);
178
179 m_targetConnectionMap.remove(targetIdentifier);
180
181 if (targetIdentifier == m_automaticInspectionCandidateTargetIdentifier)
182 m_automaticInspectionPaused = false;
183
184 updateHasActiveDebugSession();
185 updateTargetListing(targetIdentifier);
186 pushListingsSoon();
187}
188
189void RemoteInspector::setupCompleted(TargetID targetIdentifier)
190{
191 LockHolder lock(m_mutex);
192
193 if (targetIdentifier == m_automaticInspectionCandidateTargetIdentifier)
194 m_automaticInspectionPaused = false;
195}
196
197bool RemoteInspector::waitingForAutomaticInspection(TargetID)
198{
199 // We don't take the lock to check this because we assume it will be checked repeatedly.
200 return m_automaticInspectionPaused;
201}
202
203void RemoteInspector::clientCapabilitiesDidChange()
204{
205 updateClientCapabilities();
206 pushListingsSoon();
207}
208
209void RemoteInspector::stop()
210{
211 LockHolder lock(m_mutex);
212
213 stopInternal(StopSource::API);
214}
215
216TargetListing RemoteInspector::listingForTarget(const RemoteControllableTarget& target) const
217{
218 if (is<RemoteInspectionTarget>(target))
219 return listingForInspectionTarget(downcast<RemoteInspectionTarget>(target));
220 if (is<RemoteAutomationTarget>(target))
221 return listingForAutomationTarget(downcast<RemoteAutomationTarget>(target));
222
223 ASSERT_NOT_REACHED();
224 return nullptr;
225}
226
227void RemoteInspector::updateTargetListing(TargetID targetIdentifier)
228{
229 auto target = m_targetMap.get(targetIdentifier);
230 if (!target)
231 return;
232
233 updateTargetListing(*target);
234}
235
236void RemoteInspector::updateTargetListing(const RemoteControllableTarget& target)
237{
238 auto targetListing = listingForTarget(target);
239 if (!targetListing)
240 return;
241
242 m_targetListingMap.set(target.targetIdentifier(), targetListing);
243
244 pushListingsSoon();
245}
246
247void RemoteInspector::updateHasActiveDebugSession()
248{
249 bool hasActiveDebuggerSession = !m_targetConnectionMap.isEmpty();
250 if (hasActiveDebuggerSession == m_hasActiveDebugSession)
251 return;
252
253 m_hasActiveDebugSession = hasActiveDebuggerSession;
254
255 // FIXME: Expose some way to access this state in an embedder.
256 // Legacy iOS WebKit 1 had a notification. This will need to be smarter with WebKit2.
257}
258
259RemoteInspector::Client::~Client()
260{
261}
262
263} // namespace Inspector
264
265#endif // ENABLE(REMOTE_INSPECTOR)
266