blob: e886608d186f663c5e9ddeae4d833b0acc58660b [file] [log] [blame]
/*
* Copyright (C) 2020-2021 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. AND ITS CONTRIBUTORS ``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 ITS 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.
*/
#include "config.h"
#include "WebInspectorUIExtensionControllerProxy.h"
#if ENABLE(INSPECTOR_EXTENSIONS)
#include "APIInspectorExtension.h"
#include "APIURL.h"
#include "WebInspectorUIExtensionControllerMessages.h"
#include "WebInspectorUIExtensionControllerProxyMessages.h"
#include "WebPageProxy.h"
#include "WebProcessProxy.h"
namespace WebKit {
WebInspectorUIExtensionControllerProxy::WebInspectorUIExtensionControllerProxy(WebPageProxy& inspectorPage)
: m_inspectorPage(inspectorPage)
{
m_inspectorPage->process().addMessageReceiver(Messages::WebInspectorUIExtensionControllerProxy::messageReceiverName(), m_inspectorPage->webPageID(), *this);
}
WebInspectorUIExtensionControllerProxy::~WebInspectorUIExtensionControllerProxy()
{
auto callbacks = std::exchange(m_frontendLoadedCallbackQueue, { });
for (auto& callback : callbacks)
callback();
// At this point, we should have already been notified that the frontend has closed.
ASSERT(!m_inspectorPage);
}
Ref<WebInspectorUIExtensionControllerProxy> WebInspectorUIExtensionControllerProxy::create(WebPageProxy& inspectorPage)
{
return adoptRef(*new WebInspectorUIExtensionControllerProxy(inspectorPage));
}
void WebInspectorUIExtensionControllerProxy::whenFrontendHasLoaded(Function<void()>&& callback)
{
if (m_frontendLoaded && m_inspectorPage) {
callback();
return;
}
m_frontendLoadedCallbackQueue.append(WTFMove(callback));
}
void WebInspectorUIExtensionControllerProxy::inspectorFrontendLoaded()
{
ASSERT(m_inspectorPage);
m_frontendLoaded = true;
auto callbacks = std::exchange(m_frontendLoadedCallbackQueue, { });
for (auto& callback : callbacks)
callback();
}
void WebInspectorUIExtensionControllerProxy::inspectorFrontendWillClose()
{
if (!m_inspectorPage)
return;
m_inspectorPage->process().removeMessageReceiver(Messages::WebInspectorUIExtensionControllerProxy::messageReceiverName(), m_inspectorPage->webPageID());
m_inspectorPage = nullptr;
m_extensionAPIObjectMap.clear();
}
// API
void WebInspectorUIExtensionControllerProxy::registerExtension(const Inspector::ExtensionID& extensionID, const String& extensionBundleIdentifier, const String& displayName, WTF::CompletionHandler<void(Expected<RefPtr<API::InspectorExtension>, Inspector::ExtensionError>)>&& completionHandler)
{
whenFrontendHasLoaded([weakThis = WeakPtr { *this }, extensionID, extensionBundleIdentifier, displayName, completionHandler = WTFMove(completionHandler)] () mutable {
if (!weakThis || !weakThis->m_inspectorPage) {
completionHandler(makeUnexpected(Inspector::ExtensionError::InvalidRequest));
return;
}
weakThis->m_inspectorPage->sendWithAsyncReply(Messages::WebInspectorUIExtensionController::RegisterExtension { extensionID, extensionBundleIdentifier, displayName }, [strongThis = Ref { *weakThis.get() }, extensionID, completionHandler = WTFMove(completionHandler)](Expected<void, Inspector::ExtensionError> result) mutable {
if (!result) {
completionHandler(makeUnexpected(Inspector::ExtensionError::RegistrationFailed));
return;
}
if (!strongThis->m_inspectorPage) {
completionHandler(makeUnexpected(Inspector::ExtensionError::ContextDestroyed));
return;
}
RefPtr<API::InspectorExtension> extensionAPIObject = API::InspectorExtension::create(extensionID, strongThis.get());
strongThis->m_extensionAPIObjectMap.set(extensionID, extensionAPIObject.copyRef());
completionHandler(WTFMove(extensionAPIObject));
});
});
}
void WebInspectorUIExtensionControllerProxy::unregisterExtension(const Inspector::ExtensionID& extensionID, WTF::CompletionHandler<void(Expected<void, Inspector::ExtensionError>)>&& completionHandler)
{
whenFrontendHasLoaded([weakThis = WeakPtr { *this }, extensionID, completionHandler = WTFMove(completionHandler)] () mutable {
if (!weakThis || !weakThis->m_inspectorPage) {
completionHandler(makeUnexpected(Inspector::ExtensionError::InvalidRequest));
return;
}
weakThis->m_inspectorPage->sendWithAsyncReply(Messages::WebInspectorUIExtensionController::UnregisterExtension { extensionID }, [strongThis = Ref { *weakThis.get() }, extensionID, completionHandler = WTFMove(completionHandler)](Expected<void, Inspector::ExtensionError> result) mutable {
if (!result) {
completionHandler(makeUnexpected(Inspector::ExtensionError::InvalidRequest));
return;
}
if (!strongThis->m_inspectorPage) {
completionHandler(makeUnexpected(Inspector::ExtensionError::ContextDestroyed));
return;
}
strongThis->m_extensionAPIObjectMap.take(extensionID);
completionHandler(WTFMove(result));
});
});
}
void WebInspectorUIExtensionControllerProxy::createTabForExtension(const Inspector::ExtensionID& extensionID, const String& tabName, const URL& tabIconURL, const URL& sourceURL, WTF::CompletionHandler<void(Expected<Inspector::ExtensionTabID, Inspector::ExtensionError>)>&& completionHandler)
{
whenFrontendHasLoaded([weakThis = WeakPtr { *this }, extensionID, tabName, tabIconURL, sourceURL, completionHandler = WTFMove(completionHandler)] () mutable {
if (!weakThis || !weakThis->m_inspectorPage) {
completionHandler(makeUnexpected(Inspector::ExtensionError::InvalidRequest));
return;
}
weakThis->m_inspectorPage->sendWithAsyncReply(Messages::WebInspectorUIExtensionController::CreateTabForExtension { extensionID, tabName, tabIconURL, sourceURL }, WTFMove(completionHandler));
});
}
void WebInspectorUIExtensionControllerProxy::evaluateScriptForExtension(const Inspector::ExtensionID& extensionID, const String& scriptSource, const std::optional<WTF::URL>& frameURL, const std::optional<WTF::URL>& contextSecurityOrigin, const std::optional<bool>& useContentScriptContext, WTF::CompletionHandler<void(Inspector::ExtensionEvaluationResult)>&& completionHandler)
{
whenFrontendHasLoaded([weakThis = WeakPtr { *this }, extensionID, scriptSource, frameURL, contextSecurityOrigin, useContentScriptContext, completionHandler = WTFMove(completionHandler)] () mutable {
if (!weakThis || !weakThis->m_inspectorPage) {
completionHandler(makeUnexpected(Inspector::ExtensionError::ContextDestroyed));
return;
}
weakThis->m_inspectorPage->sendWithAsyncReply(Messages::WebInspectorUIExtensionController::EvaluateScriptForExtension {extensionID, scriptSource, frameURL, contextSecurityOrigin, useContentScriptContext}, [completionHandler = WTFMove(completionHandler)](const IPC::DataReference& dataReference, const std::optional<WebCore::ExceptionDetails>& details, const std::optional<Inspector::ExtensionError> error) mutable {
if (error) {
completionHandler(makeUnexpected(error.value()));
return;
}
if (details) {
Expected<RefPtr<API::SerializedScriptValue>, WebCore::ExceptionDetails> returnedValue = makeUnexpected(details.value());
return completionHandler({ returnedValue });
}
completionHandler({ { API::SerializedScriptValue::adopt(Vector { dataReference.data(), dataReference.size() }).ptr() } });
});
});
}
void WebInspectorUIExtensionControllerProxy::reloadForExtension(const Inspector::ExtensionID& extensionID, const std::optional<bool>& ignoreCache, const std::optional<String>& userAgent, const std::optional<String>& injectedScript, WTF::CompletionHandler<void(Inspector::ExtensionEvaluationResult)>&& completionHandler)
{
whenFrontendHasLoaded([weakThis = WeakPtr { *this }, extensionID, ignoreCache, userAgent, injectedScript, completionHandler = WTFMove(completionHandler)] () mutable {
if (!weakThis || !weakThis->m_inspectorPage) {
completionHandler(makeUnexpected(Inspector::ExtensionError::ContextDestroyed));
return;
}
weakThis->m_inspectorPage->sendWithAsyncReply(Messages::WebInspectorUIExtensionController::ReloadForExtension {extensionID, ignoreCache, userAgent, injectedScript}, [completionHandler = WTFMove(completionHandler)](const std::optional<Inspector::ExtensionError> error) mutable {
if (error) {
completionHandler(makeUnexpected(error.value()));
return;
}
completionHandler({ });
});
});
}
void WebInspectorUIExtensionControllerProxy::showExtensionTab(const Inspector::ExtensionTabID& extensionTabIdentifier, CompletionHandler<void(Expected<void, Inspector::ExtensionError>)>&& completionHandler)
{
whenFrontendHasLoaded([weakThis = WeakPtr { *this }, extensionTabIdentifier, completionHandler = WTFMove(completionHandler)] () mutable {
if (!weakThis || !weakThis->m_inspectorPage) {
completionHandler(makeUnexpected(Inspector::ExtensionError::ContextDestroyed));
return;
}
weakThis->m_inspectorPage->sendWithAsyncReply(Messages::WebInspectorUIExtensionController::ShowExtensionTab { extensionTabIdentifier }, WTFMove(completionHandler));
});
}
// API for testing.
void WebInspectorUIExtensionControllerProxy::evaluateScriptInExtensionTab(const Inspector::ExtensionTabID& extensionTabID, const String& scriptSource, WTF::CompletionHandler<void(Inspector::ExtensionEvaluationResult)>&& completionHandler)
{
whenFrontendHasLoaded([weakThis = WeakPtr { *this }, extensionTabID, scriptSource, completionHandler = WTFMove(completionHandler)] () mutable {
if (!weakThis || !weakThis->m_inspectorPage) {
completionHandler(makeUnexpected(Inspector::ExtensionError::ContextDestroyed));
return;
}
weakThis->m_inspectorPage->sendWithAsyncReply(Messages::WebInspectorUIExtensionController::EvaluateScriptInExtensionTab {extensionTabID, scriptSource}, [completionHandler = WTFMove(completionHandler)](const IPC::DataReference& dataReference, const std::optional<WebCore::ExceptionDetails>& details, const std::optional<Inspector::ExtensionError>& error) mutable {
if (error) {
completionHandler(makeUnexpected(error.value()));
return;
}
if (details) {
Expected<RefPtr<API::SerializedScriptValue>, WebCore::ExceptionDetails> returnedValue = makeUnexpected(details.value());
return completionHandler({ returnedValue });
}
completionHandler({ { API::SerializedScriptValue::adopt({ dataReference.data(), dataReference.size() }).ptr() } });
});
});
}
// WebInspectorUIExtensionControllerProxy IPC messages.
void WebInspectorUIExtensionControllerProxy::didShowExtensionTab(const Inspector::ExtensionID& extensionID, const Inspector::ExtensionTabID& extensionTabID)
{
auto it = m_extensionAPIObjectMap.find(extensionID);
if (it == m_extensionAPIObjectMap.end())
return;
RefPtr<API::InspectorExtension> extension = it->value;
auto extensionClient = extension->client();
if (!extensionClient)
return;
extensionClient->didShowExtensionTab(extensionTabID);
}
void WebInspectorUIExtensionControllerProxy::didHideExtensionTab(const Inspector::ExtensionID& extensionID, const Inspector::ExtensionTabID& extensionTabID)
{
auto it = m_extensionAPIObjectMap.find(extensionID);
if (it == m_extensionAPIObjectMap.end())
return;
RefPtr<API::InspectorExtension> extension = it->value;
auto extensionClient = extension->client();
if (!extensionClient)
return;
extensionClient->didHideExtensionTab(extensionTabID);
}
void WebInspectorUIExtensionControllerProxy::didNavigateExtensionTab(const Inspector::ExtensionID& extensionID, const Inspector::ExtensionTabID& extensionTabID, const WTF::URL& newURL)
{
auto it = m_extensionAPIObjectMap.find(extensionID);
if (it == m_extensionAPIObjectMap.end())
return;
RefPtr<API::InspectorExtension> extension = it->value;
auto extensionClient = extension->client();
if (!extensionClient)
return;
extensionClient->didNavigateExtensionTab(extensionTabID, newURL);
}
void WebInspectorUIExtensionControllerProxy::inspectedPageDidNavigate(const URL& newURL)
{
for (auto& extension : copyToVector(m_extensionAPIObjectMap.values())) {
auto extensionClient = extension->client();
if (!extensionClient)
continue;
extensionClient->inspectedPageDidNavigate(newURL);
}
}
} // namespace WebKit
#endif // ENABLE(INSPECTOR_EXTENSIONS)