Web Inspector: Should be able to attach a debugger to a JSContext before anything...
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 23 Sep 2014 18:38:24 +0000 (18:38 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 23 Sep 2014 18:38:24 +0000 (18:38 +0000)
https://bugs.webkit.org/show_bug.cgi?id=136893

Patch by Joseph Pecoraro <pecoraro@apple.com> on 2014-09-23
Reviewed by Timothy Hatcher.

Source/JavaScriptCore:

Adds new remote inspector protocol handling for automatic inspection.
Debuggers can signal they have enabled automatic inspection, and
when debuggables are created the current application will pause to
see if the debugger will inspect or decline to inspect the debuggable.

* inspector/remote/RemoteInspectorConstants.h:
* inspector/remote/RemoteInspector.h:
* inspector/remote/RemoteInspector.mm:
(Inspector::globalAutomaticInspectionState):
(Inspector::RemoteInspector::RemoteInspector):
(Inspector::RemoteInspector::start):
When first starting, check the global "is there an auto-inspect" debugger state.
This is necessary so that the current application knows if it should pause or
not when a debuggable is created, even without having connected to webinspectord yet.

(Inspector::RemoteInspector::updateDebuggableAutomaticInspectCandidate):
When a debuggable has enabled remote inspection, take this path to propose
it as an automatic inspection candidate if there is an auto-inspect debugger.

(Inspector::RemoteInspector::sendAutomaticInspectionCandidateMessage):
Send the automatic inspection candidate message.

(Inspector::RemoteInspector::receivedSetupMessage):
(Inspector::RemoteInspector::setupFailed):
(Inspector::RemoteInspector::setupSucceeded):
After attempting to open an inspector, unpause if it was for the
automatic inspection candidate.

(Inspector::RemoteInspector::waitingForAutomaticInspection):
When running a nested runloop, check if we should remain paused.

(Inspector::RemoteInspector::setupXPCConnectionIfNeeded):
If by the time we connect to webinspectord we have a candidate, then
immediately send the candidate message.

(Inspector::RemoteInspector::stopInternal):
(Inspector::RemoteInspector::xpcConnectionFailed):
In error cases, clear our state.

(Inspector::RemoteInspector::xpcConnectionReceivedMessage):
(Inspector::RemoteInspector::receivedAutomaticInspectionConfigurationMessage):
(Inspector::RemoteInspector::receivedAutomaticInspectionRejectMessage):
Update state when receiving new messages.

* inspector/remote/RemoteInspectorDebuggable.h:
* inspector/remote/RemoteInspectorDebuggable.cpp:
(Inspector::RemoteInspectorDebuggable::setRemoteDebuggingAllowed):
Special case when a debuggable is newly allowed to be debuggable.

(Inspector::RemoteInspectorDebuggable::pauseWaitingForAutomaticInspection):
Run a nested run loop while this is an automatic inspection candidate.

* inspector/JSGlobalObjectInspectorController.h:
* inspector/JSGlobalObjectInspectorController.cpp:
(Inspector::JSGlobalObjectInspectorController::JSGlobalObjectInspectorController):
(Inspector::JSGlobalObjectInspectorController::connectFrontend):
When the inspector starts via automatic inspection automatically pause.
We plan on removing this condition by having the frontend signal to the
backend when it is completely initialized.

* inspector/remote/RemoteInspectorDebuggableConnection.h:
* inspector/remote/RemoteInspectorDebuggableConnection.mm:
(Inspector::RemoteInspectorDebuggableConnection::setup):
Pass on the flag of whether or not this was automatic inspection.

* runtime/JSGlobalObjectDebuggable.h:
* runtime/JSGlobalObjectDebuggable.cpp:
(JSC::JSGlobalObjectDebuggable::connect):
(JSC::JSGlobalObjectDebuggable::pauseWaitingForAutomaticInspection):
When pausing in a JSGlobalObject we need to release the API lock.

Source/WebCore:

Automatic inspection is currently disabled for web pages.
This just updates the interfaces that changed.

* WebCore.exp.in:
* inspector/InspectorController.cpp:
(WebCore::InspectorController::connectFrontend):
(WebCore::InspectorController::show):
* inspector/InspectorController.h:
* page/PageDebuggable.cpp:
(WebCore::PageDebuggable::connect):
* page/PageDebuggable.h:
* testing/Internals.cpp:
(WebCore::Internals::openDummyInspectorFrontend):

Source/WebKit:

* WebKit.vcxproj/WebKitExportGenerator/WebKitExports.def.in:

Source/WebKit2:

* WebProcess/WebPage/WebInspector.cpp:
(WebKit::WebInspector::remoteFrontendConnected):

Source/WTF:

Currently automatic inspection only happens in processes that have a
debugger attached. That condition may change in the future, but this
function can stand on its own in WTF. It may be useful in the future
to perhaps continue though ASSERTs if you have a debugger attached.

* wtf/Assertions.cpp:
* wtf/Assertions.h:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@173882 268f45cc-cd09-0410-ab3c-d52691b4dbfc

26 files changed:
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/inspector/JSGlobalObjectInspectorController.cpp
Source/JavaScriptCore/inspector/JSGlobalObjectInspectorController.h
Source/JavaScriptCore/inspector/remote/RemoteInspector.h
Source/JavaScriptCore/inspector/remote/RemoteInspector.mm
Source/JavaScriptCore/inspector/remote/RemoteInspectorConstants.h
Source/JavaScriptCore/inspector/remote/RemoteInspectorDebuggable.cpp
Source/JavaScriptCore/inspector/remote/RemoteInspectorDebuggable.h
Source/JavaScriptCore/inspector/remote/RemoteInspectorDebuggableConnection.h
Source/JavaScriptCore/inspector/remote/RemoteInspectorDebuggableConnection.mm
Source/JavaScriptCore/runtime/JSGlobalObjectDebuggable.cpp
Source/JavaScriptCore/runtime/JSGlobalObjectDebuggable.h
Source/WTF/ChangeLog
Source/WTF/wtf/Assertions.cpp
Source/WTF/wtf/Assertions.h
Source/WebCore/ChangeLog
Source/WebCore/WebCore.exp.in
Source/WebCore/inspector/InspectorController.cpp
Source/WebCore/inspector/InspectorController.h
Source/WebCore/page/PageDebuggable.cpp
Source/WebCore/page/PageDebuggable.h
Source/WebCore/testing/Internals.cpp
Source/WebKit/ChangeLog
Source/WebKit/WebKit.vcxproj/WebKitExportGenerator/WebKitExports.def.in
Source/WebKit2/ChangeLog
Source/WebKit2/WebProcess/WebPage/WebInspector.cpp

index 9d3949b..bae754f 100644 (file)
@@ -1,3 +1,82 @@
+2014-09-23  Joseph Pecoraro  <pecoraro@apple.com>
+
+        Web Inspector: Should be able to attach a debugger to a JSContext before anything is executed
+        https://bugs.webkit.org/show_bug.cgi?id=136893
+
+        Reviewed by Timothy Hatcher.
+
+        Adds new remote inspector protocol handling for automatic inspection.
+        Debuggers can signal they have enabled automatic inspection, and
+        when debuggables are created the current application will pause to
+        see if the debugger will inspect or decline to inspect the debuggable.
+
+        * inspector/remote/RemoteInspectorConstants.h:
+        * inspector/remote/RemoteInspector.h:
+        * inspector/remote/RemoteInspector.mm:
+        (Inspector::globalAutomaticInspectionState):
+        (Inspector::RemoteInspector::RemoteInspector):
+        (Inspector::RemoteInspector::start):
+        When first starting, check the global "is there an auto-inspect" debugger state.
+        This is necessary so that the current application knows if it should pause or
+        not when a debuggable is created, even without having connected to webinspectord yet.
+
+        (Inspector::RemoteInspector::updateDebuggableAutomaticInspectCandidate):
+        When a debuggable has enabled remote inspection, take this path to propose
+        it as an automatic inspection candidate if there is an auto-inspect debugger.
+
+        (Inspector::RemoteInspector::sendAutomaticInspectionCandidateMessage):
+        Send the automatic inspection candidate message.
+
+        (Inspector::RemoteInspector::receivedSetupMessage):
+        (Inspector::RemoteInspector::setupFailed):
+        (Inspector::RemoteInspector::setupSucceeded):
+        After attempting to open an inspector, unpause if it was for the
+        automatic inspection candidate.
+
+        (Inspector::RemoteInspector::waitingForAutomaticInspection):
+        When running a nested runloop, check if we should remain paused.
+
+        (Inspector::RemoteInspector::setupXPCConnectionIfNeeded):
+        If by the time we connect to webinspectord we have a candidate, then
+        immediately send the candidate message.
+
+        (Inspector::RemoteInspector::stopInternal):
+        (Inspector::RemoteInspector::xpcConnectionFailed):
+        In error cases, clear our state.
+
+        (Inspector::RemoteInspector::xpcConnectionReceivedMessage):
+        (Inspector::RemoteInspector::receivedAutomaticInspectionConfigurationMessage):
+        (Inspector::RemoteInspector::receivedAutomaticInspectionRejectMessage):
+        Update state when receiving new messages.
+
+
+        * inspector/remote/RemoteInspectorDebuggable.h:
+        * inspector/remote/RemoteInspectorDebuggable.cpp:
+        (Inspector::RemoteInspectorDebuggable::setRemoteDebuggingAllowed):
+        Special case when a debuggable is newly allowed to be debuggable.
+
+        (Inspector::RemoteInspectorDebuggable::pauseWaitingForAutomaticInspection):
+        Run a nested run loop while this is an automatic inspection candidate.
+
+        * inspector/JSGlobalObjectInspectorController.h:
+        * inspector/JSGlobalObjectInspectorController.cpp:
+        (Inspector::JSGlobalObjectInspectorController::JSGlobalObjectInspectorController):
+        (Inspector::JSGlobalObjectInspectorController::connectFrontend):
+        When the inspector starts via automatic inspection automatically pause.
+        We plan on removing this condition by having the frontend signal to the
+        backend when it is completely initialized.
+        
+        * inspector/remote/RemoteInspectorDebuggableConnection.h:
+        * inspector/remote/RemoteInspectorDebuggableConnection.mm:
+        (Inspector::RemoteInspectorDebuggableConnection::setup):
+        Pass on the flag of whether or not this was automatic inspection.
+
+        * runtime/JSGlobalObjectDebuggable.h:
+        * runtime/JSGlobalObjectDebuggable.cpp:
+        (JSC::JSGlobalObjectDebuggable::connect):
+        (JSC::JSGlobalObjectDebuggable::pauseWaitingForAutomaticInspection):
+        When pausing in a JSGlobalObject we need to release the API lock.
+
 2014-09-22  Filip Pizlo  <fpizlo@apple.com>
 
         FTL allocatePropertyStorage code should involve less copy-paste
index 8809e8a..c88a43c 100644 (file)
@@ -61,6 +61,7 @@ JSGlobalObjectInspectorController::JSGlobalObjectInspectorController(JSGlobalObj
     auto consoleAgent = std::make_unique<JSGlobalObjectConsoleAgent>(m_injectedScriptManager.get());
     auto debuggerAgent = std::make_unique<JSGlobalObjectDebuggerAgent>(m_injectedScriptManager.get(), m_globalObject, consoleAgent.get());
 
+    m_debuggerAgent = debuggerAgent.get();
     m_consoleAgent = consoleAgent.get();
     m_consoleClient = std::make_unique<JSGlobalObjectConsoleClient>(m_consoleAgent);
 
@@ -84,7 +85,7 @@ void JSGlobalObjectInspectorController::globalObjectDestroyed()
     m_injectedScriptManager->disconnect();
 }
 
-void JSGlobalObjectInspectorController::connectFrontend(InspectorFrontendChannel* frontendChannel)
+void JSGlobalObjectInspectorController::connectFrontend(InspectorFrontendChannel* frontendChannel, bool isAutomaticInspection)
 {
     ASSERT(!m_inspectorFrontendChannel);
     ASSERT(!m_inspectorBackendDispatcher);
@@ -93,6 +94,15 @@ void JSGlobalObjectInspectorController::connectFrontend(InspectorFrontendChannel
     m_inspectorBackendDispatcher = InspectorBackendDispatcher::create(frontendChannel);
 
     m_agents.didCreateFrontendAndBackend(frontendChannel, m_inspectorBackendDispatcher.get());
+
+    if (isAutomaticInspection) {
+        // FIXME: We should not always pause for automatic inspection.
+        // Currently if we don't automatically pause, then we may miss a breakpoint, since breakpoints
+        // come from the frontend and might be received after some evaluateScript message. We should
+        // have the frontend signal the backend when its setup messages are complete.
+        m_debuggerAgent->enable(nullptr);
+        m_debuggerAgent->pause(nullptr);
+    }
 }
 
 void JSGlobalObjectInspectorController::disconnectFrontend(InspectorDisconnectReason reason)
index 7605a5c..7fe1c93 100644 (file)
@@ -47,6 +47,7 @@ class InjectedScriptManager;
 class InspectorConsoleAgent;
 class InspectorBackendDispatcher;
 class InspectorConsoleAgent;
+class InspectorDebuggerAgent;
 class InspectorFrontendChannel;
 class JSGlobalObjectConsoleClient;
 class ScriptCallStack;
@@ -58,7 +59,7 @@ public:
     JSGlobalObjectInspectorController(JSC::JSGlobalObject&);
     ~JSGlobalObjectInspectorController();
 
-    void connectFrontend(InspectorFrontendChannel*);
+    void connectFrontend(InspectorFrontendChannel*, bool isAutomaticInspection);
     void disconnectFrontend(InspectorDisconnectReason reason);
     void dispatchMessageFromFrontend(const String&);
 
@@ -85,6 +86,7 @@ private:
     std::unique_ptr<InjectedScriptManager> m_injectedScriptManager;
     std::unique_ptr<JSGlobalObjectConsoleClient> m_consoleClient;
     InspectorConsoleAgent* m_consoleAgent;
+    InspectorDebuggerAgent* m_debuggerAgent;
     InspectorAgentRegistry m_agents;
     InspectorFrontendChannel* m_inspectorFrontendChannel;
     RefPtr<InspectorBackendDispatcher> m_inspectorBackendDispatcher;
index fb6a78a..bcd7808 100644 (file)
@@ -52,8 +52,11 @@ public:
     void registerDebuggable(RemoteInspectorDebuggable*);
     void unregisterDebuggable(RemoteInspectorDebuggable*);
     void updateDebuggable(RemoteInspectorDebuggable*);
+    void updateDebuggableAutomaticInspectCandidate(RemoteInspectorDebuggable*);
     void sendMessageToRemoteFrontend(unsigned identifier, const String& message);
     void setupFailed(unsigned identifier);
+    void setupSucceeded(unsigned identifier);
+    bool waitingForAutomaticInspection(unsigned identifier);
 
     bool enabled() const { return m_enabled; }
     bool hasActiveDebugSession() const { return m_hasActiveDebugSession; }
@@ -83,6 +86,8 @@ private:
 
     void updateHasActiveDebugSession();
 
+    void sendAutomaticInspectionCandidateMessage();
+
     virtual void xpcConnectionReceivedMessage(RemoteInspectorXPCConnection*, NSString *messageName, NSDictionary *userInfo) override;
     virtual void xpcConnectionFailed(RemoteInspectorXPCConnection*) override;
     virtual void xpcConnectionUnhandledMessage(RemoteInspectorXPCConnection*, xpc_object_t) override;
@@ -94,6 +99,8 @@ private:
     void receivedIndicateMessage(NSDictionary *userInfo);
     void receivedProxyApplicationSetupMessage(NSDictionary *userInfo);
     void receivedConnectionDiedMessage(NSDictionary *userInfo);
+    void receivedAutomaticInspectionConfigurationMessage(NSDictionary *userInfo);
+    void receivedAutomaticInspectionRejectMessage(NSDictionary *userInfo);
 
     static bool startEnabled;
 
@@ -117,6 +124,9 @@ private:
     pid_t m_parentProcessIdentifier;
     RetainPtr<CFDataRef> m_parentProcessAuditData;
     bool m_shouldSendParentProcessInformation;
+    bool m_automaticInspectionEnabled;
+    bool m_automaticInspectionPaused;
+    unsigned m_automaticInspectionCandidateIdentifier;
 };
 
 } // namespace Inspector
index 68faa82..6bf742c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 Apple Inc. All Rights Reserved.
+ * Copyright (C) 2013, 2014 Apple Inc. All Rights Reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -68,6 +68,17 @@ static bool canAccessWebInspectorMachPort()
     return sandbox_check(getpid(), "mach-lookup", SANDBOX_FILTER_GLOBAL_NAME, WIRXPCMachPortName) == 0;
 }
 
+static bool globalAutomaticInspectionState()
+{
+    int token = 0;
+    if (notify_register_check(WIRAutomaticInspectionEnabledState, &token) != NOTIFY_STATUS_OK)
+        return false;
+
+    uint64_t automaticInspectionEnabled = 0;
+    notify_get_state(token, &automaticInspectionEnabled);
+    return automaticInspectionEnabled == 1;
+}
+
 static void dispatchAsyncOnQueueSafeForAnyDebuggable(void (^block)())
 {
 #if PLATFORM(IOS)
@@ -112,6 +123,9 @@ RemoteInspector::RemoteInspector()
     , m_pushScheduled(false)
     , m_parentProcessIdentifier(0)
     , m_shouldSendParentProcessInformation(false)
+    , m_automaticInspectionEnabled(false)
+    , m_automaticInspectionPaused(false)
+    , m_automaticInspectionCandidateIdentifier(0)
 {
 }
 
@@ -170,6 +184,74 @@ void RemoteInspector::updateDebuggable(RemoteInspectorDebuggable* debuggable)
     pushListingSoon();
 }
 
+void RemoteInspector::updateDebuggableAutomaticInspectCandidate(RemoteInspectorDebuggable* debuggable)
+{
+    {
+        std::lock_guard<std::mutex> lock(m_mutex);
+
+        unsigned identifier = debuggable->identifier();
+        if (!identifier)
+            return;
+
+        auto result = m_debuggableMap.set(identifier, std::make_pair(debuggable, debuggable->info()));
+        ASSERT_UNUSED(result, !result.isNewEntry);
+
+        // Don't allow automatic inspection unless there is a debugger or we are stopped.
+        if (!WTFIsDebuggerAttached() || !m_automaticInspectionEnabled || !m_enabled) {
+            pushListingSoon();
+            return;
+        }
+
+        // FIXME: We should handle multiple debuggables trying to pause at the same time on different threads.
+        // To make this work we will need to change m_automaticInspectionCandidateIdentifier to be a per-thread value.
+        // Multiple attempts on the same thread should not be possible because our nested run loop is in a special RWI mode.
+        if (m_automaticInspectionPaused) {
+            LOG_ERROR("Skipping Automatic Inspection Candidate with pageId(%u) because we are already paused waiting for pageId(%u)", identifier, m_automaticInspectionCandidateIdentifier);
+            pushListingSoon();
+            return;
+        }
+
+        m_automaticInspectionPaused = true;
+        m_automaticInspectionCandidateIdentifier = identifier;
+
+        // If we are pausing before we have connected to webinspectord the candidate message will be sent as soon as the connection is established.
+        if (m_xpcConnection) {
+            pushListingNow();
+            sendAutomaticInspectionCandidateMessage();
+        }
+
+        // In case debuggers fail to respond, or we cannot connect to webinspectord, automatically continue after a short period of time.
+        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.3 * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+            std::lock_guard<std::mutex> lock(m_mutex);
+            if (m_automaticInspectionCandidateIdentifier == identifier) {
+                LOG_ERROR("Skipping Automatic Inspection Candidate with pageId(%u) because we failed to receive a response in time.", m_automaticInspectionCandidateIdentifier);
+                m_automaticInspectionPaused = false;
+            }
+        });
+    }
+
+    debuggable->pauseWaitingForAutomaticInspection();
+
+    {
+        std::lock_guard<std::mutex> lock(m_mutex);
+
+        ASSERT(m_automaticInspectionCandidateIdentifier);
+        m_automaticInspectionCandidateIdentifier = 0;
+    }
+}
+
+void RemoteInspector::sendAutomaticInspectionCandidateMessage()
+{
+    ASSERT(m_enabled);
+    ASSERT(m_automaticInspectionEnabled);
+    ASSERT(m_automaticInspectionPaused);
+    ASSERT(m_automaticInspectionCandidateIdentifier);
+    ASSERT(m_xpcConnection);
+
+    NSDictionary *details = @{WIRPageIdentifierKey: @(m_automaticInspectionCandidateIdentifier)};
+    m_xpcConnection->sendMessage(WIRAutomaticInspectionCandidateMessage, details);
+}
+
 void RemoteInspector::sendMessageToRemoteFrontend(unsigned identifier, const String& message)
 {
     std::lock_guard<std::mutex> lock(m_mutex);
@@ -198,9 +280,26 @@ void RemoteInspector::setupFailed(unsigned identifier)
 
     updateHasActiveDebugSession();
 
+    if (identifier == m_automaticInspectionCandidateIdentifier)
+        m_automaticInspectionPaused = false;
+
     pushListingSoon();
 }
 
+void RemoteInspector::setupSucceeded(unsigned identifier)
+{
+    std::lock_guard<std::mutex> lock(m_mutex);
+
+    if (identifier == m_automaticInspectionCandidateIdentifier)
+        m_automaticInspectionPaused = false;
+}
+
+bool RemoteInspector::waitingForAutomaticInspection(unsigned)
+{
+    // We don't take the lock to check this because we assume it will be checked repeatedly.
+    return m_automaticInspectionPaused;
+}
+
 void RemoteInspector::start()
 {
     std::lock_guard<std::mutex> lock(m_mutex);
@@ -210,6 +309,12 @@ void RemoteInspector::start()
 
     m_enabled = true;
 
+    // Load the initial automatic inspection state when first started, so we know it before we have even connected to webinspectord.
+    static dispatch_once_t once;
+    dispatch_once(&once, ^{
+        m_automaticInspectionEnabled = globalAutomaticInspectionState();
+    });
+
     notify_register_dispatch(WIRServiceAvailableNotification, &m_notifyToken, m_xpcQueue, ^(int) {
         RemoteInspector::shared().setupXPCConnectionIfNeeded();
     });
@@ -239,6 +344,8 @@ void RemoteInspector::stopInternal(StopSource source)
 
     updateHasActiveDebugSession();
 
+    m_automaticInspectionPaused = false;
+
     if (m_xpcConnection) {
         switch (source) {
         case StopSource::API:
@@ -270,7 +377,12 @@ void RemoteInspector::setupXPCConnectionIfNeeded()
     m_xpcConnection->sendMessage(@"syn", nil); // Send a simple message to initialize the XPC connection.
     xpc_release(connection);
 
-    pushListingSoon();
+    if (m_automaticInspectionCandidateIdentifier) {
+        // We already have a debuggable waiting to be automatically inspected.
+        pushListingNow();
+        sendAutomaticInspectionCandidateMessage();
+    } else
+        pushListingSoon();
 }
 
 #pragma mark - Proxy Application Information
@@ -314,6 +426,10 @@ void RemoteInspector::xpcConnectionReceivedMessage(RemoteInspectorXPCConnection*
         receivedProxyApplicationSetupMessage(userInfo);
     else if ([messageName isEqualToString:WIRConnectionDiedMessage])
         receivedConnectionDiedMessage(userInfo);
+    else if ([messageName isEqualToString:WIRAutomaticInspectionConfigurationMessage])
+        receivedAutomaticInspectionConfigurationMessage(userInfo);
+    else if ([messageName isEqualToString:WIRAutomaticInspectionRejectMessage])
+        receivedAutomaticInspectionRejectMessage(userInfo);
     else
         NSLog(@"Unrecognized RemoteInspector XPC Message: %@", messageName);
 }
@@ -334,6 +450,8 @@ void RemoteInspector::xpcConnectionFailed(RemoteInspectorXPCConnection* connecti
 
     updateHasActiveDebugSession();
 
+    m_automaticInspectionPaused = false;
+
     // The connection will close itself.
     m_xpcConnection = nullptr;
 }
@@ -433,6 +551,7 @@ void RemoteInspector::updateHasActiveDebugSession()
     // Legacy iOS WebKit 1 had a notification. This will need to be smarter with WebKit2.
 }
 
+
 #pragma mark - Received XPC Messages
 
 void RemoteInspector::receivedSetupMessage(NSDictionary *userInfo)
@@ -461,7 +580,8 @@ void RemoteInspector::receivedSetupMessage(NSDictionary *userInfo)
     RemoteInspectorDebuggable* debuggable = it->value.first;
     RemoteInspectorDebuggableInfo debuggableInfo = it->value.second;
     RefPtr<RemoteInspectorDebuggableConnection> connection = adoptRef(new RemoteInspectorDebuggableConnection(debuggable, connectionIdentifier, sender, debuggableInfo.type));
-    if (!connection->setup()) {
+    bool isAutomaticInspection = m_automaticInspectionCandidateIdentifier == debuggable->identifier();
+    if (!connection->setup(isAutomaticInspection)) {
         connection->close();
         return;
     }
@@ -593,6 +713,23 @@ void RemoteInspector::receivedConnectionDiedMessage(NSDictionary *userInfo)
     updateHasActiveDebugSession();
 }
 
+void RemoteInspector::receivedAutomaticInspectionConfigurationMessage(NSDictionary *userInfo)
+{
+    m_automaticInspectionEnabled = [[userInfo objectForKey:WIRAutomaticInspectionEnabledKey] boolValue];
+
+    if (!m_automaticInspectionEnabled && m_automaticInspectionPaused)
+        m_automaticInspectionPaused = false;
+}
+
+void RemoteInspector::receivedAutomaticInspectionRejectMessage(NSDictionary *userInfo)
+{
+    unsigned rejectionIdentifier = [[userInfo objectForKey:WIRPageIdentifierKey] unsignedIntValue];
+
+    ASSERT(rejectionIdentifier == m_automaticInspectionCandidateIdentifier);
+    if (rejectionIdentifier == m_automaticInspectionCandidateIdentifier)
+        m_automaticInspectionPaused = false;
+}
+
 } // namespace Inspector
 
 #endif // ENABLE(REMOTE_INSPECTOR)
index 647b22e..e3eb445 100644 (file)
 #define WIRServiceAvailabilityCheckNotification "com.apple.webinspectord.availability_check"
 #define WIRServiceEnabledNotification           "com.apple.webinspectord.enabled"
 #define WIRServiceDisabledNotification          "com.apple.webinspectord.disabled"
+#define WIRAutomaticInspectionEnabledState      "com.apple.webinspectord.automatic_inspection_enabled"
 
 
 #define WIRApplicationIdentifierKey             @"WIRApplicationIdentifierKey"
 #define WIRApplicationBundleIdentifierKey       @"WIRApplicationBundleIdentifierKey"
 #define WIRApplicationNameKey                   @"WIRApplicationNameKey"
 #define WIRIsApplicationProxyKey                @"WIRIsApplicationProxyKey"
+#define WIRIsApplicationActiveKey               @"WIRIsApplicationActiveKey"
 #define WIRHostApplicationIdentifierKey         @"WIRHostApplicationIdentifierKey"
 #define WIRHostApplicationNameKey               @"WIRHostApplicationNameKey"
 #define WIRConnectionIdentifierKey              @"WIRConnectionIdentifierKey"
 #define WIRTypeJavaScript                       @"WIRTypeJavaScript"
 #define WIRTypeWeb                              @"WIRTypeWeb"
 
+#define WIRAutomaticInspectionEnabledKey           @"WIRAutomaticInspectionEnabledKey"
+#define WIRAutomaticInspectionSessionIdentifierKey @"WIRAutomaticInspectionSessionIdentifierKey"
+#define WIRAutomaticInspectionConfigurationMessage @"WIRAutomaticInspectionConfigurationMessage"
+#define WIRAutomaticInspectionRejectMessage        @"WIRAutomaticInspectionRejectMessage"
+#define WIRAutomaticInspectionCandidateMessage     @"WIRAutomaticInspectionCandidateMessage"
+
 // These definitions are shared with a Simulator webinspectord and
 // OS X process communicating with it.
 
index fdf1d96..1f98a70 100644 (file)
@@ -28,6 +28,7 @@
 
 #if ENABLE(REMOTE_INSPECTOR)
 
+#include "EventLoop.h"
 #include "InspectorFrontendChannel.h"
 #include "RemoteInspector.h"
 
@@ -61,7 +62,10 @@ void RemoteInspectorDebuggable::setRemoteDebuggingAllowed(bool allowed)
 
     m_allowed = allowed;
 
-    update();
+    if (m_allowed && automaticInspectionAllowed())
+        RemoteInspector::shared().updateDebuggableAutomaticInspectCandidate(this);
+    else
+        RemoteInspector::shared().updateDebuggable(this);
 }
 
 RemoteInspectorDebuggableInfo RemoteInspectorDebuggable::info() const
@@ -76,6 +80,17 @@ RemoteInspectorDebuggableInfo RemoteInspectorDebuggable::info() const
     return info;
 }
 
+void RemoteInspectorDebuggable::pauseWaitingForAutomaticInspection()
+{
+    ASSERT(m_identifier);
+    ASSERT(m_allowed);
+    ASSERT(automaticInspectionAllowed());
+
+    EventLoop loop;
+    while (RemoteInspector::shared().waitingForAutomaticInspection(identifier()) && !loop.ended())
+        loop.cycle();
+}
+
 } // namespace Inspector
 
 #endif // ENABLE(REMOTE_INSPECTOR)
index d480968..39d50b6 100644 (file)
@@ -62,11 +62,14 @@ public:
     virtual String url() const { return String(); } // Web
     virtual bool hasLocalDebugger() const = 0;
 
-    virtual void connect(InspectorFrontendChannel*) = 0;
+    virtual void connect(InspectorFrontendChannel*, bool isAutomaticInspection) = 0;
     virtual void disconnect() = 0;
     virtual void dispatchMessageFromRemoteFrontend(const String& message) = 0;
     virtual void setIndicating(bool) { } // Default is to do nothing.
 
+    virtual bool automaticInspectionAllowed() const { return false; }
+    virtual void pauseWaitingForAutomaticInspection();
+
 private:
     unsigned m_identifier;
     bool m_allowed;
index 2bec437..15cbb2c 100644 (file)
@@ -84,7 +84,7 @@ public:
     NSString *connectionIdentifier() const;
     unsigned identifier() const { return m_identifier; }
 
-    bool setup();
+    bool setup(bool isAutomaticInspection);
 
     void close();
     void closeFromDebuggable();
index 0c3b908..5d9353e 100644 (file)
@@ -149,7 +149,7 @@ void RemoteInspectorDebuggableConnection::dispatchAsyncOnDebuggable(void (^block
     RemoteInspectorQueueTaskOnGlobalQueue(block);
 }
 
-bool RemoteInspectorDebuggableConnection::setup()
+bool RemoteInspectorDebuggableConnection::setup(bool isAutomaticInspection)
 {
     std::lock_guard<std::mutex> lock(m_debuggableMutex);
 
@@ -164,8 +164,9 @@ bool RemoteInspectorDebuggableConnection::setup()
                 RemoteInspector::shared().setupFailed(identifier());
                 m_debuggable = nullptr;
             } else {
-                m_debuggable->connect(this);
+                m_debuggable->connect(this, isAutomaticInspection);
                 m_connected = true;
+                RemoteInspector::shared().setupSucceeded(identifier());
             }
         }
         deref();
index 05e0d87..e428f45 100644 (file)
@@ -31,6 +31,7 @@
 #include "InspectorAgentBase.h"
 #include "InspectorFrontendChannel.h"
 #include "JSGlobalObject.h"
+#include "JSLock.h"
 #include "RemoteInspector.h"
 
 using namespace Inspector;
@@ -48,11 +49,11 @@ String JSGlobalObjectDebuggable::name() const
     return name.isEmpty() ? ASCIILiteral("JSContext") : name;
 }
 
-void JSGlobalObjectDebuggable::connect(InspectorFrontendChannel* frontendChannel)
+void JSGlobalObjectDebuggable::connect(InspectorFrontendChannel* frontendChannel, bool automaticInspection)
 {
     JSLockHolder locker(&m_globalObject.vm());
 
-    m_globalObject.inspectorController().connectFrontend(frontendChannel);
+    m_globalObject.inspectorController().connectFrontend(frontendChannel, automaticInspection);
 }
 
 void JSGlobalObjectDebuggable::disconnect()
@@ -69,6 +70,12 @@ void JSGlobalObjectDebuggable::dispatchMessageFromRemoteFrontend(const String& m
     m_globalObject.inspectorController().dispatchMessageFromFrontend(message);
 }
 
+void JSGlobalObjectDebuggable::pauseWaitingForAutomaticInspection()
+{
+    JSC::JSLock::DropAllLocks dropAllLocks(&m_globalObject.vm());
+    RemoteInspectorDebuggable::pauseWaitingForAutomaticInspection();
+}
+
 } // namespace JSC
 
 #endif // ENABLE(REMOTE_INSPECTOR)
index 9f4b6e8..8c6eab7 100644 (file)
@@ -51,10 +51,13 @@ public:
     virtual String name() const override;
     virtual bool hasLocalDebugger() const override { return false; }
 
-    virtual void connect(Inspector::InspectorFrontendChannel*) override;
+    virtual void connect(Inspector::InspectorFrontendChannel*, bool automaticInspection) override;
     virtual void disconnect() override;
     virtual void dispatchMessageFromRemoteFrontend(const String& message) override;
 
+    virtual bool automaticInspectionAllowed() const override { return true; }
+    virtual void pauseWaitingForAutomaticInspection() override;
+
 private:
     JSGlobalObject& m_globalObject;
 };
index 15f870f..ec8e261 100644 (file)
@@ -1,3 +1,18 @@
+2014-09-23  Joseph Pecoraro  <pecoraro@apple.com>
+
+        Web Inspector: Should be able to attach a debugger to a JSContext before anything is executed
+        https://bugs.webkit.org/show_bug.cgi?id=136893
+
+        Reviewed by Timothy Hatcher.
+
+        Currently automatic inspection only happens in processes that have a
+        debugger attached. That condition may change in the future, but this
+        function can stand on its own in WTF. It may be useful in the future
+        to perhaps continue though ASSERTs if you have a debugger attached.
+
+        * wtf/Assertions.cpp:
+        * wtf/Assertions.h:
+
 2014-09-22  Sam Weinig  <sam@webkit.org>
 
         Eliminate redundant PtrHash specializations
index 341bd01..ea5b9b3 100644 (file)
 #include <windows.h>
 #endif
 
+#if OS(DARWIN)
+#include <sys/sysctl.h>
+#include <unistd.h>
+#endif
+
 #if OS(DARWIN) || (OS(LINUX) && !defined(__UCLIBC__))
 #include <cxxabi.h>
 #include <dlfcn.h>
@@ -385,6 +390,20 @@ void WTFInstallReportBacktraceOnCrashHook()
 #endif
 }
 
+bool WTFIsDebuggerAttached()
+{
+#if OS(DARWIN)
+    struct kinfo_proc info;
+    int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid() };
+    size_t size = sizeof(info);
+    if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), &info, &size, nullptr, 0) == -1)
+        return false;
+    return info.kp_proc.p_flag & P_TRACED;
+#else
+    return false;
+#endif
+}
+
 void WTFReportFatalError(const char* file, int line, const char* function, const char* format, ...)
 {
     va_list args;
index f4c892a..8be094c 100644 (file)
@@ -38,6 +38,7 @@
 
 #include <inttypes.h>
 #include <stdarg.h>
+#include <stdbool.h>
 #include <stddef.h>
 
 #ifdef NDEBUG
@@ -145,6 +146,8 @@ typedef void (*WTFCrashHookFunction)();
 WTF_EXPORT_PRIVATE void WTFSetCrashHook(WTFCrashHookFunction);
 WTF_EXPORT_PRIVATE void WTFInstallReportBacktraceOnCrashHook();
 
+WTF_EXPORT_PRIVATE bool WTFIsDebuggerAttached();
+
 #ifdef __cplusplus
 }
 #endif
index 191183a..228174f 100644 (file)
@@ -1,3 +1,24 @@
+2014-09-23  Joseph Pecoraro  <pecoraro@apple.com>
+
+        Web Inspector: Should be able to attach a debugger to a JSContext before anything is executed
+        https://bugs.webkit.org/show_bug.cgi?id=136893
+
+        Reviewed by Timothy Hatcher.
+
+        Automatic inspection is currently disabled for web pages.
+        This just updates the interfaces that changed.
+
+        * WebCore.exp.in:
+        * inspector/InspectorController.cpp:
+        (WebCore::InspectorController::connectFrontend):
+        (WebCore::InspectorController::show):
+        * inspector/InspectorController.h:
+        * page/PageDebuggable.cpp:
+        (WebCore::PageDebuggable::connect):
+        * page/PageDebuggable.h:
+        * testing/Internals.cpp:
+        (WebCore::Internals::openDummyInspectorFrontend):
+
 2014-09-23  Eduardo Lima Mitev  <elima@igalia.com>
 
         [GTK] Adds implementation of subtle crypto HMAC algorithm
index af0d5ab..21729a9 100644 (file)
@@ -3122,7 +3122,7 @@ __ZN7WebCore5ColorC1ERKN3WTF6StringE
 #if ENABLE(INSPECTOR)
 __ZN7WebCore14SchemeRegistry27shouldTreatURLSchemeAsLocalERKN3WTF6StringE
 __ZN7WebCore15InspectorClient31doDispatchMessageOnFrontendPageEPNS_4PageERKN3WTF6StringE
-__ZN7WebCore19InspectorController15connectFrontendEPN9Inspector24InspectorFrontendChannelE
+__ZN7WebCore19InspectorController15connectFrontendEPN9Inspector24InspectorFrontendChannelEb
 __ZN7WebCore19InspectorController18disconnectFrontendEN9Inspector25InspectorDisconnectReasonE
 __ZN7WebCore19InspectorController18setProfilerEnabledEb
 __ZN7WebCore19InspectorController25evaluateForTestInFrontendERKN3WTF6StringE
index c7c8795..171a6a2 100644 (file)
@@ -230,7 +230,7 @@ void InspectorController::didClearWindowObjectInWorld(Frame* frame, DOMWrapperWo
         m_inspectorFrontendClient->windowObjectCleared();
 }
 
-void InspectorController::connectFrontend(InspectorFrontendChannel* frontendChannel)
+void InspectorController::connectFrontend(InspectorFrontendChannel* frontendChannel, bool)
 {
     ASSERT(frontendChannel);
     ASSERT(m_inspectorClient);
@@ -285,7 +285,7 @@ void InspectorController::show()
     else {
         InspectorFrontendChannel* frontendChannel = m_inspectorClient->openInspectorFrontend(this);
         if (frontendChannel)
-            connectFrontend(frontendChannel);
+            connectFrontend(frontendChannel, false);
     }
 }
 
index fd55924..12a1e6a 100644 (file)
@@ -93,7 +93,7 @@ public:
     bool hasLocalFrontend() const;
     bool hasRemoteFrontend() const;
 
-    WEBCORE_EXPORT void connectFrontend(Inspector::InspectorFrontendChannel*);
+    WEBCORE_EXPORT void connectFrontend(Inspector::InspectorFrontendChannel*, bool isAutomaticInspection);
     WEBCORE_EXPORT void disconnectFrontend(Inspector::InspectorDisconnectReason);
     void setProcessId(long);
 
index 678c5f2..5cf849a 100644 (file)
@@ -68,7 +68,7 @@ bool PageDebuggable::hasLocalDebugger() const
     return m_page.inspectorController().hasLocalFrontend();
 }
 
-void PageDebuggable::connect(Inspector::InspectorFrontendChannel* channel)
+void PageDebuggable::connect(Inspector::InspectorFrontendChannel* channel, bool isAutomaticInspection)
 {
     if (!m_page.settings().developerExtrasEnabled()) {
         m_forcedDeveloperExtrasEnabled = true;
@@ -78,7 +78,7 @@ void PageDebuggable::connect(Inspector::InspectorFrontendChannel* channel)
 
     InspectorController& inspectorController = m_page.inspectorController();
     inspectorController.setHasRemoteFrontend(true);
-    inspectorController.connectFrontend(reinterpret_cast<WebCore::InspectorFrontendChannel*>(channel));
+    inspectorController.connectFrontend(reinterpret_cast<WebCore::InspectorFrontendChannel*>(channel), isAutomaticInspection);
 }
 
 void PageDebuggable::disconnect()
index 673264c..41efbc7 100644 (file)
@@ -47,7 +47,7 @@ public:
     virtual String url() const override;
     virtual bool hasLocalDebugger() const override;
 
-    virtual void connect(Inspector::InspectorFrontendChannel*) override;
+    virtual void connect(Inspector::InspectorFrontendChannel*, bool isAutomaticInspection) override;
     virtual void disconnect() override;
     virtual void dispatchMessageFromRemoteFrontend(const String& message) override;
     virtual void setIndicating(bool) override;
index 0a0267a..df77d93 100644 (file)
@@ -1477,7 +1477,8 @@ PassRefPtr<DOMWindow> Internals::openDummyInspectorFrontend(const String& url)
 
     m_frontendChannel = adoptPtr(new InspectorFrontendChannelDummy(frontendPage));
 
-    page->inspectorController().connectFrontend(m_frontendChannel.get());
+    bool isAutomaticInspection = false;
+    page->inspectorController().connectFrontend(m_frontendChannel.get(), isAutomaticInspection);
 
     return m_frontendWindow;
 }
index bedb618..0bca520 100644 (file)
@@ -1,3 +1,12 @@
+2014-09-23  Joseph Pecoraro  <pecoraro@apple.com>
+
+        Web Inspector: Should be able to attach a debugger to a JSContext before anything is executed
+        https://bugs.webkit.org/show_bug.cgi?id=136893
+
+        Reviewed by Timothy Hatcher.
+
+        * WebKit.vcxproj/WebKitExportGenerator/WebKitExports.def.in:
+
 2014-09-23  Brent Fulgham  <bfulgham@apple.com>
 
         [Win] Correct 64-bit Windows export definitions after r173804.
index 0b40a90..b046186 100644 (file)
@@ -219,7 +219,7 @@ EXPORTS
         symbolWithPointer(??1InspectorFrontendClientLocal@WebCore@@UAE@XZ, ??1InspectorFrontendClientLocal@WebCore@@UEAA@XZ)
         symbolWithPointer(?changeAttachedWindowHeight@InspectorFrontendClientLocal@WebCore@@UAEXI@Z, ?changeAttachedWindowHeight@InspectorFrontendClientLocal@WebCore@@UEAAXI@Z)
         symbolWithPointer(?changeAttachedWindowWidth@InspectorFrontendClientLocal@WebCore@@UAEXI@Z, ?changeAttachedWindowWidth@InspectorFrontendClientLocal@WebCore@@UEAAXI@Z)
-        symbolWithPointer(?connectFrontend@InspectorController@WebCore@@QAEXPAVInspectorFrontendChannel@Inspector@@@Z, ?connectFrontend@InspectorController@WebCore@@QEAAXPEAVInspectorFrontendChannel@Inspector@@@Z)
+        symbolWithPointer(?connectFrontend@InspectorController@WebCore@@QAEXPAVInspectorFrontendChannel@Inspector@@_N@Z, ?connectFrontend@InspectorController@WebCore@@QAEXPAVInspectorFrontendChannel@Inspector@@_N@Z)
         symbolWithPointer(?doDispatchMessageOnFrontendPage@InspectorClient@WebCore@@SA_NPAVPage@2@ABVString@WTF@@@Z, ?doDispatchMessageOnFrontendPage@InspectorClient@WebCore@@SA_NPEAVPage@2@AEBVString@WTF@@@Z)
         symbolWithPointer(?frontendLoaded@InspectorFrontendClientLocal@WebCore@@UAEXXZ, ?frontendLoaded@InspectorFrontendClientLocal@WebCore@@UEAAXXZ)
         symbolWithPointer(?getProperty@Settings@InspectorFrontendClientLocal@WebCore@@UAE?AVString@WTF@@ABV45@@Z, ?getProperty@Settings@InspectorFrontendClientLocal@WebCore@@UEAA?AVString@WTF@@AEBV45@@Z)
index de46cde..9e3141a 100644 (file)
@@ -1,3 +1,13 @@
+2014-09-23  Joseph Pecoraro  <pecoraro@apple.com>
+
+        Web Inspector: Should be able to attach a debugger to a JSContext before anything is executed
+        https://bugs.webkit.org/show_bug.cgi?id=136893
+
+        Reviewed by Timothy Hatcher.
+
+        * WebProcess/WebPage/WebInspector.cpp:
+        (WebKit::WebInspector::remoteFrontendConnected):
+
 2014-09-22  Daniel Bates  <dabates@apple.com>
 
         [Cocoa] Add UI delegate callback when window.close() is called
index 561297a..df6110c 100644 (file)
@@ -331,8 +331,9 @@ void WebInspector::remoteFrontendConnected()
     ASSERT(!m_remoteFrontendConnected);
     // Switching between in-process and remote inspectors isn't supported yet.
     ASSERT(!m_inspectorPage);
-    
-    m_page->corePage()->inspectorController().connectFrontend(m_frontendChannel);
+
+    bool isAutomaticInspection = false;
+    m_page->corePage()->inspectorController().connectFrontend(m_frontendChannel, isAutomaticInspection);
     m_remoteFrontendConnected = true;
 }