]>
Commit | Line | Data |
---|---|---|
81345200 | 1 | /* |
ed1e77d3 | 2 | * Copyright (C) 2013, 2014 Apple Inc. All Rights Reserved. |
81345200 A |
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 "RemoteInspector.h" | |
28 | ||
29 | #if ENABLE(REMOTE_INSPECTOR) | |
30 | ||
31 | #import "InitializeThreading.h" | |
32 | #import "RemoteInspectorConstants.h" | |
33 | #import "RemoteInspectorDebuggable.h" | |
34 | #import "RemoteInspectorDebuggableConnection.h" | |
35 | #import <Foundation/Foundation.h> | |
ed1e77d3 | 36 | #import <dispatch/dispatch.h> |
81345200 A |
37 | #import <notify.h> |
38 | #import <wtf/Assertions.h> | |
ed1e77d3 | 39 | #import <wtf/MainThread.h> |
81345200 | 40 | #import <wtf/NeverDestroyed.h> |
ed1e77d3 | 41 | #import <wtf/spi/darwin/XPCSPI.h> |
81345200 | 42 | #import <wtf/text/WTFString.h> |
81345200 | 43 | |
ed1e77d3 A |
44 | #if __has_include(<sandbox/private.h>) |
45 | #import <sandbox/private.h> | |
46 | #else | |
47 | enum sandbox_filter_type { | |
48 | SANDBOX_FILTER_GLOBAL_NAME = 2, | |
49 | }; | |
81345200 A |
50 | #endif |
51 | ||
ed1e77d3 A |
52 | extern "C" int sandbox_check(pid_t, const char *operation, enum sandbox_filter_type, ...); |
53 | extern "C" const enum sandbox_filter_type SANDBOX_CHECK_NO_REPORT; | |
54 | ||
81345200 A |
55 | namespace Inspector { |
56 | ||
ed1e77d3 | 57 | static bool canAccessWebInspectorMachPort() |
81345200 | 58 | { |
ed1e77d3 A |
59 | return sandbox_check(getpid(), "mach-lookup", static_cast<enum sandbox_filter_type>(SANDBOX_FILTER_GLOBAL_NAME | SANDBOX_CHECK_NO_REPORT), WIRXPCMachPortName) == 0; |
60 | } | |
61 | ||
62 | static bool globalAutomaticInspectionState() | |
63 | { | |
64 | int token = 0; | |
65 | if (notify_register_check(WIRAutomaticInspectionEnabledState, &token) != NOTIFY_STATUS_OK) | |
66 | return false; | |
81345200 | 67 | |
ed1e77d3 A |
68 | uint64_t automaticInspectionEnabled = 0; |
69 | notify_get_state(token, &automaticInspectionEnabled); | |
70 | return automaticInspectionEnabled == 1; | |
81345200 A |
71 | } |
72 | ||
73 | bool RemoteInspector::startEnabled = true; | |
74 | ||
75 | void RemoteInspector::startDisabled() | |
76 | { | |
77 | RemoteInspector::startEnabled = false; | |
78 | } | |
79 | ||
ed1e77d3 | 80 | RemoteInspector& RemoteInspector::singleton() |
81345200 A |
81 | { |
82 | static NeverDestroyed<RemoteInspector> shared; | |
83 | ||
84 | static dispatch_once_t once; | |
85 | dispatch_once(&once, ^{ | |
ed1e77d3 A |
86 | if (canAccessWebInspectorMachPort()) { |
87 | dispatch_block_t initialize = ^{ | |
88 | WTF::initializeMainThread(); | |
89 | JSC::initializeThreading(); | |
90 | if (RemoteInspector::startEnabled) | |
91 | shared.get().start(); | |
92 | }; | |
93 | ||
94 | if ([NSThread isMainThread]) | |
95 | initialize(); | |
96 | else { | |
97 | // FIXME: This means that we may miss an auto-attach to a JSContext created on a non-main thread. | |
98 | // The main thread initialization is required for certain WTF values that need to be initialized | |
99 | // on the "real" main thread. We should investigate a better way to handle this. | |
100 | dispatch_async(dispatch_get_main_queue(), initialize); | |
101 | } | |
102 | } | |
81345200 A |
103 | }); |
104 | ||
105 | return shared; | |
106 | } | |
107 | ||
108 | RemoteInspector::RemoteInspector() | |
109 | : m_xpcQueue(dispatch_queue_create("com.apple.JavaScriptCore.remote-inspector-xpc", DISPATCH_QUEUE_SERIAL)) | |
110 | , m_nextAvailableIdentifier(1) | |
111 | , m_notifyToken(0) | |
112 | , m_enabled(false) | |
113 | , m_hasActiveDebugSession(false) | |
114 | , m_pushScheduled(false) | |
115 | , m_parentProcessIdentifier(0) | |
116 | , m_shouldSendParentProcessInformation(false) | |
ed1e77d3 A |
117 | , m_automaticInspectionEnabled(false) |
118 | , m_automaticInspectionPaused(false) | |
119 | , m_automaticInspectionCandidateIdentifier(0) | |
81345200 A |
120 | { |
121 | } | |
122 | ||
123 | unsigned RemoteInspector::nextAvailableIdentifier() | |
124 | { | |
125 | unsigned nextValidIdentifier; | |
126 | do { | |
127 | nextValidIdentifier = m_nextAvailableIdentifier++; | |
128 | } while (!nextValidIdentifier || nextValidIdentifier == std::numeric_limits<unsigned>::max() || m_debuggableMap.contains(nextValidIdentifier)); | |
129 | return nextValidIdentifier; | |
130 | } | |
131 | ||
132 | void RemoteInspector::registerDebuggable(RemoteInspectorDebuggable* debuggable) | |
133 | { | |
134 | std::lock_guard<std::mutex> lock(m_mutex); | |
135 | ||
136 | unsigned identifier = nextAvailableIdentifier(); | |
137 | debuggable->setIdentifier(identifier); | |
138 | ||
139 | auto result = m_debuggableMap.set(identifier, std::make_pair(debuggable, debuggable->info())); | |
140 | ASSERT_UNUSED(result, result.isNewEntry); | |
141 | ||
142 | if (debuggable->remoteDebuggingAllowed()) | |
143 | pushListingSoon(); | |
144 | } | |
145 | ||
146 | void RemoteInspector::unregisterDebuggable(RemoteInspectorDebuggable* debuggable) | |
147 | { | |
148 | std::lock_guard<std::mutex> lock(m_mutex); | |
149 | ||
150 | unsigned identifier = debuggable->identifier(); | |
151 | if (!identifier) | |
152 | return; | |
153 | ||
154 | bool wasRemoved = m_debuggableMap.remove(identifier); | |
155 | ASSERT_UNUSED(wasRemoved, wasRemoved); | |
156 | ||
157 | if (RefPtr<RemoteInspectorDebuggableConnection> connection = m_connectionMap.take(identifier)) | |
158 | connection->closeFromDebuggable(); | |
159 | ||
160 | if (debuggable->remoteDebuggingAllowed()) | |
161 | pushListingSoon(); | |
162 | } | |
163 | ||
164 | void RemoteInspector::updateDebuggable(RemoteInspectorDebuggable* debuggable) | |
165 | { | |
166 | std::lock_guard<std::mutex> lock(m_mutex); | |
167 | ||
168 | unsigned identifier = debuggable->identifier(); | |
169 | if (!identifier) | |
170 | return; | |
171 | ||
172 | auto result = m_debuggableMap.set(identifier, std::make_pair(debuggable, debuggable->info())); | |
173 | ASSERT_UNUSED(result, !result.isNewEntry); | |
174 | ||
175 | pushListingSoon(); | |
176 | } | |
177 | ||
ed1e77d3 A |
178 | void RemoteInspector::updateDebuggableAutomaticInspectCandidate(RemoteInspectorDebuggable* debuggable) |
179 | { | |
180 | { | |
181 | std::lock_guard<std::mutex> lock(m_mutex); | |
182 | ||
183 | unsigned identifier = debuggable->identifier(); | |
184 | if (!identifier) | |
185 | return; | |
186 | ||
187 | auto result = m_debuggableMap.set(identifier, std::make_pair(debuggable, debuggable->info())); | |
188 | ASSERT_UNUSED(result, !result.isNewEntry); | |
189 | ||
190 | // Don't allow automatic inspection unless it is allowed or we are stopped. | |
191 | if (!m_automaticInspectionEnabled || !m_enabled) { | |
192 | pushListingSoon(); | |
193 | return; | |
194 | } | |
195 | ||
196 | // FIXME: We should handle multiple debuggables trying to pause at the same time on different threads. | |
197 | // To make this work we will need to change m_automaticInspectionCandidateIdentifier to be a per-thread value. | |
198 | // Multiple attempts on the same thread should not be possible because our nested run loop is in a special RWI mode. | |
199 | if (m_automaticInspectionPaused) { | |
200 | LOG_ERROR("Skipping Automatic Inspection Candidate with pageId(%u) because we are already paused waiting for pageId(%u)", identifier, m_automaticInspectionCandidateIdentifier); | |
201 | pushListingSoon(); | |
202 | return; | |
203 | } | |
204 | ||
205 | m_automaticInspectionPaused = true; | |
206 | m_automaticInspectionCandidateIdentifier = identifier; | |
207 | ||
208 | // If we are pausing before we have connected to webinspectord the candidate message will be sent as soon as the connection is established. | |
209 | if (m_xpcConnection) { | |
210 | pushListingNow(); | |
211 | sendAutomaticInspectionCandidateMessage(); | |
212 | } | |
213 | ||
214 | // In case debuggers fail to respond, or we cannot connect to webinspectord, automatically continue after a short period of time. | |
215 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.8 * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ | |
216 | std::lock_guard<std::mutex> lock(m_mutex); | |
217 | if (m_automaticInspectionCandidateIdentifier == identifier) { | |
218 | LOG_ERROR("Skipping Automatic Inspection Candidate with pageId(%u) because we failed to receive a response in time.", m_automaticInspectionCandidateIdentifier); | |
219 | m_automaticInspectionPaused = false; | |
220 | } | |
221 | }); | |
222 | } | |
223 | ||
224 | debuggable->pauseWaitingForAutomaticInspection(); | |
225 | ||
226 | { | |
227 | std::lock_guard<std::mutex> lock(m_mutex); | |
228 | ||
229 | ASSERT(m_automaticInspectionCandidateIdentifier); | |
230 | m_automaticInspectionCandidateIdentifier = 0; | |
231 | } | |
232 | } | |
233 | ||
234 | void RemoteInspector::sendAutomaticInspectionCandidateMessage() | |
235 | { | |
236 | ASSERT(m_enabled); | |
237 | ASSERT(m_automaticInspectionEnabled); | |
238 | ASSERT(m_automaticInspectionPaused); | |
239 | ASSERT(m_automaticInspectionCandidateIdentifier); | |
240 | ASSERT(m_xpcConnection); | |
241 | ||
242 | NSDictionary *details = @{WIRPageIdentifierKey: @(m_automaticInspectionCandidateIdentifier)}; | |
243 | m_xpcConnection->sendMessage(WIRAutomaticInspectionCandidateMessage, details); | |
244 | } | |
245 | ||
81345200 A |
246 | void RemoteInspector::sendMessageToRemoteFrontend(unsigned identifier, const String& message) |
247 | { | |
248 | std::lock_guard<std::mutex> lock(m_mutex); | |
249 | ||
250 | if (!m_xpcConnection) | |
251 | return; | |
252 | ||
253 | RefPtr<RemoteInspectorDebuggableConnection> connection = m_connectionMap.get(identifier); | |
254 | if (!connection) | |
255 | return; | |
256 | ||
257 | NSDictionary *userInfo = @{ | |
258 | WIRRawDataKey: [static_cast<NSString *>(message) dataUsingEncoding:NSUTF8StringEncoding], | |
259 | WIRConnectionIdentifierKey: connection->connectionIdentifier(), | |
260 | WIRDestinationKey: connection->destination() | |
261 | }; | |
262 | ||
263 | m_xpcConnection->sendMessage(WIRRawDataMessage, userInfo); | |
264 | } | |
265 | ||
266 | void RemoteInspector::setupFailed(unsigned identifier) | |
267 | { | |
268 | std::lock_guard<std::mutex> lock(m_mutex); | |
269 | ||
270 | m_connectionMap.remove(identifier); | |
271 | ||
272 | updateHasActiveDebugSession(); | |
273 | ||
ed1e77d3 A |
274 | if (identifier == m_automaticInspectionCandidateIdentifier) |
275 | m_automaticInspectionPaused = false; | |
276 | ||
81345200 A |
277 | pushListingSoon(); |
278 | } | |
279 | ||
ed1e77d3 A |
280 | void RemoteInspector::setupCompleted(unsigned identifier) |
281 | { | |
282 | std::lock_guard<std::mutex> lock(m_mutex); | |
283 | ||
284 | if (identifier == m_automaticInspectionCandidateIdentifier) | |
285 | m_automaticInspectionPaused = false; | |
286 | } | |
287 | ||
288 | bool RemoteInspector::waitingForAutomaticInspection(unsigned) | |
289 | { | |
290 | // We don't take the lock to check this because we assume it will be checked repeatedly. | |
291 | return m_automaticInspectionPaused; | |
292 | } | |
293 | ||
81345200 A |
294 | void RemoteInspector::start() |
295 | { | |
296 | std::lock_guard<std::mutex> lock(m_mutex); | |
297 | ||
298 | if (m_enabled) | |
299 | return; | |
300 | ||
301 | m_enabled = true; | |
302 | ||
ed1e77d3 A |
303 | // Load the initial automatic inspection state when first started, so we know it before we have even connected to webinspectord. |
304 | static dispatch_once_t once; | |
305 | dispatch_once(&once, ^{ | |
306 | m_automaticInspectionEnabled = globalAutomaticInspectionState(); | |
307 | }); | |
308 | ||
81345200 | 309 | notify_register_dispatch(WIRServiceAvailableNotification, &m_notifyToken, m_xpcQueue, ^(int) { |
ed1e77d3 | 310 | RemoteInspector::singleton().setupXPCConnectionIfNeeded(); |
81345200 A |
311 | }); |
312 | ||
313 | notify_post(WIRServiceAvailabilityCheckNotification); | |
314 | } | |
315 | ||
316 | void RemoteInspector::stop() | |
317 | { | |
318 | std::lock_guard<std::mutex> lock(m_mutex); | |
319 | ||
320 | stopInternal(StopSource::API); | |
321 | } | |
322 | ||
323 | void RemoteInspector::stopInternal(StopSource source) | |
324 | { | |
325 | if (!m_enabled) | |
326 | return; | |
327 | ||
328 | m_enabled = false; | |
329 | ||
330 | m_pushScheduled = false; | |
331 | ||
332 | for (auto it = m_connectionMap.begin(), end = m_connectionMap.end(); it != end; ++it) | |
333 | it->value->close(); | |
334 | m_connectionMap.clear(); | |
335 | ||
336 | updateHasActiveDebugSession(); | |
337 | ||
ed1e77d3 A |
338 | m_automaticInspectionPaused = false; |
339 | ||
81345200 A |
340 | if (m_xpcConnection) { |
341 | switch (source) { | |
342 | case StopSource::API: | |
343 | m_xpcConnection->close(); | |
344 | break; | |
345 | case StopSource::XPCMessage: | |
346 | m_xpcConnection->closeFromMessage(); | |
347 | break; | |
348 | } | |
349 | ||
350 | m_xpcConnection = nullptr; | |
351 | } | |
352 | ||
353 | notify_cancel(m_notifyToken); | |
354 | } | |
355 | ||
356 | void RemoteInspector::setupXPCConnectionIfNeeded() | |
357 | { | |
358 | std::lock_guard<std::mutex> lock(m_mutex); | |
359 | ||
360 | if (m_xpcConnection) | |
361 | return; | |
362 | ||
363 | xpc_connection_t connection = xpc_connection_create_mach_service(WIRXPCMachPortName, m_xpcQueue, 0); | |
364 | if (!connection) | |
365 | return; | |
366 | ||
367 | m_xpcConnection = adoptRef(new RemoteInspectorXPCConnection(connection, m_xpcQueue, this)); | |
368 | m_xpcConnection->sendMessage(@"syn", nil); // Send a simple message to initialize the XPC connection. | |
369 | xpc_release(connection); | |
370 | ||
ed1e77d3 A |
371 | if (m_automaticInspectionCandidateIdentifier) { |
372 | // We already have a debuggable waiting to be automatically inspected. | |
373 | pushListingNow(); | |
374 | sendAutomaticInspectionCandidateMessage(); | |
375 | } else | |
376 | pushListingSoon(); | |
81345200 A |
377 | } |
378 | ||
379 | #pragma mark - Proxy Application Information | |
380 | ||
381 | void RemoteInspector::setParentProcessInformation(pid_t pid, RetainPtr<CFDataRef> auditData) | |
382 | { | |
383 | std::lock_guard<std::mutex> lock(m_mutex); | |
384 | ||
385 | if (m_parentProcessIdentifier || m_parentProcessAuditData) | |
386 | return; | |
387 | ||
388 | m_parentProcessIdentifier = pid; | |
389 | m_parentProcessAuditData = auditData; | |
390 | ||
391 | if (m_shouldSendParentProcessInformation) | |
392 | receivedProxyApplicationSetupMessage(nil); | |
393 | } | |
394 | ||
395 | #pragma mark - RemoteInspectorXPCConnection::Client | |
396 | ||
397 | void RemoteInspector::xpcConnectionReceivedMessage(RemoteInspectorXPCConnection*, NSString *messageName, NSDictionary *userInfo) | |
398 | { | |
399 | std::lock_guard<std::mutex> lock(m_mutex); | |
400 | ||
401 | if ([messageName isEqualToString:WIRPermissionDenied]) { | |
402 | stopInternal(StopSource::XPCMessage); | |
403 | return; | |
404 | } | |
405 | ||
406 | if ([messageName isEqualToString:WIRSocketDataMessage]) | |
407 | receivedDataMessage(userInfo); | |
408 | else if ([messageName isEqualToString:WIRSocketSetupMessage]) | |
409 | receivedSetupMessage(userInfo); | |
410 | else if ([messageName isEqualToString:WIRWebPageCloseMessage]) | |
411 | receivedDidCloseMessage(userInfo); | |
412 | else if ([messageName isEqualToString:WIRApplicationGetListingMessage]) | |
413 | receivedGetListingMessage(userInfo); | |
414 | else if ([messageName isEqualToString:WIRIndicateMessage]) | |
415 | receivedIndicateMessage(userInfo); | |
416 | else if ([messageName isEqualToString:WIRProxyApplicationSetupMessage]) | |
417 | receivedProxyApplicationSetupMessage(userInfo); | |
418 | else if ([messageName isEqualToString:WIRConnectionDiedMessage]) | |
419 | receivedConnectionDiedMessage(userInfo); | |
ed1e77d3 A |
420 | else if ([messageName isEqualToString:WIRAutomaticInspectionConfigurationMessage]) |
421 | receivedAutomaticInspectionConfigurationMessage(userInfo); | |
422 | else if ([messageName isEqualToString:WIRAutomaticInspectionRejectMessage]) | |
423 | receivedAutomaticInspectionRejectMessage(userInfo); | |
81345200 A |
424 | else |
425 | NSLog(@"Unrecognized RemoteInspector XPC Message: %@", messageName); | |
426 | } | |
427 | ||
428 | void RemoteInspector::xpcConnectionFailed(RemoteInspectorXPCConnection* connection) | |
429 | { | |
430 | std::lock_guard<std::mutex> lock(m_mutex); | |
431 | ||
432 | ASSERT(connection == m_xpcConnection); | |
433 | if (connection != m_xpcConnection) | |
434 | return; | |
435 | ||
436 | m_pushScheduled = false; | |
437 | ||
438 | for (auto it = m_connectionMap.begin(), end = m_connectionMap.end(); it != end; ++it) | |
439 | it->value->close(); | |
440 | m_connectionMap.clear(); | |
441 | ||
442 | updateHasActiveDebugSession(); | |
443 | ||
ed1e77d3 A |
444 | m_automaticInspectionPaused = false; |
445 | ||
81345200 A |
446 | // The connection will close itself. |
447 | m_xpcConnection = nullptr; | |
448 | } | |
449 | ||
450 | void RemoteInspector::xpcConnectionUnhandledMessage(RemoteInspectorXPCConnection*, xpc_object_t) | |
451 | { | |
452 | // Intentionally ignored. | |
453 | } | |
454 | ||
455 | #pragma mark - Listings | |
456 | ||
457 | NSDictionary *RemoteInspector::listingForDebuggable(const RemoteInspectorDebuggableInfo& debuggableInfo) const | |
458 | { | |
459 | NSMutableDictionary *debuggableDetails = [NSMutableDictionary dictionary]; | |
460 | ||
461 | [debuggableDetails setObject:@(debuggableInfo.identifier) forKey:WIRPageIdentifierKey]; | |
462 | ||
463 | switch (debuggableInfo.type) { | |
464 | case RemoteInspectorDebuggable::JavaScript: { | |
465 | NSString *name = debuggableInfo.name; | |
466 | [debuggableDetails setObject:name forKey:WIRTitleKey]; | |
467 | [debuggableDetails setObject:WIRTypeJavaScript forKey:WIRTypeKey]; | |
468 | break; | |
469 | } | |
470 | case RemoteInspectorDebuggable::Web: { | |
471 | NSString *url = debuggableInfo.url; | |
472 | NSString *title = debuggableInfo.name; | |
473 | [debuggableDetails setObject:url forKey:WIRURLKey]; | |
474 | [debuggableDetails setObject:title forKey:WIRTitleKey]; | |
475 | [debuggableDetails setObject:WIRTypeWeb forKey:WIRTypeKey]; | |
476 | break; | |
477 | } | |
478 | default: | |
479 | ASSERT_NOT_REACHED(); | |
480 | break; | |
481 | } | |
482 | ||
483 | if (RefPtr<RemoteInspectorDebuggableConnection> connection = m_connectionMap.get(debuggableInfo.identifier)) | |
484 | [debuggableDetails setObject:connection->connectionIdentifier() forKey:WIRConnectionIdentifierKey]; | |
485 | ||
486 | if (debuggableInfo.hasLocalDebugger) | |
487 | [debuggableDetails setObject:@YES forKey:WIRHasLocalDebuggerKey]; | |
488 | ||
489 | return debuggableDetails; | |
490 | } | |
491 | ||
492 | void RemoteInspector::pushListingNow() | |
493 | { | |
494 | ASSERT(m_xpcConnection); | |
495 | if (!m_xpcConnection) | |
496 | return; | |
497 | ||
498 | m_pushScheduled = false; | |
499 | ||
500 | RetainPtr<NSMutableDictionary> response = adoptNS([[NSMutableDictionary alloc] init]); | |
501 | for (auto it = m_debuggableMap.begin(), end = m_debuggableMap.end(); it != end; ++it) { | |
502 | const RemoteInspectorDebuggableInfo& debuggableInfo = it->value.second; | |
503 | if (debuggableInfo.remoteDebuggingAllowed) { | |
504 | NSDictionary *details = listingForDebuggable(debuggableInfo); | |
505 | [response setObject:details forKey:[NSString stringWithFormat:@"%u", debuggableInfo.identifier]]; | |
506 | } | |
507 | } | |
508 | ||
509 | RetainPtr<NSMutableDictionary> outgoing = adoptNS([[NSMutableDictionary alloc] init]); | |
510 | [outgoing setObject:response.get() forKey:WIRListingKey]; | |
511 | ||
512 | m_xpcConnection->sendMessage(WIRListingMessage, outgoing.get()); | |
513 | } | |
514 | ||
515 | void RemoteInspector::pushListingSoon() | |
516 | { | |
517 | if (!m_xpcConnection) | |
518 | return; | |
519 | ||
520 | if (m_pushScheduled) | |
521 | return; | |
522 | ||
523 | m_pushScheduled = true; | |
524 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.2 * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ | |
525 | std::lock_guard<std::mutex> lock(m_mutex); | |
526 | if (m_pushScheduled) | |
527 | pushListingNow(); | |
528 | }); | |
529 | } | |
530 | ||
531 | #pragma mark - Active Debugger Sessions | |
532 | ||
533 | void RemoteInspector::updateHasActiveDebugSession() | |
534 | { | |
535 | bool hasActiveDebuggerSession = !m_connectionMap.isEmpty(); | |
536 | if (hasActiveDebuggerSession == m_hasActiveDebugSession) | |
537 | return; | |
538 | ||
539 | m_hasActiveDebugSession = hasActiveDebuggerSession; | |
540 | ||
541 | // FIXME: Expose some way to access this state in an embedder. | |
542 | // Legacy iOS WebKit 1 had a notification. This will need to be smarter with WebKit2. | |
543 | } | |
544 | ||
ed1e77d3 | 545 | |
81345200 A |
546 | #pragma mark - Received XPC Messages |
547 | ||
548 | void RemoteInspector::receivedSetupMessage(NSDictionary *userInfo) | |
549 | { | |
550 | NSNumber *pageId = [userInfo objectForKey:WIRPageIdentifierKey]; | |
551 | if (!pageId) | |
552 | return; | |
553 | ||
554 | NSString *connectionIdentifier = [userInfo objectForKey:WIRConnectionIdentifierKey]; | |
555 | if (!connectionIdentifier) | |
556 | return; | |
557 | ||
558 | NSString *sender = [userInfo objectForKey:WIRSenderKey]; | |
559 | if (!sender) | |
560 | return; | |
561 | ||
562 | unsigned identifier = [pageId unsignedIntValue]; | |
563 | if (m_connectionMap.contains(identifier)) | |
564 | return; | |
565 | ||
566 | auto it = m_debuggableMap.find(identifier); | |
567 | if (it == m_debuggableMap.end()) | |
568 | return; | |
569 | ||
570 | // Attempt to create a connection. This may fail if the page already has an inspector or if it disallows inspection. | |
571 | RemoteInspectorDebuggable* debuggable = it->value.first; | |
572 | RemoteInspectorDebuggableInfo debuggableInfo = it->value.second; | |
573 | RefPtr<RemoteInspectorDebuggableConnection> connection = adoptRef(new RemoteInspectorDebuggableConnection(debuggable, connectionIdentifier, sender, debuggableInfo.type)); | |
ed1e77d3 A |
574 | bool isAutomaticInspection = m_automaticInspectionCandidateIdentifier == debuggable->identifier(); |
575 | ||
576 | bool automaticallyPause = false; | |
577 | NSNumber *automaticallyPauseObject = [userInfo objectForKey:WIRAutomaticallyPause]; | |
578 | if ([automaticallyPauseObject isKindOfClass:[NSNumber class]]) | |
579 | automaticallyPause = [automaticallyPauseObject boolValue]; | |
580 | ||
581 | if (!connection->setup(isAutomaticInspection, automaticallyPause)) { | |
81345200 A |
582 | connection->close(); |
583 | return; | |
584 | } | |
585 | ||
586 | m_connectionMap.set(identifier, connection.release()); | |
587 | ||
588 | updateHasActiveDebugSession(); | |
589 | ||
590 | pushListingSoon(); | |
591 | } | |
592 | ||
593 | void RemoteInspector::receivedDataMessage(NSDictionary *userInfo) | |
594 | { | |
595 | NSNumber *pageId = [userInfo objectForKey:WIRPageIdentifierKey]; | |
596 | if (!pageId) | |
597 | return; | |
598 | ||
599 | unsigned pageIdentifier = [pageId unsignedIntValue]; | |
600 | RefPtr<RemoteInspectorDebuggableConnection> connection = m_connectionMap.get(pageIdentifier); | |
601 | if (!connection) | |
602 | return; | |
603 | ||
604 | NSData *data = [userInfo objectForKey:WIRSocketDataKey]; | |
605 | RetainPtr<NSString> message = adoptNS([[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]); | |
606 | connection->sendMessageToBackend(message.get()); | |
607 | } | |
608 | ||
609 | void RemoteInspector::receivedDidCloseMessage(NSDictionary *userInfo) | |
610 | { | |
611 | NSNumber *pageId = [userInfo objectForKey:WIRPageIdentifierKey]; | |
612 | if (!pageId) | |
613 | return; | |
614 | ||
615 | NSString *connectionIdentifier = [userInfo objectForKey:WIRConnectionIdentifierKey]; | |
616 | if (!connectionIdentifier) | |
617 | return; | |
618 | ||
619 | unsigned identifier = [pageId unsignedIntValue]; | |
620 | RefPtr<RemoteInspectorDebuggableConnection> connection = m_connectionMap.get(identifier); | |
621 | if (!connection) | |
622 | return; | |
623 | ||
624 | if (![connectionIdentifier isEqualToString:connection->connectionIdentifier()]) | |
625 | return; | |
626 | ||
627 | connection->close(); | |
628 | m_connectionMap.remove(identifier); | |
629 | ||
630 | updateHasActiveDebugSession(); | |
631 | ||
632 | pushListingSoon(); | |
633 | } | |
634 | ||
635 | void RemoteInspector::receivedGetListingMessage(NSDictionary *) | |
636 | { | |
637 | pushListingNow(); | |
638 | } | |
639 | ||
640 | void RemoteInspector::receivedIndicateMessage(NSDictionary *userInfo) | |
641 | { | |
642 | NSNumber *pageId = [userInfo objectForKey:WIRPageIdentifierKey]; | |
643 | if (!pageId) | |
644 | return; | |
645 | ||
646 | unsigned identifier = [pageId unsignedIntValue]; | |
647 | BOOL indicateEnabled = [[userInfo objectForKey:WIRIndicateEnabledKey] boolValue]; | |
648 | ||
ed1e77d3 | 649 | callOnWebThreadOrDispatchAsyncOnMainThread(^{ |
81345200 A |
650 | RemoteInspectorDebuggable* debuggable = nullptr; |
651 | { | |
652 | std::lock_guard<std::mutex> lock(m_mutex); | |
653 | ||
654 | auto it = m_debuggableMap.find(identifier); | |
655 | if (it == m_debuggableMap.end()) | |
656 | return; | |
657 | ||
658 | debuggable = it->value.first; | |
659 | } | |
660 | debuggable->setIndicating(indicateEnabled); | |
661 | }); | |
662 | } | |
663 | ||
664 | void RemoteInspector::receivedProxyApplicationSetupMessage(NSDictionary *) | |
665 | { | |
666 | ASSERT(m_xpcConnection); | |
667 | if (!m_xpcConnection) | |
668 | return; | |
669 | ||
670 | if (!m_parentProcessIdentifier || !m_parentProcessAuditData) { | |
671 | // We are a proxy application without parent process information. | |
672 | // Wait a bit for the information, but give up after a second. | |
673 | m_shouldSendParentProcessInformation = true; | |
674 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ | |
675 | std::lock_guard<std::mutex> lock(m_mutex); | |
676 | if (m_shouldSendParentProcessInformation) | |
677 | stopInternal(StopSource::XPCMessage); | |
678 | }); | |
679 | return; | |
680 | } | |
681 | ||
682 | m_shouldSendParentProcessInformation = false; | |
683 | ||
684 | m_xpcConnection->sendMessage(WIRProxyApplicationSetupResponseMessage, @{ | |
685 | WIRProxyApplicationParentPIDKey: @(m_parentProcessIdentifier), | |
686 | WIRProxyApplicationParentAuditDataKey: (NSData *)m_parentProcessAuditData.get(), | |
687 | }); | |
688 | } | |
689 | ||
690 | void RemoteInspector::receivedConnectionDiedMessage(NSDictionary *userInfo) | |
691 | { | |
692 | NSString *connectionIdentifier = [userInfo objectForKey:WIRConnectionIdentifierKey]; | |
693 | if (!connectionIdentifier) | |
694 | return; | |
695 | ||
696 | auto it = m_connectionMap.begin(); | |
697 | auto end = m_connectionMap.end(); | |
698 | for (; it != end; ++it) { | |
699 | if ([connectionIdentifier isEqualToString:it->value->connectionIdentifier()]) | |
700 | break; | |
701 | } | |
702 | ||
703 | if (it == end) | |
704 | return; | |
705 | ||
706 | RefPtr<RemoteInspectorDebuggableConnection> connection = it->value; | |
707 | connection->close(); | |
708 | m_connectionMap.remove(it); | |
709 | ||
710 | updateHasActiveDebugSession(); | |
711 | } | |
712 | ||
ed1e77d3 A |
713 | void RemoteInspector::receivedAutomaticInspectionConfigurationMessage(NSDictionary *userInfo) |
714 | { | |
715 | m_automaticInspectionEnabled = [[userInfo objectForKey:WIRAutomaticInspectionEnabledKey] boolValue]; | |
716 | ||
717 | if (!m_automaticInspectionEnabled && m_automaticInspectionPaused) | |
718 | m_automaticInspectionPaused = false; | |
719 | } | |
720 | ||
721 | void RemoteInspector::receivedAutomaticInspectionRejectMessage(NSDictionary *userInfo) | |
722 | { | |
723 | unsigned rejectionIdentifier = [[userInfo objectForKey:WIRPageIdentifierKey] unsignedIntValue]; | |
724 | ||
725 | ASSERT(rejectionIdentifier == m_automaticInspectionCandidateIdentifier); | |
726 | if (rejectionIdentifier == m_automaticInspectionCandidateIdentifier) | |
727 | m_automaticInspectionPaused = false; | |
728 | } | |
729 | ||
81345200 A |
730 | } // namespace Inspector |
731 | ||
732 | #endif // ENABLE(REMOTE_INSPECTOR) |