]> git.saurik.com Git - apple/javascriptcore.git/blob - inspector/remote/RemoteInspector.mm
6814dd311338d6d3f98dee5c189c25929175415c
[apple/javascriptcore.git] / inspector / remote / RemoteInspector.mm
1 /*
2 * Copyright (C) 2013, 2014 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 "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>
36 #import <dispatch/dispatch.h>
37 #import <notify.h>
38 #import <wtf/Assertions.h>
39 #import <wtf/MainThread.h>
40 #import <wtf/NeverDestroyed.h>
41 #import <wtf/spi/darwin/XPCSPI.h>
42 #import <wtf/text/WTFString.h>
43
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 };
50 #endif
51
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
55 namespace Inspector {
56
57 static bool canAccessWebInspectorMachPort()
58 {
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;
67
68 uint64_t automaticInspectionEnabled = 0;
69 notify_get_state(token, &automaticInspectionEnabled);
70 return automaticInspectionEnabled == 1;
71 }
72
73 bool RemoteInspector::startEnabled = true;
74
75 void RemoteInspector::startDisabled()
76 {
77 RemoteInspector::startEnabled = false;
78 }
79
80 RemoteInspector& RemoteInspector::singleton()
81 {
82 static NeverDestroyed<RemoteInspector> shared;
83
84 static dispatch_once_t once;
85 dispatch_once(&once, ^{
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 }
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)
117 , m_automaticInspectionEnabled(false)
118 , m_automaticInspectionPaused(false)
119 , m_automaticInspectionCandidateIdentifier(0)
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
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
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
274 if (identifier == m_automaticInspectionCandidateIdentifier)
275 m_automaticInspectionPaused = false;
276
277 pushListingSoon();
278 }
279
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
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
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
309 notify_register_dispatch(WIRServiceAvailableNotification, &m_notifyToken, m_xpcQueue, ^(int) {
310 RemoteInspector::singleton().setupXPCConnectionIfNeeded();
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
338 m_automaticInspectionPaused = false;
339
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
371 if (m_automaticInspectionCandidateIdentifier) {
372 // We already have a debuggable waiting to be automatically inspected.
373 pushListingNow();
374 sendAutomaticInspectionCandidateMessage();
375 } else
376 pushListingSoon();
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);
420 else if ([messageName isEqualToString:WIRAutomaticInspectionConfigurationMessage])
421 receivedAutomaticInspectionConfigurationMessage(userInfo);
422 else if ([messageName isEqualToString:WIRAutomaticInspectionRejectMessage])
423 receivedAutomaticInspectionRejectMessage(userInfo);
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
444 m_automaticInspectionPaused = false;
445
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
545
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));
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)) {
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
649 callOnWebThreadOrDispatchAsyncOnMainThread(^{
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
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
730 } // namespace Inspector
731
732 #endif // ENABLE(REMOTE_INSPECTOR)