/*
 * Copyright (C) 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"
#if ENABLE(SERVICE_WORKER)
#include "WebSWServerConnection.h"

#include "ArgumentCoders.h"
#include "Decoder.h"
#include "HandleMessage.h"
#include "WebCoreArgumentCoders.h"
#include "WebSWServerConnectionMessages.h"
#include <WebCore/ExceptionData.h>
#include <WebCore/MessageWithMessagePorts.h>
#include <WebCore/NavigationPreloadState.h>
#include <WebCore/ProcessQualified.h>
#include <WebCore/PushSubscriptionData.h>
#include <WebCore/PushSubscriptionIdentifier.h>
#include <WebCore/ScriptExecutionContextIdentifier.h>
#include <WebCore/SecurityOriginData.h>
#include <WebCore/ServiceWorkerClientData.h>
#include <WebCore/ServiceWorkerIdentifier.h>
#include <WebCore/ServiceWorkerJobData.h>
#include <WebCore/ServiceWorkerJobDataIdentifier.h>
#include <WebCore/ServiceWorkerRegistrationData.h>
#include <WebCore/ServiceWorkerRegistrationKey.h>
#include <WebCore/ServiceWorkerTypes.h>
#include <WebCore/WorkerFetchResult.h>
#include <optional>
#include <wtf/Expected.h>
#include <wtf/ObjectIdentifier.h>
#include <wtf/URLHash.h>
#include <wtf/Vector.h>
#include <wtf/text/WTFString.h>

namespace Messages {

namespace WebSWServerConnection {

void ScheduleUnregisterJobInServer::callReply(IPC::Decoder& decoder, CompletionHandler<void(Expected<bool, WebCore::ExceptionData>&&)>&& completionHandler)
{
    std::optional<Expected<bool, WebCore::ExceptionData>> result;
    decoder >> result;
    if (!result) {
        ASSERT_NOT_REACHED();
        cancelReply(WTFMove(completionHandler));
        return;
    }
    completionHandler(WTFMove(*result));
}

void ScheduleUnregisterJobInServer::cancelReply(CompletionHandler<void(Expected<bool, WebCore::ExceptionData>&&)>&& completionHandler)
{
    completionHandler(IPC::AsyncReplyError<Expected<bool, WebCore::ExceptionData>>::create());
}

void ScheduleUnregisterJobInServer::send(UniqueRef<IPC::Encoder>&& encoder, IPC::Connection& connection, const Expected<bool, WebCore::ExceptionData>& result)
{
    encoder.get() << result;
    connection.sendSyncReply(WTFMove(encoder));
}

void MatchRegistration::callReply(IPC::Decoder& decoder, CompletionHandler<void(std::optional<WebCore::ServiceWorkerRegistrationData>&&)>&& completionHandler)
{
    std::optional<std::optional<WebCore::ServiceWorkerRegistrationData>> registration;
    decoder >> registration;
    if (!registration) {
        ASSERT_NOT_REACHED();
        cancelReply(WTFMove(completionHandler));
        return;
    }
    completionHandler(WTFMove(*registration));
}

void MatchRegistration::cancelReply(CompletionHandler<void(std::optional<WebCore::ServiceWorkerRegistrationData>&&)>&& completionHandler)
{
    completionHandler(IPC::AsyncReplyError<std::optional<WebCore::ServiceWorkerRegistrationData>>::create());
}

void MatchRegistration::send(UniqueRef<IPC::Encoder>&& encoder, IPC::Connection& connection, const std::optional<WebCore::ServiceWorkerRegistrationData>& registration)
{
    encoder.get() << registration;
    connection.sendSyncReply(WTFMove(encoder));
}

void WhenRegistrationReady::callReply(IPC::Decoder& decoder, CompletionHandler<void(std::optional<WebCore::ServiceWorkerRegistrationData>&&)>&& completionHandler)
{
    std::optional<std::optional<WebCore::ServiceWorkerRegistrationData>> registration;
    decoder >> registration;
    if (!registration) {
        ASSERT_NOT_REACHED();
        cancelReply(WTFMove(completionHandler));
        return;
    }
    completionHandler(WTFMove(*registration));
}

void WhenRegistrationReady::cancelReply(CompletionHandler<void(std::optional<WebCore::ServiceWorkerRegistrationData>&&)>&& completionHandler)
{
    completionHandler(IPC::AsyncReplyError<std::optional<WebCore::ServiceWorkerRegistrationData>>::create());
}

void WhenRegistrationReady::send(UniqueRef<IPC::Encoder>&& encoder, IPC::Connection& connection, const std::optional<WebCore::ServiceWorkerRegistrationData>& registration)
{
    encoder.get() << registration;
    connection.sendSyncReply(WTFMove(encoder));
}

void GetRegistrations::callReply(IPC::Decoder& decoder, CompletionHandler<void(Vector<WebCore::ServiceWorkerRegistrationData>&&)>&& completionHandler)
{
    std::optional<Vector<WebCore::ServiceWorkerRegistrationData>> registrations;
    decoder >> registrations;
    if (!registrations) {
        ASSERT_NOT_REACHED();
        cancelReply(WTFMove(completionHandler));
        return;
    }
    completionHandler(WTFMove(*registrations));
}

void GetRegistrations::cancelReply(CompletionHandler<void(Vector<WebCore::ServiceWorkerRegistrationData>&&)>&& completionHandler)
{
    completionHandler(IPC::AsyncReplyError<Vector<WebCore::ServiceWorkerRegistrationData>>::create());
}

void GetRegistrations::send(UniqueRef<IPC::Encoder>&& encoder, IPC::Connection& connection, const Vector<WebCore::ServiceWorkerRegistrationData>& registrations)
{
    encoder.get() << registrations;
    connection.sendSyncReply(WTFMove(encoder));
}

void TerminateWorkerFromClient::callReply(IPC::Decoder& decoder, CompletionHandler<void()>&& completionHandler)
{
    completionHandler();
}

void TerminateWorkerFromClient::cancelReply(CompletionHandler<void()>&& completionHandler)
{
    completionHandler();
}

void TerminateWorkerFromClient::send(UniqueRef<IPC::Encoder>&& encoder, IPC::Connection& connection)
{
    connection.sendSyncReply(WTFMove(encoder));
}

void WhenServiceWorkerIsTerminatedForTesting::callReply(IPC::Decoder& decoder, CompletionHandler<void()>&& completionHandler)
{
    completionHandler();
}

void WhenServiceWorkerIsTerminatedForTesting::cancelReply(CompletionHandler<void()>&& completionHandler)
{
    completionHandler();
}

void WhenServiceWorkerIsTerminatedForTesting::send(UniqueRef<IPC::Encoder>&& encoder, IPC::Connection& connection)
{
    connection.sendSyncReply(WTFMove(encoder));
}

void StoreRegistrationsOnDisk::callReply(IPC::Decoder& decoder, CompletionHandler<void()>&& completionHandler)
{
    completionHandler();
}

void StoreRegistrationsOnDisk::cancelReply(CompletionHandler<void()>&& completionHandler)
{
    completionHandler();
}

void StoreRegistrationsOnDisk::send(UniqueRef<IPC::Encoder>&& encoder, IPC::Connection& connection)
{
    connection.sendSyncReply(WTFMove(encoder));
}

void SubscribeToPushService::callReply(IPC::Decoder& decoder, CompletionHandler<void(Expected<WebCore::PushSubscriptionData, WebCore::ExceptionData>&&)>&& completionHandler)
{
    std::optional<Expected<WebCore::PushSubscriptionData, WebCore::ExceptionData>> result;
    decoder >> result;
    if (!result) {
        ASSERT_NOT_REACHED();
        cancelReply(WTFMove(completionHandler));
        return;
    }
    completionHandler(WTFMove(*result));
}

void SubscribeToPushService::cancelReply(CompletionHandler<void(Expected<WebCore::PushSubscriptionData, WebCore::ExceptionData>&&)>&& completionHandler)
{
    completionHandler(IPC::AsyncReplyError<Expected<WebCore::PushSubscriptionData, WebCore::ExceptionData>>::create());
}

void SubscribeToPushService::send(UniqueRef<IPC::Encoder>&& encoder, IPC::Connection& connection, const Expected<WebCore::PushSubscriptionData, WebCore::ExceptionData>& result)
{
    encoder.get() << result;
    connection.sendSyncReply(WTFMove(encoder));
}

void UnsubscribeFromPushService::callReply(IPC::Decoder& decoder, CompletionHandler<void(Expected<bool, WebCore::ExceptionData>&&)>&& completionHandler)
{
    std::optional<Expected<bool, WebCore::ExceptionData>> result;
    decoder >> result;
    if (!result) {
        ASSERT_NOT_REACHED();
        cancelReply(WTFMove(completionHandler));
        return;
    }
    completionHandler(WTFMove(*result));
}

void UnsubscribeFromPushService::cancelReply(CompletionHandler<void(Expected<bool, WebCore::ExceptionData>&&)>&& completionHandler)
{
    completionHandler(IPC::AsyncReplyError<Expected<bool, WebCore::ExceptionData>>::create());
}

void UnsubscribeFromPushService::send(UniqueRef<IPC::Encoder>&& encoder, IPC::Connection& connection, const Expected<bool, WebCore::ExceptionData>& result)
{
    encoder.get() << result;
    connection.sendSyncReply(WTFMove(encoder));
}

void GetPushSubscription::callReply(IPC::Decoder& decoder, CompletionHandler<void(Expected<std::optional<WebCore::PushSubscriptionData>, WebCore::ExceptionData>&&)>&& completionHandler)
{
    std::optional<Expected<std::optional<WebCore::PushSubscriptionData>, WebCore::ExceptionData>> result;
    decoder >> result;
    if (!result) {
        ASSERT_NOT_REACHED();
        cancelReply(WTFMove(completionHandler));
        return;
    }
    completionHandler(WTFMove(*result));
}

void GetPushSubscription::cancelReply(CompletionHandler<void(Expected<std::optional<WebCore::PushSubscriptionData>, WebCore::ExceptionData>&&)>&& completionHandler)
{
    completionHandler(IPC::AsyncReplyError<Expected<std::optional<WebCore::PushSubscriptionData>, WebCore::ExceptionData>>::create());
}

void GetPushSubscription::send(UniqueRef<IPC::Encoder>&& encoder, IPC::Connection& connection, const Expected<std::optional<WebCore::PushSubscriptionData>, WebCore::ExceptionData>& result)
{
    encoder.get() << result;
    connection.sendSyncReply(WTFMove(encoder));
}

void GetPushPermissionState::callReply(IPC::Decoder& decoder, CompletionHandler<void(Expected<uint8_t, WebCore::ExceptionData>&&)>&& completionHandler)
{
    std::optional<Expected<uint8_t, WebCore::ExceptionData>> result;
    decoder >> result;
    if (!result) {
        ASSERT_NOT_REACHED();
        cancelReply(WTFMove(completionHandler));
        return;
    }
    completionHandler(WTFMove(*result));
}

void GetPushPermissionState::cancelReply(CompletionHandler<void(Expected<uint8_t, WebCore::ExceptionData>&&)>&& completionHandler)
{
    completionHandler(IPC::AsyncReplyError<Expected<uint8_t, WebCore::ExceptionData>>::create());
}

void GetPushPermissionState::send(UniqueRef<IPC::Encoder>&& encoder, IPC::Connection& connection, const Expected<uint8_t, WebCore::ExceptionData>& result)
{
    encoder.get() << result;
    connection.sendSyncReply(WTFMove(encoder));
}

void EnableNavigationPreload::callReply(IPC::Decoder& decoder, CompletionHandler<void(std::optional<WebCore::ExceptionData>&&)>&& completionHandler)
{
    std::optional<std::optional<WebCore::ExceptionData>> result;
    decoder >> result;
    if (!result) {
        ASSERT_NOT_REACHED();
        cancelReply(WTFMove(completionHandler));
        return;
    }
    completionHandler(WTFMove(*result));
}

void EnableNavigationPreload::cancelReply(CompletionHandler<void(std::optional<WebCore::ExceptionData>&&)>&& completionHandler)
{
    completionHandler(IPC::AsyncReplyError<std::optional<WebCore::ExceptionData>>::create());
}

void EnableNavigationPreload::send(UniqueRef<IPC::Encoder>&& encoder, IPC::Connection& connection, const std::optional<WebCore::ExceptionData>& result)
{
    encoder.get() << result;
    connection.sendSyncReply(WTFMove(encoder));
}

void DisableNavigationPreload::callReply(IPC::Decoder& decoder, CompletionHandler<void(std::optional<WebCore::ExceptionData>&&)>&& completionHandler)
{
    std::optional<std::optional<WebCore::ExceptionData>> result;
    decoder >> result;
    if (!result) {
        ASSERT_NOT_REACHED();
        cancelReply(WTFMove(completionHandler));
        return;
    }
    completionHandler(WTFMove(*result));
}

void DisableNavigationPreload::cancelReply(CompletionHandler<void(std::optional<WebCore::ExceptionData>&&)>&& completionHandler)
{
    completionHandler(IPC::AsyncReplyError<std::optional<WebCore::ExceptionData>>::create());
}

void DisableNavigationPreload::send(UniqueRef<IPC::Encoder>&& encoder, IPC::Connection& connection, const std::optional<WebCore::ExceptionData>& result)
{
    encoder.get() << result;
    connection.sendSyncReply(WTFMove(encoder));
}

void SetNavigationPreloadHeaderValue::callReply(IPC::Decoder& decoder, CompletionHandler<void(std::optional<WebCore::ExceptionData>&&)>&& completionHandler)
{
    std::optional<std::optional<WebCore::ExceptionData>> result;
    decoder >> result;
    if (!result) {
        ASSERT_NOT_REACHED();
        cancelReply(WTFMove(completionHandler));
        return;
    }
    completionHandler(WTFMove(*result));
}

void SetNavigationPreloadHeaderValue::cancelReply(CompletionHandler<void(std::optional<WebCore::ExceptionData>&&)>&& completionHandler)
{
    completionHandler(IPC::AsyncReplyError<std::optional<WebCore::ExceptionData>>::create());
}

void SetNavigationPreloadHeaderValue::send(UniqueRef<IPC::Encoder>&& encoder, IPC::Connection& connection, const std::optional<WebCore::ExceptionData>& result)
{
    encoder.get() << result;
    connection.sendSyncReply(WTFMove(encoder));
}

void GetNavigationPreloadState::callReply(IPC::Decoder& decoder, CompletionHandler<void(Expected<WebCore::NavigationPreloadState, WebCore::ExceptionData>&&)>&& completionHandler)
{
    std::optional<Expected<WebCore::NavigationPreloadState, WebCore::ExceptionData>> result;
    decoder >> result;
    if (!result) {
        ASSERT_NOT_REACHED();
        cancelReply(WTFMove(completionHandler));
        return;
    }
    completionHandler(WTFMove(*result));
}

void GetNavigationPreloadState::cancelReply(CompletionHandler<void(Expected<WebCore::NavigationPreloadState, WebCore::ExceptionData>&&)>&& completionHandler)
{
    completionHandler(IPC::AsyncReplyError<Expected<WebCore::NavigationPreloadState, WebCore::ExceptionData>>::create());
}

void GetNavigationPreloadState::send(UniqueRef<IPC::Encoder>&& encoder, IPC::Connection& connection, const Expected<WebCore::NavigationPreloadState, WebCore::ExceptionData>& result)
{
    encoder.get() << result;
    connection.sendSyncReply(WTFMove(encoder));
}

} // namespace WebSWServerConnection

} // namespace Messages

namespace WebKit {

void WebSWServerConnection::didReceiveMessage(IPC::Connection& connection, IPC::Decoder& decoder)
{
    if (decoder.messageName() == Messages::WebSWServerConnection::ScheduleJobInServer::name())
        return IPC::handleMessage<Messages::WebSWServerConnection::ScheduleJobInServer>(connection, decoder, this, &WebSWServerConnection::scheduleJobInServer);
    if (decoder.messageName() == Messages::WebSWServerConnection::ScheduleUnregisterJobInServer::name())
        return IPC::handleMessageAsync<Messages::WebSWServerConnection::ScheduleUnregisterJobInServer>(connection, decoder, this, &WebSWServerConnection::scheduleUnregisterJobInServer);
    if (decoder.messageName() == Messages::WebSWServerConnection::FinishFetchingScriptInServer::name())
        return IPC::handleMessage<Messages::WebSWServerConnection::FinishFetchingScriptInServer>(connection, decoder, this, &WebSWServerConnection::finishFetchingScriptInServer);
    if (decoder.messageName() == Messages::WebSWServerConnection::AddServiceWorkerRegistrationInServer::name())
        return IPC::handleMessage<Messages::WebSWServerConnection::AddServiceWorkerRegistrationInServer>(connection, decoder, this, &WebSWServerConnection::addServiceWorkerRegistrationInServer);
    if (decoder.messageName() == Messages::WebSWServerConnection::RemoveServiceWorkerRegistrationInServer::name())
        return IPC::handleMessage<Messages::WebSWServerConnection::RemoveServiceWorkerRegistrationInServer>(connection, decoder, this, &WebSWServerConnection::removeServiceWorkerRegistrationInServer);
    if (decoder.messageName() == Messages::WebSWServerConnection::PostMessageToServiceWorker::name())
        return IPC::handleMessage<Messages::WebSWServerConnection::PostMessageToServiceWorker>(connection, decoder, this, &WebSWServerConnection::postMessageToServiceWorker);
    if (decoder.messageName() == Messages::WebSWServerConnection::DidResolveRegistrationPromise::name())
        return IPC::handleMessage<Messages::WebSWServerConnection::DidResolveRegistrationPromise>(connection, decoder, this, &WebSWServerConnection::didResolveRegistrationPromise);
    if (decoder.messageName() == Messages::WebSWServerConnection::MatchRegistration::name())
        return IPC::handleMessageAsync<Messages::WebSWServerConnection::MatchRegistration>(connection, decoder, this, &WebSWServerConnection::matchRegistration);
    if (decoder.messageName() == Messages::WebSWServerConnection::WhenRegistrationReady::name())
        return IPC::handleMessageAsync<Messages::WebSWServerConnection::WhenRegistrationReady>(connection, decoder, this, &WebSWServerConnection::whenRegistrationReady);
    if (decoder.messageName() == Messages::WebSWServerConnection::GetRegistrations::name())
        return IPC::handleMessageAsync<Messages::WebSWServerConnection::GetRegistrations>(connection, decoder, this, &WebSWServerConnection::getRegistrations);
    if (decoder.messageName() == Messages::WebSWServerConnection::RegisterServiceWorkerClient::name())
        return IPC::handleMessage<Messages::WebSWServerConnection::RegisterServiceWorkerClient>(connection, decoder, this, &WebSWServerConnection::registerServiceWorkerClient);
    if (decoder.messageName() == Messages::WebSWServerConnection::UnregisterServiceWorkerClient::name())
        return IPC::handleMessage<Messages::WebSWServerConnection::UnregisterServiceWorkerClient>(connection, decoder, this, &WebSWServerConnection::unregisterServiceWorkerClient);
    if (decoder.messageName() == Messages::WebSWServerConnection::TerminateWorkerFromClient::name())
        return IPC::handleMessageAsync<Messages::WebSWServerConnection::TerminateWorkerFromClient>(connection, decoder, this, &WebSWServerConnection::terminateWorkerFromClient);
    if (decoder.messageName() == Messages::WebSWServerConnection::WhenServiceWorkerIsTerminatedForTesting::name())
        return IPC::handleMessageAsync<Messages::WebSWServerConnection::WhenServiceWorkerIsTerminatedForTesting>(connection, decoder, this, &WebSWServerConnection::whenServiceWorkerIsTerminatedForTesting);
    if (decoder.messageName() == Messages::WebSWServerConnection::SetThrottleState::name())
        return IPC::handleMessage<Messages::WebSWServerConnection::SetThrottleState>(connection, decoder, this, &WebSWServerConnection::setThrottleState);
    if (decoder.messageName() == Messages::WebSWServerConnection::StoreRegistrationsOnDisk::name())
        return IPC::handleMessageAsync<Messages::WebSWServerConnection::StoreRegistrationsOnDisk>(connection, decoder, this, &WebSWServerConnection::storeRegistrationsOnDisk);
    if (decoder.messageName() == Messages::WebSWServerConnection::SubscribeToPushService::name())
        return IPC::handleMessageAsync<Messages::WebSWServerConnection::SubscribeToPushService>(connection, decoder, this, &WebSWServerConnection::subscribeToPushService);
    if (decoder.messageName() == Messages::WebSWServerConnection::UnsubscribeFromPushService::name())
        return IPC::handleMessageAsync<Messages::WebSWServerConnection::UnsubscribeFromPushService>(connection, decoder, this, &WebSWServerConnection::unsubscribeFromPushService);
    if (decoder.messageName() == Messages::WebSWServerConnection::GetPushSubscription::name())
        return IPC::handleMessageAsync<Messages::WebSWServerConnection::GetPushSubscription>(connection, decoder, this, &WebSWServerConnection::getPushSubscription);
    if (decoder.messageName() == Messages::WebSWServerConnection::GetPushPermissionState::name())
        return IPC::handleMessageAsync<Messages::WebSWServerConnection::GetPushPermissionState>(connection, decoder, this, &WebSWServerConnection::getPushPermissionState);
    if (decoder.messageName() == Messages::WebSWServerConnection::EnableNavigationPreload::name())
        return IPC::handleMessageAsync<Messages::WebSWServerConnection::EnableNavigationPreload>(connection, decoder, this, &WebSWServerConnection::enableNavigationPreload);
    if (decoder.messageName() == Messages::WebSWServerConnection::DisableNavigationPreload::name())
        return IPC::handleMessageAsync<Messages::WebSWServerConnection::DisableNavigationPreload>(connection, decoder, this, &WebSWServerConnection::disableNavigationPreload);
    if (decoder.messageName() == Messages::WebSWServerConnection::SetNavigationPreloadHeaderValue::name())
        return IPC::handleMessageAsync<Messages::WebSWServerConnection::SetNavigationPreloadHeaderValue>(connection, decoder, this, &WebSWServerConnection::setNavigationPreloadHeaderValue);
    if (decoder.messageName() == Messages::WebSWServerConnection::GetNavigationPreloadState::name())
        return IPC::handleMessageAsync<Messages::WebSWServerConnection::GetNavigationPreloadState>(connection, decoder, this, &WebSWServerConnection::getNavigationPreloadState);
    UNUSED_PARAM(connection);
    UNUSED_PARAM(decoder);
#if ENABLE(IPC_TESTING_API)
    if (connection.ignoreInvalidMessageForTesting())
        return;
#endif // ENABLE(IPC_TESTING_API)
    ASSERT_NOT_REACHED_WITH_MESSAGE("Unhandled message %s to %" PRIu64, IPC::description(decoder.messageName()), decoder.destinationID());
}

} // namespace WebKit

#endif // ENABLE(SERVICE_WORKER)
