blob: 3c628257c287bed949d93af4e23cc8156b4e9ea9 [file] [log] [blame]
/*
* Copyright (C) 2013, 2015 Apple Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "config.h"
#import "RemoteConnectionToTarget.h"
#if ENABLE(REMOTE_INSPECTOR)
#import "JSGlobalObjectScriptDebugServer.h"
#import "RemoteAutomationTarget.h"
#import "RemoteInspectionTarget.h"
#import "RemoteInspector.h"
#import <dispatch/dispatch.h>
#import <wtf/Optional.h>
#import <wtf/RunLoop.h>
#if PLATFORM(IOS_FAMILY)
#import <wtf/ios/WebCoreThread.h>
#endif
namespace Inspector {
static Lock rwiQueueMutex;
static CFRunLoopSourceRef rwiRunLoopSource;
static RemoteTargetQueue* rwiQueue;
static void RemoteTargetHandleRunSourceGlobal(void*)
{
ASSERT(CFRunLoopGetCurrent() == CFRunLoopGetMain());
ASSERT(rwiRunLoopSource);
ASSERT(rwiQueue);
RemoteTargetQueue queueCopy;
{
LockHolder lock(rwiQueueMutex);
queueCopy = *rwiQueue;
rwiQueue->clear();
}
for (const auto& block : queueCopy)
block();
}
static void RemoteTargetQueueTaskOnGlobalQueue(void (^task)())
{
ASSERT(rwiRunLoopSource);
ASSERT(rwiQueue);
{
LockHolder lock(rwiQueueMutex);
rwiQueue->append(task);
}
CFRunLoopSourceSignal(rwiRunLoopSource);
CFRunLoopWakeUp(CFRunLoopGetMain());
}
static void RemoteTargetInitializeGlobalQueue()
{
static dispatch_once_t pred;
dispatch_once(&pred, ^{
rwiQueue = new RemoteTargetQueue;
CFRunLoopSourceContext runLoopSourceContext = { 0, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, RemoteTargetHandleRunSourceGlobal };
rwiRunLoopSource = CFRunLoopSourceCreate(kCFAllocatorDefault, 1, &runLoopSourceContext);
// Add to the default run loop mode for default handling, and the JSContext remote inspector run loop mode when paused.
CFRunLoopAddSource(CFRunLoopGetMain(), rwiRunLoopSource, kCFRunLoopDefaultMode);
auto mode = JSGlobalObjectScriptDebugServer::runLoopMode();
if (!mode.isNull())
CFRunLoopAddSource(CFRunLoopGetMain(), rwiRunLoopSource, mode.createCFString().get());
});
}
static void RemoteTargetHandleRunSourceWithInfo(void* info)
{
RemoteConnectionToTarget *connectionToTarget = static_cast<RemoteConnectionToTarget*>(info);
RemoteTargetQueue queueCopy;
{
LockHolder lock(connectionToTarget->queueMutex());
queueCopy = connectionToTarget->queue();
connectionToTarget->clearQueue();
}
for (const auto& block : queueCopy)
block();
}
RemoteConnectionToTarget::RemoteConnectionToTarget(RemoteControllableTarget* target, NSString *connectionIdentifier, NSString *destination)
: m_target(target)
, m_connectionIdentifier(connectionIdentifier)
, m_destination(destination)
{
setupRunLoop();
}
RemoteConnectionToTarget::~RemoteConnectionToTarget()
{
teardownRunLoop();
}
Optional<TargetID> RemoteConnectionToTarget::targetIdentifier() const
{
return m_target ? Optional<TargetID>(m_target->targetIdentifier()) : WTF::nullopt;
}
NSString *RemoteConnectionToTarget::connectionIdentifier() const
{
return [[m_connectionIdentifier copy] autorelease];
}
NSString *RemoteConnectionToTarget::destination() const
{
return [[m_destination copy] autorelease];
}
void RemoteConnectionToTarget::dispatchAsyncOnTarget(void (^block)())
{
if (m_runLoop) {
queueTaskOnPrivateRunLoop(block);
return;
}
#if PLATFORM(IOS_FAMILY)
if (WebCoreWebThreadIsEnabled && WebCoreWebThreadIsEnabled()) {
WebCoreWebThreadRun(block);
return;
}
#endif
RemoteTargetQueueTaskOnGlobalQueue(block);
}
bool RemoteConnectionToTarget::setup(bool isAutomaticInspection, bool automaticallyPause)
{
LockHolder lock(m_targetMutex);
if (!m_target)
return false;
auto targetIdentifier = this->targetIdentifier().valueOr(0);
ref();
dispatchAsyncOnTarget(^{
{
LockHolder lock(m_targetMutex);
if (!m_target || !m_target->remoteControlAllowed()) {
RemoteInspector::singleton().setupFailed(targetIdentifier);
m_target = nullptr;
} else if (is<RemoteInspectionTarget>(m_target)) {
auto castedTarget = downcast<RemoteInspectionTarget>(m_target);
castedTarget->connect(*this, isAutomaticInspection, automaticallyPause);
m_connected = true;
RemoteInspector::singleton().updateTargetListing(targetIdentifier);
} else if (is<RemoteAutomationTarget>(m_target)) {
auto castedTarget = downcast<RemoteAutomationTarget>(m_target);
castedTarget->connect(*this);
m_connected = true;
RemoteInspector::singleton().updateTargetListing(targetIdentifier);
}
}
deref();
});
return true;
}
void RemoteConnectionToTarget::targetClosed()
{
LockHolder lock(m_targetMutex);
m_target = nullptr;
}
void RemoteConnectionToTarget::close()
{
auto targetIdentifier = m_target ? m_target->targetIdentifier() : 0;
ref();
dispatchAsyncOnTarget(^{
{
LockHolder lock(m_targetMutex);
if (m_target) {
if (m_connected)
m_target->disconnect(*this);
m_target = nullptr;
RemoteInspector::singleton().updateTargetListing(targetIdentifier);
}
}
deref();
});
}
void RemoteConnectionToTarget::sendMessageToTarget(NSString *message)
{
ref();
dispatchAsyncOnTarget(^{
{
RemoteControllableTarget* target = nullptr;
{
LockHolder lock(m_targetMutex);
if (!m_target)
return;
target = m_target;
}
target->dispatchMessageFromRemote(message);
}
deref();
});
}
void RemoteConnectionToTarget::sendMessageToFrontend(const String& message)
{
if (!targetIdentifier())
return;
RemoteInspector::singleton().sendMessageToRemote(targetIdentifier().value(), message);
}
void RemoteConnectionToTarget::setupRunLoop()
{
CFRunLoopRef targetRunLoop = m_target->targetRunLoop();
if (!targetRunLoop) {
RemoteTargetInitializeGlobalQueue();
return;
}
m_runLoop = targetRunLoop;
CFRunLoopSourceContext runLoopSourceContext = { 0, this, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, RemoteTargetHandleRunSourceWithInfo };
m_runLoopSource = adoptCF(CFRunLoopSourceCreate(kCFAllocatorDefault, 1, &runLoopSourceContext));
CFRunLoopAddSource(m_runLoop.get(), m_runLoopSource.get(), kCFRunLoopDefaultMode);
auto mode = JSGlobalObjectScriptDebugServer::runLoopMode();
if (!mode.isNull())
CFRunLoopAddSource(m_runLoop.get(), m_runLoopSource.get(), mode.createCFString().get());
}
void RemoteConnectionToTarget::teardownRunLoop()
{
if (!m_runLoop)
return;
CFRunLoopRemoveSource(m_runLoop.get(), m_runLoopSource.get(), kCFRunLoopDefaultMode);
auto mode = JSGlobalObjectScriptDebugServer::runLoopMode();
if (!mode.isNull())
CFRunLoopRemoveSource(m_runLoop.get(), m_runLoopSource.get(), mode.createCFString().get());
m_runLoop = nullptr;
m_runLoopSource = nullptr;
}
void RemoteConnectionToTarget::queueTaskOnPrivateRunLoop(void (^block)())
{
ASSERT(m_runLoop);
{
LockHolder lock(m_queueMutex);
m_queue.append(block);
}
CFRunLoopSourceSignal(m_runLoopSource.get());
CFRunLoopWakeUp(m_runLoop.get());
}
} // namespace Inspector
#endif // ENABLE(REMOTE_INSPECTOR)