[WTF] Import std::optional reference implementation as WTF::Optional
[WebKit-https.git] / Source / JavaScriptCore / inspector / remote / RemoteInspector.mm
1 /*
2  * Copyright (C) 2013-2016 Apple Inc. All Rights Reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "RemoteInspector.h"
28
29 #if ENABLE(REMOTE_INSPECTOR)
30
31 #import "InitializeThreading.h"
32 #import "RemoteAutomationTarget.h"
33 #import "RemoteConnectionToTarget.h"
34 #import "RemoteInspectionTarget.h"
35 #import "RemoteInspectorConstants.h"
36 #import <Foundation/Foundation.h>
37 #import <dispatch/dispatch.h>
38 #import <notify.h>
39 #import <wtf/Assertions.h>
40 #import <wtf/MainThread.h>
41 #import <wtf/NeverDestroyed.h>
42 #import <wtf/spi/darwin/SandboxSPI.h>
43 #import <wtf/spi/darwin/XPCSPI.h>
44 #import <wtf/text/WTFString.h>
45
46 namespace Inspector {
47
48 static bool canAccessWebInspectorMachPort()
49 {
50     return sandbox_check(getpid(), "mach-lookup", static_cast<enum sandbox_filter_type>(SANDBOX_FILTER_GLOBAL_NAME | SANDBOX_CHECK_NO_REPORT), WIRXPCMachPortName) == 0;
51 }
52
53 static bool globalAutomaticInspectionState()
54 {
55     int token = 0;
56     if (notify_register_check(WIRAutomaticInspectionEnabledState, &token) != NOTIFY_STATUS_OK)
57         return false;
58
59     uint64_t automaticInspectionEnabled = 0;
60     notify_get_state(token, &automaticInspectionEnabled);
61     return automaticInspectionEnabled == 1;
62 }
63
64 bool RemoteInspector::startEnabled = true;
65
66 void RemoteInspector::startDisabled()
67 {
68     RemoteInspector::startEnabled = false;
69 }
70
71 RemoteInspector& RemoteInspector::singleton()
72 {
73     static NeverDestroyed<RemoteInspector> shared;
74
75     static dispatch_once_t once;
76     dispatch_once(&once, ^{
77         if (canAccessWebInspectorMachPort()) {
78             dispatch_block_t initialize = ^{
79                 WTF::initializeMainThread();
80                 JSC::initializeThreading();
81                 if (RemoteInspector::startEnabled)
82                     shared.get().start();
83             };
84
85             if ([NSThread isMainThread])
86                 initialize();
87             else {
88                 // FIXME: This means that we may miss an auto-attach to a JSContext created on a non-main thread.
89                 // The main thread initialization is required for certain WTF values that need to be initialized
90                 // on the "real" main thread. We should investigate a better way to handle this.
91                 dispatch_async(dispatch_get_main_queue(), initialize);
92             }
93         }
94     });
95
96     return shared;
97 }
98
99 RemoteInspector::RemoteInspector()
100     : m_xpcQueue(dispatch_queue_create("com.apple.JavaScriptCore.remote-inspector-xpc", DISPATCH_QUEUE_SERIAL))
101 {
102 }
103
104 unsigned RemoteInspector::nextAvailableTargetIdentifier()
105 {
106     unsigned nextValidTargetIdentifier;
107     do {
108         nextValidTargetIdentifier = m_nextAvailableTargetIdentifier++;
109     } while (!nextValidTargetIdentifier || nextValidTargetIdentifier == std::numeric_limits<unsigned>::max() || m_targetMap.contains(nextValidTargetIdentifier));
110     return nextValidTargetIdentifier;
111 }
112
113 void RemoteInspector::registerTarget(RemoteControllableTarget* target)
114 {
115     ASSERT_ARG(target, target);
116
117     std::lock_guard<Lock> lock(m_mutex);
118
119     unsigned targetIdentifier = nextAvailableTargetIdentifier();
120     target->setTargetIdentifier(targetIdentifier);
121
122     {
123         auto result = m_targetMap.set(targetIdentifier, target);
124         ASSERT_UNUSED(result, result.isNewEntry);
125     }
126
127     // If remote control is not allowed, a null listing is returned.
128     if (RetainPtr<NSDictionary> targetListing = listingForTarget(*target)) {
129         auto result = m_targetListingMap.set(targetIdentifier, targetListing);
130         ASSERT_UNUSED(result, result.isNewEntry);
131     }
132
133     pushListingsSoon();
134 }
135
136 void RemoteInspector::unregisterTarget(RemoteControllableTarget* target)
137 {
138     ASSERT_ARG(target, target);
139
140     std::lock_guard<Lock> lock(m_mutex);
141
142     unsigned targetIdentifier = target->targetIdentifier();
143     if (!targetIdentifier)
144         return;
145
146     bool wasRemoved = m_targetMap.remove(targetIdentifier);
147     ASSERT_UNUSED(wasRemoved, wasRemoved);
148
149     // The listing may never have been added if remote control isn't allowed.
150     m_targetListingMap.remove(targetIdentifier);
151
152     if (auto connectionToTarget = m_targetConnectionMap.take(targetIdentifier))
153         connectionToTarget->targetClosed();
154
155     pushListingsSoon();
156 }
157
158 void RemoteInspector::updateTarget(RemoteControllableTarget* target)
159 {
160     ASSERT_ARG(target, target);
161
162     std::lock_guard<Lock> lock(m_mutex);
163
164     unsigned targetIdentifier = target->targetIdentifier();
165     if (!targetIdentifier)
166         return;
167
168     {
169         auto result = m_targetMap.set(targetIdentifier, target);
170         ASSERT_UNUSED(result, !result.isNewEntry);
171     }
172
173     // If the target has just allowed remote control, then the listing won't exist yet.
174     // If the target has no identifier remove the old listing.
175     if (RetainPtr<NSDictionary> targetListing = listingForTarget(*target))
176         m_targetListingMap.set(targetIdentifier, targetListing);
177     else
178         m_targetListingMap.remove(targetIdentifier);
179
180     pushListingsSoon();
181 }
182
183 void RemoteInspector::updateAutomaticInspectionCandidate(RemoteInspectionTarget* target)
184 {
185     ASSERT_ARG(target, target);
186     {
187         std::lock_guard<Lock> lock(m_mutex);
188
189         unsigned targetIdentifier = target->targetIdentifier();
190         if (!targetIdentifier)
191             return;
192
193         auto result = m_targetMap.set(targetIdentifier, target);
194         ASSERT_UNUSED(result, !result.isNewEntry);
195
196         // If the target has just allowed remote control, then the listing won't exist yet.
197         // If the target has no identifier remove the old listing.
198         if (RetainPtr<NSDictionary> targetListing = listingForTarget(*target))
199             m_targetListingMap.set(targetIdentifier, targetListing);
200         else
201             m_targetListingMap.remove(targetIdentifier);
202
203         // Don't allow automatic inspection unless it is allowed or we are stopped.
204         if (!m_automaticInspectionEnabled || !m_enabled) {
205             pushListingsSoon();
206             return;
207         }
208
209         // FIXME: We should handle multiple debuggables trying to pause at the same time on different threads.
210         // To make this work we will need to change m_automaticInspectionCandidateTargetIdentifier to be a per-thread value.
211         // Multiple attempts on the same thread should not be possible because our nested run loop is in a special RWI mode.
212         if (m_automaticInspectionPaused) {
213             LOG_ERROR("Skipping Automatic Inspection Candidate with pageId(%u) because we are already paused waiting for pageId(%u)", targetIdentifier, m_automaticInspectionCandidateTargetIdentifier);
214             pushListingsSoon();
215             return;
216         }
217
218         m_automaticInspectionPaused = true;
219         m_automaticInspectionCandidateTargetIdentifier = targetIdentifier;
220
221         // If we are pausing before we have connected to webinspectord the candidate message will be sent as soon as the connection is established.
222         if (m_relayConnection) {
223             pushListingsNow();
224             sendAutomaticInspectionCandidateMessage();
225         }
226
227         // In case debuggers fail to respond, or we cannot connect to webinspectord, automatically continue after a short period of time.
228 #if PLATFORM(WATCHOS)
229         int64_t debuggerTimeoutDelay = 5;
230 #else
231         int64_t debuggerTimeoutDelay = 1;
232 #endif
233         dispatch_after(dispatch_time(DISPATCH_TIME_NOW, debuggerTimeoutDelay * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
234             std::lock_guard<Lock> lock(m_mutex);
235             if (m_automaticInspectionCandidateTargetIdentifier == targetIdentifier) {
236                 LOG_ERROR("Skipping Automatic Inspection Candidate with pageId(%u) because we failed to receive a response in time.", m_automaticInspectionCandidateTargetIdentifier);
237                 m_automaticInspectionPaused = false;
238             }
239         });
240     }
241
242     target->pauseWaitingForAutomaticInspection();
243
244     {
245         std::lock_guard<Lock> lock(m_mutex);
246
247         ASSERT(m_automaticInspectionCandidateTargetIdentifier);
248         m_automaticInspectionCandidateTargetIdentifier = 0;
249     }
250 }
251
252 void RemoteInspector::updateClientCapabilities()
253 {
254     ASSERT(isMainThread());
255
256     std::lock_guard<Lock> lock(m_mutex);
257
258     if (!m_client)
259         m_clientCapabilities = std::nullopt;
260     else {
261         RemoteInspector::Client::Capabilities updatedCapabilities = {
262             m_client->remoteAutomationAllowed() // remoteAutomationAllowed
263         };
264
265         m_clientCapabilities = updatedCapabilities;
266     }
267 }
268
269 void RemoteInspector::setRemoteInspectorClient(RemoteInspector::Client* client)
270 {
271     ASSERT_ARG(client, client);
272     ASSERT(!m_client);
273
274     {
275         std::lock_guard<Lock> lock(m_mutex);
276         m_client = client;
277     }
278
279     // Send an updated listing that includes whether the client allows remote automation.
280     updateClientCapabilities();
281     pushListingsSoon();
282 }
283
284 void RemoteInspector::sendAutomaticInspectionCandidateMessage()
285 {
286     ASSERT(m_enabled);
287     ASSERT(m_automaticInspectionEnabled);
288     ASSERT(m_automaticInspectionPaused);
289     ASSERT(m_automaticInspectionCandidateTargetIdentifier);
290     ASSERT(m_relayConnection);
291
292     NSDictionary *details = @{WIRTargetIdentifierKey: @(m_automaticInspectionCandidateTargetIdentifier)};
293     m_relayConnection->sendMessage(WIRAutomaticInspectionCandidateMessage, details);
294 }
295
296 void RemoteInspector::sendMessageToRemote(unsigned targetIdentifier, const String& message)
297 {
298     std::lock_guard<Lock> lock(m_mutex);
299
300     if (!m_relayConnection)
301         return;
302
303     auto targetConnection = m_targetConnectionMap.get(targetIdentifier);
304     if (!targetConnection)
305         return;
306
307     NSDictionary *userInfo = @{
308         WIRRawDataKey: [static_cast<NSString *>(message) dataUsingEncoding:NSUTF8StringEncoding],
309         WIRConnectionIdentifierKey: targetConnection->connectionIdentifier(),
310         WIRDestinationKey: targetConnection->destination()
311     };
312
313     m_relayConnection->sendMessage(WIRRawDataMessage, userInfo);
314 }
315
316 void RemoteInspector::setupFailed(unsigned targetIdentifier)
317 {
318     std::lock_guard<Lock> lock(m_mutex);
319
320     m_targetConnectionMap.remove(targetIdentifier);
321
322     updateHasActiveDebugSession();
323
324     if (targetIdentifier == m_automaticInspectionCandidateTargetIdentifier)
325         m_automaticInspectionPaused = false;
326
327     pushListingsSoon();
328 }
329
330 void RemoteInspector::setupCompleted(unsigned targetIdentifier)
331 {
332     std::lock_guard<Lock> lock(m_mutex);
333
334     if (targetIdentifier == m_automaticInspectionCandidateTargetIdentifier)
335         m_automaticInspectionPaused = false;
336 }
337
338 bool RemoteInspector::waitingForAutomaticInspection(unsigned)
339 {
340     // We don't take the lock to check this because we assume it will be checked repeatedly.
341     return m_automaticInspectionPaused;
342 }
343
344 void RemoteInspector::clientCapabilitiesDidChange()
345 {
346     updateClientCapabilities();
347     pushListingsSoon();
348 }
349
350 void RemoteInspector::start()
351 {
352     std::lock_guard<Lock> lock(m_mutex);
353
354     if (m_enabled)
355         return;
356
357     m_enabled = true;
358
359     // Load the initial automatic inspection state when first started, so we know it before we have even connected to webinspectord.
360     static dispatch_once_t once;
361     dispatch_once(&once, ^{
362         m_automaticInspectionEnabled = globalAutomaticInspectionState();
363     });
364
365     notify_register_dispatch(WIRServiceAvailableNotification, &m_notifyToken, m_xpcQueue, ^(int) {
366         RemoteInspector::singleton().setupXPCConnectionIfNeeded();
367     });
368
369     notify_post(WIRServiceAvailabilityCheckNotification);
370 }
371
372 void RemoteInspector::stop()
373 {
374     std::lock_guard<Lock> lock(m_mutex);
375
376     stopInternal(StopSource::API);
377 }
378
379 void RemoteInspector::stopInternal(StopSource source)
380 {
381     if (!m_enabled)
382         return;
383
384     m_enabled = false;
385
386     m_pushScheduled = false;
387
388     for (auto targetConnection : m_targetConnectionMap.values())
389         targetConnection->close();
390     m_targetConnectionMap.clear();
391
392     updateHasActiveDebugSession();
393
394     m_automaticInspectionPaused = false;
395
396     if (m_relayConnection) {
397         switch (source) {
398         case StopSource::API:
399             m_relayConnection->close();
400             break;
401         case StopSource::XPCMessage:
402             m_relayConnection->closeFromMessage();
403             break;
404         }
405
406         m_relayConnection = nullptr;
407     }
408
409     notify_cancel(m_notifyToken);
410 }
411
412 void RemoteInspector::setupXPCConnectionIfNeeded()
413 {
414     std::lock_guard<Lock> lock(m_mutex);
415
416     if (m_relayConnection)
417         return;
418
419     xpc_connection_t connection = xpc_connection_create_mach_service(WIRXPCMachPortName, m_xpcQueue, 0);
420     if (!connection)
421         return;
422
423     m_relayConnection = adoptRef(new RemoteInspectorXPCConnection(connection, m_xpcQueue, this));
424     m_relayConnection->sendMessage(@"syn", nil); // Send a simple message to initialize the XPC connection.
425     xpc_release(connection);
426
427     if (m_automaticInspectionCandidateTargetIdentifier) {
428         // We already have a debuggable waiting to be automatically inspected.
429         pushListingsNow();
430         sendAutomaticInspectionCandidateMessage();
431     } else
432         pushListingsSoon();
433 }
434
435 #pragma mark - Proxy Application Information
436
437 void RemoteInspector::setParentProcessInformation(pid_t pid, RetainPtr<CFDataRef> auditData)
438 {
439     std::lock_guard<Lock> lock(m_mutex);
440
441     if (m_parentProcessIdentifier || m_parentProcessAuditData)
442         return;
443
444     m_parentProcessIdentifier = pid;
445     m_parentProcessAuditData = auditData;
446
447     if (m_shouldSendParentProcessInformation)
448         receivedProxyApplicationSetupMessage(nil);
449 }
450
451 #pragma mark - RemoteInspectorXPCConnection::Client
452
453 void RemoteInspector::xpcConnectionReceivedMessage(RemoteInspectorXPCConnection*, NSString *messageName, NSDictionary *userInfo)
454 {
455     std::lock_guard<Lock> lock(m_mutex);
456
457     if ([messageName isEqualToString:WIRPermissionDenied]) {
458         stopInternal(StopSource::XPCMessage);
459         return;
460     }
461
462     if ([messageName isEqualToString:WIRSocketDataMessage])
463         receivedDataMessage(userInfo);
464     else if ([messageName isEqualToString:WIRSocketSetupMessage])
465         receivedSetupMessage(userInfo);
466     else if ([messageName isEqualToString:WIRWebPageCloseMessage])
467         receivedDidCloseMessage(userInfo);
468     else if ([messageName isEqualToString:WIRApplicationGetListingMessage])
469         receivedGetListingMessage(userInfo);
470     else if ([messageName isEqualToString:WIRIndicateMessage])
471         receivedIndicateMessage(userInfo);
472     else if ([messageName isEqualToString:WIRProxyApplicationSetupMessage])
473         receivedProxyApplicationSetupMessage(userInfo);
474     else if ([messageName isEqualToString:WIRConnectionDiedMessage])
475         receivedConnectionDiedMessage(userInfo);
476     else if ([messageName isEqualToString:WIRAutomaticInspectionConfigurationMessage])
477         receivedAutomaticInspectionConfigurationMessage(userInfo);
478     else if ([messageName isEqualToString:WIRAutomaticInspectionRejectMessage])
479         receivedAutomaticInspectionRejectMessage(userInfo);
480     else if ([messageName isEqualToString:WIRAutomationSessionRequestMessage])
481         receivedAutomationSessionRequestMessage(userInfo);
482     else
483         NSLog(@"Unrecognized RemoteInspector XPC Message: %@", messageName);
484 }
485
486 void RemoteInspector::xpcConnectionFailed(RemoteInspectorXPCConnection* relayConnection)
487 {
488     std::lock_guard<Lock> lock(m_mutex);
489
490     ASSERT(relayConnection == m_relayConnection);
491     if (relayConnection != m_relayConnection)
492         return;
493
494     m_pushScheduled = false;
495
496     for (auto targetConnection : m_targetConnectionMap.values())
497         targetConnection->close();
498     m_targetConnectionMap.clear();
499
500     updateHasActiveDebugSession();
501
502     m_automaticInspectionPaused = false;
503
504     // The XPC connection will close itself.
505     m_relayConnection = nullptr;
506 }
507
508 void RemoteInspector::xpcConnectionUnhandledMessage(RemoteInspectorXPCConnection*, xpc_object_t)
509 {
510     // Intentionally ignored.
511 }
512
513 #pragma mark - Listings
514
515 RetainPtr<NSDictionary> RemoteInspector::listingForTarget(const RemoteControllableTarget& target) const
516 {
517     if (is<RemoteInspectionTarget>(target))
518         return listingForInspectionTarget(downcast<RemoteInspectionTarget>(target));
519     if (is<RemoteAutomationTarget>(target))
520         return listingForAutomationTarget(downcast<RemoteAutomationTarget>(target));
521
522     ASSERT_NOT_REACHED();
523     return nil;
524 }
525
526 RetainPtr<NSDictionary> RemoteInspector::listingForInspectionTarget(const RemoteInspectionTarget& target) const
527 {
528     // Must collect target information on the WebThread, Main, or Worker thread since RemoteTargets are
529     // implemented by non-threadsafe JSC / WebCore classes such as JSGlobalObject or WebCore::Page.
530
531     if (!target.remoteDebuggingAllowed())
532         return nil;
533
534     RetainPtr<NSMutableDictionary> listing = adoptNS([[NSMutableDictionary alloc] init]);
535     [listing setObject:@(target.targetIdentifier()) forKey:WIRTargetIdentifierKey];
536
537     switch (target.type()) {
538     case RemoteInspectionTarget::Type::JavaScript:
539         [listing setObject:target.name() forKey:WIRTitleKey];
540         [listing setObject:WIRTypeJavaScript forKey:WIRTypeKey];
541         break;
542     case RemoteInspectionTarget::Type::Web:
543         [listing setObject:target.url() forKey:WIRURLKey];
544         [listing setObject:target.name() forKey:WIRTitleKey];
545         [listing setObject:WIRTypeWeb forKey:WIRTypeKey];
546         break;
547     default:
548         ASSERT_NOT_REACHED();
549         break;
550     }
551
552     if (auto* connectionToTarget = m_targetConnectionMap.get(target.targetIdentifier()))
553         [listing setObject:connectionToTarget->connectionIdentifier() forKey:WIRConnectionIdentifierKey];
554
555     if (target.hasLocalDebugger())
556         [listing setObject:@YES forKey:WIRHasLocalDebuggerKey];
557
558     return listing;
559 }
560
561 RetainPtr<NSDictionary> RemoteInspector::listingForAutomationTarget(const RemoteAutomationTarget& target) const
562 {
563     // Must collect target information on the WebThread or Main thread since RemoteTargets are
564     // implemented by non-threadsafe JSC / WebCore classes such as JSGlobalObject or WebCore::Page.
565     ASSERT(isMainThread());
566
567     RetainPtr<NSMutableDictionary> listing = adoptNS([[NSMutableDictionary alloc] init]);
568     [listing setObject:@(target.targetIdentifier()) forKey:WIRTargetIdentifierKey];
569     [listing setObject:target.name() forKey:WIRSessionIdentifierKey];
570     [listing setObject:WIRTypeAutomation forKey:WIRTypeKey];
571     [listing setObject:@(target.isPaired()) forKey:WIRAutomationTargetIsPairedKey];
572
573     if (auto connectionToTarget = m_targetConnectionMap.get(target.targetIdentifier()))
574         [listing setObject:connectionToTarget->connectionIdentifier() forKey:WIRConnectionIdentifierKey];
575
576     return listing;
577 }
578
579 void RemoteInspector::pushListingsNow()
580 {
581     ASSERT(m_relayConnection);
582     if (!m_relayConnection)
583         return;
584
585     m_pushScheduled = false;
586
587     RetainPtr<NSMutableDictionary> listings = adoptNS([[NSMutableDictionary alloc] init]);
588     for (RetainPtr<NSDictionary> listing : m_targetListingMap.values()) {
589         NSString *targetIdentifierString = [[listing.get() objectForKey:WIRTargetIdentifierKey] stringValue];
590         [listings setObject:listing.get() forKey:targetIdentifierString];
591     }
592
593     RetainPtr<NSMutableDictionary> message = adoptNS([[NSMutableDictionary alloc] init]);
594     [message setObject:listings.get() forKey:WIRListingKey];
595
596     BOOL isAllowed = m_clientCapabilities && m_clientCapabilities->remoteAutomationAllowed;
597     [message setObject:@(isAllowed) forKey:WIRRemoteAutomationEnabledKey];
598
599     m_relayConnection->sendMessage(WIRListingMessage, message.get());
600 }
601
602 void RemoteInspector::pushListingsSoon()
603 {
604     if (!m_relayConnection)
605         return;
606
607     if (m_pushScheduled)
608         return;
609
610     m_pushScheduled = true;
611     dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.2 * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
612         std::lock_guard<Lock> lock(m_mutex);
613         if (m_pushScheduled)
614             pushListingsNow();
615     });
616 }
617
618 #pragma mark - Active Debugger Sessions
619
620 void RemoteInspector::updateHasActiveDebugSession()
621 {
622     bool hasActiveDebuggerSession = !m_targetConnectionMap.isEmpty();
623     if (hasActiveDebuggerSession == m_hasActiveDebugSession)
624         return;
625
626     m_hasActiveDebugSession = hasActiveDebuggerSession;
627
628     // FIXME: Expose some way to access this state in an embedder.
629     // Legacy iOS WebKit 1 had a notification. This will need to be smarter with WebKit2.
630 }
631
632
633 #pragma mark - Received XPC Messages
634
635 void RemoteInspector::receivedSetupMessage(NSDictionary *userInfo)
636 {
637     unsigned targetIdentifier = [[userInfo objectForKey:WIRTargetIdentifierKey] unsignedIntegerValue];
638     if (!targetIdentifier)
639         return;
640
641     NSString *connectionIdentifier = [userInfo objectForKey:WIRConnectionIdentifierKey];
642     if (!connectionIdentifier)
643         return;
644
645     NSString *sender = [userInfo objectForKey:WIRSenderKey];
646     if (!sender)
647         return;
648
649     if (m_targetConnectionMap.contains(targetIdentifier))
650         return;
651
652     auto findResult = m_targetMap.find(targetIdentifier);
653     if (findResult == m_targetMap.end())
654         return;
655
656     // Attempt to create a connection. This may fail if the page already has an inspector or if it disallows inspection.
657     RemoteControllableTarget* target = findResult->value;
658     auto connectionToTarget = adoptRef(*new RemoteConnectionToTarget(target, connectionIdentifier, sender));
659
660     if (is<RemoteInspectionTarget>(target)) {
661         bool isAutomaticInspection = m_automaticInspectionCandidateTargetIdentifier == target->targetIdentifier();
662         bool automaticallyPause = [[userInfo objectForKey:WIRAutomaticallyPause] boolValue];
663
664         if (!connectionToTarget->setup(isAutomaticInspection, automaticallyPause)) {
665             connectionToTarget->close();
666             return;
667         }
668         m_targetConnectionMap.set(targetIdentifier, WTFMove(connectionToTarget));
669         updateHasActiveDebugSession();
670     } else if (is<RemoteAutomationTarget>(target)) {
671         if (!connectionToTarget->setup()) {
672             connectionToTarget->close();
673             return;
674         }
675         m_targetConnectionMap.set(targetIdentifier, WTFMove(connectionToTarget));
676         updateHasActiveDebugSession();
677     } else
678         ASSERT_NOT_REACHED();
679
680     pushListingsSoon();
681 }
682
683 void RemoteInspector::receivedDataMessage(NSDictionary *userInfo)
684 {
685     unsigned targetIdentifier = [[userInfo objectForKey:WIRTargetIdentifierKey] unsignedIntegerValue];
686     if (!targetIdentifier)
687         return;
688
689     auto connectionToTarget = m_targetConnectionMap.get(targetIdentifier);
690     if (!connectionToTarget)
691         return;
692
693     NSData *data = [userInfo objectForKey:WIRSocketDataKey];
694     RetainPtr<NSString> message = adoptNS([[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
695     connectionToTarget->sendMessageToTarget(message.get());
696 }
697
698 void RemoteInspector::receivedDidCloseMessage(NSDictionary *userInfo)
699 {
700     unsigned targetIdentifier = [[userInfo objectForKey:WIRTargetIdentifierKey] unsignedIntegerValue];
701     if (!targetIdentifier)
702         return;
703
704     NSString *connectionIdentifier = [userInfo objectForKey:WIRConnectionIdentifierKey];
705     if (!connectionIdentifier)
706         return;
707
708     auto connectionToTarget = m_targetConnectionMap.get(targetIdentifier);
709     if (!connectionToTarget)
710         return;
711
712     if (![connectionIdentifier isEqualToString:connectionToTarget->connectionIdentifier()])
713         return;
714
715     connectionToTarget->close();
716     m_targetConnectionMap.remove(targetIdentifier);
717
718     updateHasActiveDebugSession();
719
720     pushListingsSoon();
721 }
722
723 void RemoteInspector::receivedGetListingMessage(NSDictionary *)
724 {
725     pushListingsNow();
726 }
727
728 void RemoteInspector::receivedIndicateMessage(NSDictionary *userInfo)
729 {
730     unsigned identifier = [[userInfo objectForKey:WIRTargetIdentifierKey] unsignedIntegerValue];
731     if (!identifier)
732         return;
733
734     BOOL indicateEnabled = [[userInfo objectForKey:WIRIndicateEnabledKey] boolValue];
735
736     callOnWebThreadOrDispatchAsyncOnMainThread(^{
737         RemoteControllableTarget* target = nullptr;
738         {
739             std::lock_guard<Lock> lock(m_mutex);
740
741             auto findResult = m_targetMap.find(identifier);
742             if (findResult == m_targetMap.end())
743                 return;
744
745             target = findResult->value;
746         }
747         if (is<RemoteInspectionTarget>(target))
748             downcast<RemoteInspectionTarget>(target)->setIndicating(indicateEnabled);
749     });
750 }
751
752 void RemoteInspector::receivedProxyApplicationSetupMessage(NSDictionary *)
753 {
754     ASSERT(m_relayConnection);
755     if (!m_relayConnection)
756         return;
757
758     if (!m_parentProcessIdentifier || !m_parentProcessAuditData) {
759         // We are a proxy application without parent process information.
760         // Wait a bit for the information, but give up after a second.
761         m_shouldSendParentProcessInformation = true;
762         dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
763             std::lock_guard<Lock> lock(m_mutex);
764             if (m_shouldSendParentProcessInformation)
765                 stopInternal(StopSource::XPCMessage);
766         });
767         return;
768     }
769
770     m_shouldSendParentProcessInformation = false;
771
772     m_relayConnection->sendMessage(WIRProxyApplicationSetupResponseMessage, @{
773         WIRProxyApplicationParentPIDKey: @(m_parentProcessIdentifier),
774         WIRProxyApplicationParentAuditDataKey: (NSData *)m_parentProcessAuditData.get(),
775     });
776 }
777
778 void RemoteInspector::receivedConnectionDiedMessage(NSDictionary *userInfo)
779 {
780     NSString *connectionIdentifier = [userInfo objectForKey:WIRConnectionIdentifierKey];
781     if (!connectionIdentifier)
782         return;
783
784     auto it = m_targetConnectionMap.begin();
785     auto end = m_targetConnectionMap.end();
786     for (; it != end; ++it) {
787         if ([connectionIdentifier isEqualToString:it->value->connectionIdentifier()])
788             break;
789     }
790
791     if (it == end)
792         return;
793
794     auto connection = it->value;
795     connection->close();
796     m_targetConnectionMap.remove(it);
797
798     updateHasActiveDebugSession();
799 }
800
801 void RemoteInspector::receivedAutomaticInspectionConfigurationMessage(NSDictionary *userInfo)
802 {
803     m_automaticInspectionEnabled = [[userInfo objectForKey:WIRAutomaticInspectionEnabledKey] boolValue];
804
805     if (!m_automaticInspectionEnabled && m_automaticInspectionPaused)
806         m_automaticInspectionPaused = false;
807 }
808
809 void RemoteInspector::receivedAutomaticInspectionRejectMessage(NSDictionary *userInfo)
810 {
811     unsigned rejectionIdentifier = [[userInfo objectForKey:WIRTargetIdentifierKey] unsignedIntValue];
812
813     ASSERT(rejectionIdentifier == m_automaticInspectionCandidateTargetIdentifier);
814     if (rejectionIdentifier == m_automaticInspectionCandidateTargetIdentifier)
815         m_automaticInspectionPaused = false;
816 }
817
818 void RemoteInspector::receivedAutomationSessionRequestMessage(NSDictionary *userInfo)
819 {
820     if (!m_client)
821         return;
822
823     if (!m_clientCapabilities || !m_clientCapabilities->remoteAutomationAllowed)
824         return;
825
826     NSString *suggestedSessionIdentifier = [userInfo objectForKey:WIRSessionIdentifierKey];
827     if (!suggestedSessionIdentifier)
828         return;
829
830     m_client->requestAutomationSession(suggestedSessionIdentifier);
831 }
832
833 } // namespace Inspector
834
835 #endif // ENABLE(REMOTE_INSPECTOR)