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