2 * Copyright (C) 2013, 2014 Apple Inc. All Rights Reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #import "RemoteInspector.h"
29 #if ENABLE(REMOTE_INSPECTOR)
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>
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>
44 #if __has_include(<sandbox/private.h>)
45 #import <sandbox/private.h>
47 enum sandbox_filter_type {
48 SANDBOX_FILTER_GLOBAL_NAME = 2,
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;
57 static bool canAccessWebInspectorMachPort()
59 return sandbox_check(getpid(), "mach-lookup", static_cast<enum sandbox_filter_type>(SANDBOX_FILTER_GLOBAL_NAME | SANDBOX_CHECK_NO_REPORT), WIRXPCMachPortName) == 0;
62 static bool globalAutomaticInspectionState()
65 if (notify_register_check(WIRAutomaticInspectionEnabledState, &token) != NOTIFY_STATUS_OK)
68 uint64_t automaticInspectionEnabled = 0;
69 notify_get_state(token, &automaticInspectionEnabled);
70 return automaticInspectionEnabled == 1;
73 bool RemoteInspector::startEnabled = true;
75 void RemoteInspector::startDisabled()
77 RemoteInspector::startEnabled = false;
80 RemoteInspector& RemoteInspector::singleton()
82 static NeverDestroyed<RemoteInspector> shared;
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)
94 if ([NSThread isMainThread])
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);
108 RemoteInspector::RemoteInspector()
109 : m_xpcQueue(dispatch_queue_create("com.apple.JavaScriptCore.remote-inspector-xpc", DISPATCH_QUEUE_SERIAL))
110 , m_nextAvailableIdentifier(1)
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)
123 unsigned RemoteInspector::nextAvailableIdentifier()
125 unsigned nextValidIdentifier;
127 nextValidIdentifier = m_nextAvailableIdentifier++;
128 } while (!nextValidIdentifier || nextValidIdentifier == std::numeric_limits<unsigned>::max() || m_debuggableMap.contains(nextValidIdentifier));
129 return nextValidIdentifier;
132 void RemoteInspector::registerDebuggable(RemoteInspectorDebuggable* debuggable)
134 std::lock_guard<std::mutex> lock(m_mutex);
136 unsigned identifier = nextAvailableIdentifier();
137 debuggable->setIdentifier(identifier);
139 auto result = m_debuggableMap.set(identifier, std::make_pair(debuggable, debuggable->info()));
140 ASSERT_UNUSED(result, result.isNewEntry);
142 if (debuggable->remoteDebuggingAllowed())
146 void RemoteInspector::unregisterDebuggable(RemoteInspectorDebuggable* debuggable)
148 std::lock_guard<std::mutex> lock(m_mutex);
150 unsigned identifier = debuggable->identifier();
154 bool wasRemoved = m_debuggableMap.remove(identifier);
155 ASSERT_UNUSED(wasRemoved, wasRemoved);
157 if (RefPtr<RemoteInspectorDebuggableConnection> connection = m_connectionMap.take(identifier))
158 connection->closeFromDebuggable();
160 if (debuggable->remoteDebuggingAllowed())
164 void RemoteInspector::updateDebuggable(RemoteInspectorDebuggable* debuggable)
166 std::lock_guard<std::mutex> lock(m_mutex);
168 unsigned identifier = debuggable->identifier();
172 auto result = m_debuggableMap.set(identifier, std::make_pair(debuggable, debuggable->info()));
173 ASSERT_UNUSED(result, !result.isNewEntry);
178 void RemoteInspector::updateDebuggableAutomaticInspectCandidate(RemoteInspectorDebuggable* debuggable)
181 std::lock_guard<std::mutex> lock(m_mutex);
183 unsigned identifier = debuggable->identifier();
187 auto result = m_debuggableMap.set(identifier, std::make_pair(debuggable, debuggable->info()));
188 ASSERT_UNUSED(result, !result.isNewEntry);
190 // Don't allow automatic inspection unless it is allowed or we are stopped.
191 if (!m_automaticInspectionEnabled || !m_enabled) {
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);
205 m_automaticInspectionPaused = true;
206 m_automaticInspectionCandidateIdentifier = identifier;
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) {
211 sendAutomaticInspectionCandidateMessage();
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;
224 debuggable->pauseWaitingForAutomaticInspection();
227 std::lock_guard<std::mutex> lock(m_mutex);
229 ASSERT(m_automaticInspectionCandidateIdentifier);
230 m_automaticInspectionCandidateIdentifier = 0;
234 void RemoteInspector::sendAutomaticInspectionCandidateMessage()
237 ASSERT(m_automaticInspectionEnabled);
238 ASSERT(m_automaticInspectionPaused);
239 ASSERT(m_automaticInspectionCandidateIdentifier);
240 ASSERT(m_xpcConnection);
242 NSDictionary *details = @{WIRPageIdentifierKey: @(m_automaticInspectionCandidateIdentifier)};
243 m_xpcConnection->sendMessage(WIRAutomaticInspectionCandidateMessage, details);
246 void RemoteInspector::sendMessageToRemoteFrontend(unsigned identifier, const String& message)
248 std::lock_guard<std::mutex> lock(m_mutex);
250 if (!m_xpcConnection)
253 RefPtr<RemoteInspectorDebuggableConnection> connection = m_connectionMap.get(identifier);
257 NSDictionary *userInfo = @{
258 WIRRawDataKey: [static_cast<NSString *>(message) dataUsingEncoding:NSUTF8StringEncoding],
259 WIRConnectionIdentifierKey: connection->connectionIdentifier(),
260 WIRDestinationKey: connection->destination()
263 m_xpcConnection->sendMessage(WIRRawDataMessage, userInfo);
266 void RemoteInspector::setupFailed(unsigned identifier)
268 std::lock_guard<std::mutex> lock(m_mutex);
270 m_connectionMap.remove(identifier);
272 updateHasActiveDebugSession();
274 if (identifier == m_automaticInspectionCandidateIdentifier)
275 m_automaticInspectionPaused = false;
280 void RemoteInspector::setupCompleted(unsigned identifier)
282 std::lock_guard<std::mutex> lock(m_mutex);
284 if (identifier == m_automaticInspectionCandidateIdentifier)
285 m_automaticInspectionPaused = false;
288 bool RemoteInspector::waitingForAutomaticInspection(unsigned)
290 // We don't take the lock to check this because we assume it will be checked repeatedly.
291 return m_automaticInspectionPaused;
294 void RemoteInspector::start()
296 std::lock_guard<std::mutex> lock(m_mutex);
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();
309 notify_register_dispatch(WIRServiceAvailableNotification, &m_notifyToken, m_xpcQueue, ^(int) {
310 RemoteInspector::singleton().setupXPCConnectionIfNeeded();
313 notify_post(WIRServiceAvailabilityCheckNotification);
316 void RemoteInspector::stop()
318 std::lock_guard<std::mutex> lock(m_mutex);
320 stopInternal(StopSource::API);
323 void RemoteInspector::stopInternal(StopSource source)
330 m_pushScheduled = false;
332 for (auto it = m_connectionMap.begin(), end = m_connectionMap.end(); it != end; ++it)
334 m_connectionMap.clear();
336 updateHasActiveDebugSession();
338 m_automaticInspectionPaused = false;
340 if (m_xpcConnection) {
342 case StopSource::API:
343 m_xpcConnection->close();
345 case StopSource::XPCMessage:
346 m_xpcConnection->closeFromMessage();
350 m_xpcConnection = nullptr;
353 notify_cancel(m_notifyToken);
356 void RemoteInspector::setupXPCConnectionIfNeeded()
358 std::lock_guard<std::mutex> lock(m_mutex);
363 xpc_connection_t connection = xpc_connection_create_mach_service(WIRXPCMachPortName, m_xpcQueue, 0);
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);
371 if (m_automaticInspectionCandidateIdentifier) {
372 // We already have a debuggable waiting to be automatically inspected.
374 sendAutomaticInspectionCandidateMessage();
379 #pragma mark - Proxy Application Information
381 void RemoteInspector::setParentProcessInformation(pid_t pid, RetainPtr<CFDataRef> auditData)
383 std::lock_guard<std::mutex> lock(m_mutex);
385 if (m_parentProcessIdentifier || m_parentProcessAuditData)
388 m_parentProcessIdentifier = pid;
389 m_parentProcessAuditData = auditData;
391 if (m_shouldSendParentProcessInformation)
392 receivedProxyApplicationSetupMessage(nil);
395 #pragma mark - RemoteInspectorXPCConnection::Client
397 void RemoteInspector::xpcConnectionReceivedMessage(RemoteInspectorXPCConnection*, NSString *messageName, NSDictionary *userInfo)
399 std::lock_guard<std::mutex> lock(m_mutex);
401 if ([messageName isEqualToString:WIRPermissionDenied]) {
402 stopInternal(StopSource::XPCMessage);
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);
425 NSLog(@"Unrecognized RemoteInspector XPC Message: %@", messageName);
428 void RemoteInspector::xpcConnectionFailed(RemoteInspectorXPCConnection* connection)
430 std::lock_guard<std::mutex> lock(m_mutex);
432 ASSERT(connection == m_xpcConnection);
433 if (connection != m_xpcConnection)
436 m_pushScheduled = false;
438 for (auto it = m_connectionMap.begin(), end = m_connectionMap.end(); it != end; ++it)
440 m_connectionMap.clear();
442 updateHasActiveDebugSession();
444 m_automaticInspectionPaused = false;
446 // The connection will close itself.
447 m_xpcConnection = nullptr;
450 void RemoteInspector::xpcConnectionUnhandledMessage(RemoteInspectorXPCConnection*, xpc_object_t)
452 // Intentionally ignored.
455 #pragma mark - Listings
457 NSDictionary *RemoteInspector::listingForDebuggable(const RemoteInspectorDebuggableInfo& debuggableInfo) const
459 NSMutableDictionary *debuggableDetails = [NSMutableDictionary dictionary];
461 [debuggableDetails setObject:@(debuggableInfo.identifier) forKey:WIRPageIdentifierKey];
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];
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];
479 ASSERT_NOT_REACHED();
483 if (RefPtr<RemoteInspectorDebuggableConnection> connection = m_connectionMap.get(debuggableInfo.identifier))
484 [debuggableDetails setObject:connection->connectionIdentifier() forKey:WIRConnectionIdentifierKey];
486 if (debuggableInfo.hasLocalDebugger)
487 [debuggableDetails setObject:@YES forKey:WIRHasLocalDebuggerKey];
489 return debuggableDetails;
492 void RemoteInspector::pushListingNow()
494 ASSERT(m_xpcConnection);
495 if (!m_xpcConnection)
498 m_pushScheduled = false;
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]];
509 RetainPtr<NSMutableDictionary> outgoing = adoptNS([[NSMutableDictionary alloc] init]);
510 [outgoing setObject:response.get() forKey:WIRListingKey];
512 m_xpcConnection->sendMessage(WIRListingMessage, outgoing.get());
515 void RemoteInspector::pushListingSoon()
517 if (!m_xpcConnection)
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);
531 #pragma mark - Active Debugger Sessions
533 void RemoteInspector::updateHasActiveDebugSession()
535 bool hasActiveDebuggerSession = !m_connectionMap.isEmpty();
536 if (hasActiveDebuggerSession == m_hasActiveDebugSession)
539 m_hasActiveDebugSession = hasActiveDebuggerSession;
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.
546 #pragma mark - Received XPC Messages
548 void RemoteInspector::receivedSetupMessage(NSDictionary *userInfo)
550 NSNumber *pageId = [userInfo objectForKey:WIRPageIdentifierKey];
554 NSString *connectionIdentifier = [userInfo objectForKey:WIRConnectionIdentifierKey];
555 if (!connectionIdentifier)
558 NSString *sender = [userInfo objectForKey:WIRSenderKey];
562 unsigned identifier = [pageId unsignedIntValue];
563 if (m_connectionMap.contains(identifier))
566 auto it = m_debuggableMap.find(identifier);
567 if (it == m_debuggableMap.end())
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();
576 bool automaticallyPause = false;
577 NSNumber *automaticallyPauseObject = [userInfo objectForKey:WIRAutomaticallyPause];
578 if ([automaticallyPauseObject isKindOfClass:[NSNumber class]])
579 automaticallyPause = [automaticallyPauseObject boolValue];
581 if (!connection->setup(isAutomaticInspection, automaticallyPause)) {
586 m_connectionMap.set(identifier, connection.release());
588 updateHasActiveDebugSession();
593 void RemoteInspector::receivedDataMessage(NSDictionary *userInfo)
595 NSNumber *pageId = [userInfo objectForKey:WIRPageIdentifierKey];
599 unsigned pageIdentifier = [pageId unsignedIntValue];
600 RefPtr<RemoteInspectorDebuggableConnection> connection = m_connectionMap.get(pageIdentifier);
604 NSData *data = [userInfo objectForKey:WIRSocketDataKey];
605 RetainPtr<NSString> message = adoptNS([[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
606 connection->sendMessageToBackend(message.get());
609 void RemoteInspector::receivedDidCloseMessage(NSDictionary *userInfo)
611 NSNumber *pageId = [userInfo objectForKey:WIRPageIdentifierKey];
615 NSString *connectionIdentifier = [userInfo objectForKey:WIRConnectionIdentifierKey];
616 if (!connectionIdentifier)
619 unsigned identifier = [pageId unsignedIntValue];
620 RefPtr<RemoteInspectorDebuggableConnection> connection = m_connectionMap.get(identifier);
624 if (![connectionIdentifier isEqualToString:connection->connectionIdentifier()])
628 m_connectionMap.remove(identifier);
630 updateHasActiveDebugSession();
635 void RemoteInspector::receivedGetListingMessage(NSDictionary *)
640 void RemoteInspector::receivedIndicateMessage(NSDictionary *userInfo)
642 NSNumber *pageId = [userInfo objectForKey:WIRPageIdentifierKey];
646 unsigned identifier = [pageId unsignedIntValue];
647 BOOL indicateEnabled = [[userInfo objectForKey:WIRIndicateEnabledKey] boolValue];
649 callOnWebThreadOrDispatchAsyncOnMainThread(^{
650 RemoteInspectorDebuggable* debuggable = nullptr;
652 std::lock_guard<std::mutex> lock(m_mutex);
654 auto it = m_debuggableMap.find(identifier);
655 if (it == m_debuggableMap.end())
658 debuggable = it->value.first;
660 debuggable->setIndicating(indicateEnabled);
664 void RemoteInspector::receivedProxyApplicationSetupMessage(NSDictionary *)
666 ASSERT(m_xpcConnection);
667 if (!m_xpcConnection)
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);
682 m_shouldSendParentProcessInformation = false;
684 m_xpcConnection->sendMessage(WIRProxyApplicationSetupResponseMessage, @{
685 WIRProxyApplicationParentPIDKey: @(m_parentProcessIdentifier),
686 WIRProxyApplicationParentAuditDataKey: (NSData *)m_parentProcessAuditData.get(),
690 void RemoteInspector::receivedConnectionDiedMessage(NSDictionary *userInfo)
692 NSString *connectionIdentifier = [userInfo objectForKey:WIRConnectionIdentifierKey];
693 if (!connectionIdentifier)
696 auto it = m_connectionMap.begin();
697 auto end = m_connectionMap.end();
698 for (; it != end; ++it) {
699 if ([connectionIdentifier isEqualToString:it->value->connectionIdentifier()])
706 RefPtr<RemoteInspectorDebuggableConnection> connection = it->value;
708 m_connectionMap.remove(it);
710 updateHasActiveDebugSession();
713 void RemoteInspector::receivedAutomaticInspectionConfigurationMessage(NSDictionary *userInfo)
715 m_automaticInspectionEnabled = [[userInfo objectForKey:WIRAutomaticInspectionEnabledKey] boolValue];
717 if (!m_automaticInspectionEnabled && m_automaticInspectionPaused)
718 m_automaticInspectionPaused = false;
721 void RemoteInspector::receivedAutomaticInspectionRejectMessage(NSDictionary *userInfo)
723 unsigned rejectionIdentifier = [[userInfo objectForKey:WIRPageIdentifierKey] unsignedIntValue];
725 ASSERT(rejectionIdentifier == m_automaticInspectionCandidateIdentifier);
726 if (rejectionIdentifier == m_automaticInspectionCandidateIdentifier)
727 m_automaticInspectionPaused = false;
730 } // namespace Inspector
732 #endif // ENABLE(REMOTE_INSPECTOR)