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