/*
 * Copyright (C) 2012 University of Washington. All rights reserved.
 * Copyright (C) 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
 * 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
 * HOLDER 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.
 */

#include "config.h"
#include "SerializationMethods.h"

#if ENABLE(WEB_REPLAY)

#include "AllReplayInputs.h"
#include "Document.h"
#include "Frame.h"
#include "FrameTree.h"
#include "MainFrame.h"
#include "Page.h"
#include "PlatformKeyboardEvent.h"
#include "PlatformMouseEvent.h"
#include "PlatformWheelEvent.h"
#include "PluginData.h"
#include "SecurityOrigin.h"
#include "URL.h"
#include <wtf/text/Base64.h>

using WebCore::IntPoint;
using WebCore::MimeClassInfo;
using WebCore::MouseButton;
using WebCore::PlatformEvent;
using WebCore::PlatformKeyboardEvent;
using WebCore::PlatformMouseEvent;
using WebCore::PlatformWheelEvent;
using WebCore::PlatformWheelEventGranularity;
using WebCore::PluginData;
using WebCore::PluginLoadClientPolicy;
using WebCore::PluginInfo;
using WebCore::SecurityOrigin;
using WebCore::URL;

#if PLATFORM(COCOA)
using WebCore::KeypressCommand;
using WebCore::PlatformWheelEventPhase;
#endif

#define IMPORT_FROM_WEBCORE_NAMESPACE(name) \
using WebCore::name; \

WEB_REPLAY_INPUT_NAMES_FOR_EACH(IMPORT_FROM_WEBCORE_NAMESPACE)
#undef IMPORT_FROM_WEBCORE_NAMESPACE

namespace WebCore {

uint32_t frameIndexFromDocument(const Document* document)
{
    ASSERT(document);
    ASSERT(document->frame());
    return frameIndexFromFrame(document->frame());
}

uint32_t frameIndexFromFrame(const Frame* targetFrame)
{
    ASSERT(targetFrame);

    uint32_t currentIndex = 0;
    for (const Frame* frame = &targetFrame->tree().top(); frame; ++currentIndex, frame = frame->tree().traverseNext()) {
        if (frame == targetFrame)
            return currentIndex;
    }

    ASSERT_NOT_REACHED();
    return 0;
}

Document* documentFromFrameIndex(Page* page, uint32_t frameIndex)
{
    Frame* frame = frameFromFrameIndex(page, frameIndex);
    return frame ? frame->document() : nullptr;
}

Frame* frameFromFrameIndex(Page* page, uint32_t frameIndex)
{
    ASSERT(page);
    ASSERT(frameIndex >= 0);

    Frame* frame = &page->mainFrame();
    uint32_t currentIndex = 0;
    for (; currentIndex < frameIndex && frame; ++currentIndex, frame = frame->tree().traverseNext()) { }

    return frame;
}

} // namespace WebCore

#define ENCODE_TYPE_WITH_KEY(_encodedValue, _type, _key, _value) \
    _encodedValue.put<_type>(ASCIILiteral(#_key), _value)

#define ENCODE_OPTIONAL_TYPE_WITH_KEY(_encodedValue, _type, _key, _value, condition) \
    if (condition) \
        ENCODE_TYPE_WITH_KEY(_encodedValue, _type, _key, _value)

#define DECODE_TYPE_WITH_KEY_TO_LVALUE(_encodedValue, _type, _key, _lvalue) \
    if (!_encodedValue.get<_type>(ASCIILiteral(#_key), _lvalue)) \
        return false

#define DECODE_TYPE_WITH_KEY(_encodedValue, _type, _key) \
    EncodingTraits<_type>::DecodedType _key; \
    DECODE_TYPE_WITH_KEY_TO_LVALUE(_encodedValue, _type, _key, _key)

#define DECODE_OPTIONAL_TYPE_WITH_KEY_TO_LVALUE(_encodedValue, _type, _key, _lvalue) \
    bool _key ## WasDecoded = _encodedValue.get<_type>(ASCIILiteral(#_key), _lvalue)

#define DECODE_OPTIONAL_TYPE_WITH_KEY(_encodedValue, _type, _key) \
    EncodingTraits<_type>::DecodedType _key; \
    DECODE_OPTIONAL_TYPE_WITH_KEY_TO_LVALUE(_encodedValue, _type, _key, _key)

namespace JSC {

template<>
EncodedValue EncodingTraits<MimeClassInfo>::encodeValue(const MimeClassInfo& input)
{
    EncodedValue encodedData = EncodedValue::createObject();

    ENCODE_TYPE_WITH_KEY(encodedData, String, type, input.type);
    ENCODE_TYPE_WITH_KEY(encodedData, String, desc, input.desc);
    ENCODE_TYPE_WITH_KEY(encodedData, Vector<String>, extensions, input.extensions);

    return encodedData;
}

template<>
bool EncodingTraits<MimeClassInfo>::decodeValue(EncodedValue& encodedData, MimeClassInfo& input)
{
    MimeClassInfo info;

    DECODE_TYPE_WITH_KEY_TO_LVALUE(encodedData, String, type, info.type);
    DECODE_TYPE_WITH_KEY_TO_LVALUE(encodedData, String, desc, info.desc);
    DECODE_TYPE_WITH_KEY_TO_LVALUE(encodedData, Vector<String>, extensions, info.extensions);

    input = info;
    return true;
}

EncodedValue EncodingTraits<NondeterministicInputBase>::encodeValue(const NondeterministicInputBase& input)
{
    EncodedValue encodedValue = EncodedValue::createObject();
    ENCODE_TYPE_WITH_KEY(encodedValue, String, type, input.type());

#define ENCODE_IF_TYPE_TAG_MATCHES(name) \
    if (is<name>(input)) { \
        InputTraits<name>::encode(encodedValue, downcast<name>(input)); \
        return encodedValue; \
    } \

    JS_REPLAY_INPUT_NAMES_FOR_EACH(ENCODE_IF_TYPE_TAG_MATCHES)
    WEB_REPLAY_INPUT_NAMES_FOR_EACH(ENCODE_IF_TYPE_TAG_MATCHES)
#undef ENCODE_IF_TYPE_TAG_MATCHES

    // The macro won't work here because of the class template argument.
    if (is<MemoizedDOMResultBase>(input)) {
        InputTraits<MemoizedDOMResultBase>::encode(encodedValue, downcast<MemoizedDOMResultBase>(input));
        return encodedValue;
    }

    ASSERT_NOT_REACHED();
    return EncodedValue();
}

bool EncodingTraits<NondeterministicInputBase>::decodeValue(EncodedValue& encodedValue, std::unique_ptr<NondeterministicInputBase>& input)
{
    DECODE_TYPE_WITH_KEY(encodedValue, String, type);

#define DECODE_IF_TYPE_TAG_MATCHES(name) \
    if (type == InputTraits<name>::type()) { \
        std::unique_ptr<name> decodedInput; \
        if (!InputTraits<name>::decode(encodedValue, decodedInput)) \
            return false; \
        \
        input = WTFMove(decodedInput); \
        return true; \
    } \

    JS_REPLAY_INPUT_NAMES_FOR_EACH(DECODE_IF_TYPE_TAG_MATCHES)
    WEB_REPLAY_INPUT_NAMES_FOR_EACH(DECODE_IF_TYPE_TAG_MATCHES)
#undef DECODE_IF_TYPE_TAG_MATCHES

    if (type == InputTraits<MemoizedDOMResultBase>::type()) {
        std::unique_ptr<MemoizedDOMResultBase> decodedInput;
        if (!InputTraits<MemoizedDOMResultBase>::decode(encodedValue, decodedInput))
            return false;

        input = WTFMove(decodedInput);
        return true;
    }

    return false;
}

#if USE(APPKIT)
EncodedValue EncodingTraits<KeypressCommand>::encodeValue(const KeypressCommand& command)
{
    EncodedValue encodedValue = EncodedValue::createObject();

    ENCODE_TYPE_WITH_KEY(encodedValue, String, commandName, command.commandName);
    ENCODE_OPTIONAL_TYPE_WITH_KEY(encodedValue, String, text, command.text, !command.text.isEmpty());

    return encodedValue;
}

bool EncodingTraits<KeypressCommand>::decodeValue(EncodedValue& encodedValue, KeypressCommand& decodedValue)
{
    DECODE_TYPE_WITH_KEY(encodedValue, String, commandName);
    DECODE_OPTIONAL_TYPE_WITH_KEY(encodedValue, String, text);

    decodedValue = textWasDecoded ? KeypressCommand(commandName, text) : KeypressCommand(commandName);
    return true;
}

class PlatformKeyboardEventAppKit : public WebCore::PlatformKeyboardEvent {
public:
    PlatformKeyboardEventAppKit(const PlatformKeyboardEvent& event, bool handledByInputMethod, Vector<KeypressCommand>& commands)
        : PlatformKeyboardEvent(event)
    {
        m_handledByInputMethod = handledByInputMethod;
        m_commands = commands;
    }
};
#endif // USE(APPKIT)

EncodedValue EncodingTraits<PlatformKeyboardEvent>::encodeValue(const PlatformKeyboardEvent& input)
{
    EncodedValue encodedValue = EncodedValue::createObject();

    ENCODE_TYPE_WITH_KEY(encodedValue, double, timestamp, input.timestamp());
    ENCODE_TYPE_WITH_KEY(encodedValue, PlatformEvent::Type, type, input.type());
    ENCODE_TYPE_WITH_KEY(encodedValue, PlatformEvent::Modifiers, modifiers, static_cast<PlatformEvent::Modifiers>(input.modifiers()));
    ENCODE_TYPE_WITH_KEY(encodedValue, String, text, input.text());
    ENCODE_TYPE_WITH_KEY(encodedValue, String, unmodifiedText, input.unmodifiedText());
    ENCODE_TYPE_WITH_KEY(encodedValue, String, keyIdentifier, input.keyIdentifier());
    ENCODE_TYPE_WITH_KEY(encodedValue, int, windowsVirtualKeyCode, input.windowsVirtualKeyCode());
    ENCODE_TYPE_WITH_KEY(encodedValue, int, nativeVirtualKeyCode, input.nativeVirtualKeyCode());
    ENCODE_TYPE_WITH_KEY(encodedValue, int, macCharCode, input.macCharCode());
    ENCODE_TYPE_WITH_KEY(encodedValue, bool, autoRepeat, input.isAutoRepeat());
    ENCODE_TYPE_WITH_KEY(encodedValue, bool, keypad, input.isKeypad());
    ENCODE_TYPE_WITH_KEY(encodedValue, bool, systemKey, input.isSystemKey());
#if USE(APPKIT)
    ENCODE_TYPE_WITH_KEY(encodedValue, bool, handledByInputMethod, input.handledByInputMethod());
    ENCODE_TYPE_WITH_KEY(encodedValue, Vector<KeypressCommand>, commands, input.commands());
#endif
    return encodedValue;
}

bool EncodingTraits<PlatformKeyboardEvent>::decodeValue(EncodedValue& encodedValue, std::unique_ptr<PlatformKeyboardEvent>& input)
{
    DECODE_TYPE_WITH_KEY(encodedValue, double, timestamp);
    DECODE_TYPE_WITH_KEY(encodedValue, PlatformEvent::Type, type);
    DECODE_TYPE_WITH_KEY(encodedValue, PlatformEvent::Modifiers, modifiers);
    DECODE_TYPE_WITH_KEY(encodedValue, String, text);
    DECODE_TYPE_WITH_KEY(encodedValue, String, unmodifiedText);
    DECODE_TYPE_WITH_KEY(encodedValue, String, keyIdentifier);
    DECODE_TYPE_WITH_KEY(encodedValue, int, windowsVirtualKeyCode);
    DECODE_TYPE_WITH_KEY(encodedValue, int, nativeVirtualKeyCode);
    DECODE_TYPE_WITH_KEY(encodedValue, int, macCharCode);
    DECODE_TYPE_WITH_KEY(encodedValue, bool, autoRepeat);
    DECODE_TYPE_WITH_KEY(encodedValue, bool, keypad);
    DECODE_TYPE_WITH_KEY(encodedValue, bool, systemKey);
#if USE(APPKIT)
    DECODE_TYPE_WITH_KEY(encodedValue, bool, handledByInputMethod);
    DECODE_TYPE_WITH_KEY(encodedValue, Vector<KeypressCommand>, commands);
#endif

    PlatformKeyboardEvent platformEvent = PlatformKeyboardEvent(type, text, unmodifiedText, keyIdentifier, WTF::safeCast<int>(windowsVirtualKeyCode), WTF::safeCast<int>(nativeVirtualKeyCode), WTF::safeCast<int>(macCharCode), autoRepeat, keypad, systemKey, modifiers, timestamp);
#if USE(APPKIT)
    input = std::make_unique<PlatformKeyboardEventAppKit>(platformEvent, handledByInputMethod, commands);
#else
    input = std::make_unique<PlatformKeyboardEvent>(platformEvent);
#endif
    return true;
}

EncodedValue EncodingTraits<PlatformMouseEvent>::encodeValue(const PlatformMouseEvent& input)
{
    EncodedValue encodedValue = EncodedValue::createObject();

    ENCODE_TYPE_WITH_KEY(encodedValue, int, positionX, input.position().x());
    ENCODE_TYPE_WITH_KEY(encodedValue, int, positionY, input.position().y());
    ENCODE_TYPE_WITH_KEY(encodedValue, int, globalPositionX, input.globalPosition().x());
    ENCODE_TYPE_WITH_KEY(encodedValue, int, globalPositionY, input.globalPosition().y());
    ENCODE_TYPE_WITH_KEY(encodedValue, MouseButton, button, input.button());
    ENCODE_TYPE_WITH_KEY(encodedValue, PlatformEvent::Type, type, input.type());
    ENCODE_TYPE_WITH_KEY(encodedValue, int, clickCount, input.clickCount());
    ENCODE_TYPE_WITH_KEY(encodedValue, bool, shiftKey, input.shiftKey());
    ENCODE_TYPE_WITH_KEY(encodedValue, bool, ctrlKey, input.ctrlKey());
    ENCODE_TYPE_WITH_KEY(encodedValue, bool, altKey, input.altKey());
    ENCODE_TYPE_WITH_KEY(encodedValue, bool, metaKey, input.metaKey());
    ENCODE_TYPE_WITH_KEY(encodedValue, int, timestamp, input.timestamp());
    ENCODE_TYPE_WITH_KEY(encodedValue, double, force, input.force());

    return encodedValue;
}

bool EncodingTraits<PlatformMouseEvent>::decodeValue(EncodedValue& encodedValue, std::unique_ptr<PlatformMouseEvent>& input)
{
    DECODE_TYPE_WITH_KEY(encodedValue, int, positionX);
    DECODE_TYPE_WITH_KEY(encodedValue, int, positionY);
    DECODE_TYPE_WITH_KEY(encodedValue, int, globalPositionX);
    DECODE_TYPE_WITH_KEY(encodedValue, int, globalPositionY);
    DECODE_TYPE_WITH_KEY(encodedValue, MouseButton, button);
    DECODE_TYPE_WITH_KEY(encodedValue, PlatformEvent::Type, type);
    DECODE_TYPE_WITH_KEY(encodedValue, int, clickCount);
    DECODE_TYPE_WITH_KEY(encodedValue, bool, shiftKey);
    DECODE_TYPE_WITH_KEY(encodedValue, bool, ctrlKey);
    DECODE_TYPE_WITH_KEY(encodedValue, bool, altKey);
    DECODE_TYPE_WITH_KEY(encodedValue, bool, metaKey);
    DECODE_TYPE_WITH_KEY(encodedValue, int, timestamp);
    DECODE_TYPE_WITH_KEY(encodedValue, double, force);

    input = std::make_unique<PlatformMouseEvent>(IntPoint(positionX, positionY),
        IntPoint(globalPositionX, globalPositionY),
        button, type, clickCount,
        shiftKey, ctrlKey, altKey, metaKey, timestamp, force);
    return true;
}

#if PLATFORM(COCOA)
struct PlatformWheelEventCocoaArguments {
    bool directionInvertedFromDevice;
    bool hasPreciseScrollingDeltas;
    PlatformWheelEventPhase phase;
    PlatformWheelEventPhase momentumPhase;
    int scrollCount;
    float unacceleratedScrollingDeltaX;
    float unacceleratedScrollingDeltaY;
};

class PlatformWheelEventCocoa : public PlatformWheelEvent {
public:
    PlatformWheelEventCocoa(PlatformWheelEvent& event, PlatformWheelEventCocoaArguments& arguments)
        : PlatformWheelEvent(event)
    {
        m_directionInvertedFromDevice = arguments.directionInvertedFromDevice;
        m_hasPreciseScrollingDeltas = arguments.hasPreciseScrollingDeltas;
        m_phase = arguments.phase;
        m_momentumPhase = arguments.momentumPhase;
        m_scrollCount = arguments.scrollCount;
        m_unacceleratedScrollingDeltaX = arguments.unacceleratedScrollingDeltaX;
        m_unacceleratedScrollingDeltaY = arguments.unacceleratedScrollingDeltaY;
    }
};
#endif // PLATFORM(COCOA)

EncodedValue EncodingTraits<PlatformWheelEvent>::encodeValue(const PlatformWheelEvent& input)
{
    EncodedValue encodedData = EncodedValue::createObject();

    ENCODE_TYPE_WITH_KEY(encodedData, int, positionX, input.position().x());
    ENCODE_TYPE_WITH_KEY(encodedData, int, positionY, input.position().y());
    ENCODE_TYPE_WITH_KEY(encodedData, int, globalPositionX, input.globalPosition().x());
    ENCODE_TYPE_WITH_KEY(encodedData, int, globalPositionY, input.globalPosition().y());
    ENCODE_TYPE_WITH_KEY(encodedData, bool, shiftKey, input.shiftKey());
    ENCODE_TYPE_WITH_KEY(encodedData, bool, ctrlKey, input.ctrlKey());
    ENCODE_TYPE_WITH_KEY(encodedData, bool, altKey, input.altKey());
    ENCODE_TYPE_WITH_KEY(encodedData, bool, metaKey, input.metaKey());
    ENCODE_TYPE_WITH_KEY(encodedData, float, deltaX, input.deltaX());
    ENCODE_TYPE_WITH_KEY(encodedData, float, deltaY, input.deltaY());
    ENCODE_TYPE_WITH_KEY(encodedData, float, wheelTicksX, input.wheelTicksX());
    ENCODE_TYPE_WITH_KEY(encodedData, float, wheelTicksY, input.wheelTicksY());
    ENCODE_TYPE_WITH_KEY(encodedData, PlatformWheelEventGranularity, granularity, static_cast<PlatformWheelEventGranularity>(input.granularity()));

#if PLATFORM(COCOA)
    ENCODE_TYPE_WITH_KEY(encodedData, bool, directionInvertedFromDevice, input.directionInvertedFromDevice());
    ENCODE_TYPE_WITH_KEY(encodedData, bool, hasPreciseScrollingDeltas, input.hasPreciseScrollingDeltas());
    ENCODE_TYPE_WITH_KEY(encodedData, PlatformWheelEventPhase, phase, static_cast<PlatformWheelEventPhase>(input.phase()));
    ENCODE_TYPE_WITH_KEY(encodedData, PlatformWheelEventPhase, momentumPhase, static_cast<PlatformWheelEventPhase>(input.momentumPhase()));
    ENCODE_TYPE_WITH_KEY(encodedData, int, scrollCount, input.scrollCount());
    ENCODE_TYPE_WITH_KEY(encodedData, float, unacceleratedScrollingDeltaX, input.unacceleratedScrollingDeltaX());
    ENCODE_TYPE_WITH_KEY(encodedData, float, unacceleratedScrollingDeltaY, input.unacceleratedScrollingDeltaY());
#endif

    return encodedData;
}

bool EncodingTraits<PlatformWheelEvent>::decodeValue(EncodedValue& encodedData, std::unique_ptr<PlatformWheelEvent>& input)
{
    DECODE_TYPE_WITH_KEY(encodedData, int, positionX);
    DECODE_TYPE_WITH_KEY(encodedData, int, positionY);
    DECODE_TYPE_WITH_KEY(encodedData, int, globalPositionX);
    DECODE_TYPE_WITH_KEY(encodedData, int, globalPositionY);
    DECODE_TYPE_WITH_KEY(encodedData, bool, shiftKey);
    DECODE_TYPE_WITH_KEY(encodedData, bool, ctrlKey);
    DECODE_TYPE_WITH_KEY(encodedData, bool, altKey);
    DECODE_TYPE_WITH_KEY(encodedData, bool, metaKey);
    DECODE_TYPE_WITH_KEY(encodedData, float, deltaX);
    DECODE_TYPE_WITH_KEY(encodedData, float, deltaY);
    DECODE_TYPE_WITH_KEY(encodedData, float, wheelTicksX);
    DECODE_TYPE_WITH_KEY(encodedData, float, wheelTicksY);
    DECODE_TYPE_WITH_KEY(encodedData, PlatformWheelEventGranularity, granularity);

#if PLATFORM(COCOA)
    PlatformWheelEventCocoaArguments arguments;
    DECODE_TYPE_WITH_KEY_TO_LVALUE(encodedData, bool, directionInvertedFromDevice, arguments.directionInvertedFromDevice);
    DECODE_TYPE_WITH_KEY_TO_LVALUE(encodedData, bool, hasPreciseScrollingDeltas, arguments.hasPreciseScrollingDeltas);
    DECODE_TYPE_WITH_KEY_TO_LVALUE(encodedData, PlatformWheelEventPhase, phase, arguments.phase);
    DECODE_TYPE_WITH_KEY_TO_LVALUE(encodedData, PlatformWheelEventPhase, momentumPhase, arguments.momentumPhase);
    DECODE_TYPE_WITH_KEY_TO_LVALUE(encodedData, int, scrollCount, arguments.scrollCount);
    DECODE_TYPE_WITH_KEY_TO_LVALUE(encodedData, float, unacceleratedScrollingDeltaX, arguments.unacceleratedScrollingDeltaX);
    DECODE_TYPE_WITH_KEY_TO_LVALUE(encodedData, float, unacceleratedScrollingDeltaY, arguments.unacceleratedScrollingDeltaY);
#endif

    PlatformWheelEvent event(IntPoint(positionX, positionY), IntPoint(globalPositionX, globalPositionY),
        deltaX, deltaY, wheelTicksX, wheelTicksY, granularity, shiftKey, ctrlKey, altKey, metaKey);

#if PLATFORM(COCOA)
    input = std::make_unique<PlatformWheelEventCocoa>(event, arguments);
#else
    input = std::make_unique<PlatformWheelEvent>(event);
#endif
    return true;
}

EncodedValue EncodingTraits<PluginData>::encodeValue(RefPtr<PluginData> input)
{
    // FIXME: This needs to work in terms of web-visible plug-ins.
    EncodedValue encodedData = EncodedValue::createObject();

    ENCODE_TYPE_WITH_KEY(encodedData, Vector<PluginInfo>, plugins, input->plugins());

    return encodedData;
}

class DeserializedPluginData : public PluginData {
public:
    DeserializedPluginData(Vector<PluginInfo> plugins)
        : PluginData(plugins)
    {
    }
};

bool EncodingTraits<PluginData>::decodeValue(EncodedValue& encodedData, RefPtr<PluginData>& input)
{
    DECODE_TYPE_WITH_KEY(encodedData, Vector<PluginInfo>, plugins);

    // FIXME: This needs to work in terms of web-visible plug-ins.
    input = adoptRef(new DeserializedPluginData(plugins));

    return true;
}

template<>
EncodedValue EncodingTraits<PluginInfo>::encodeValue(const PluginInfo& input)
{
    EncodedValue encodedData = EncodedValue::createObject();

    ENCODE_TYPE_WITH_KEY(encodedData, String, name, input.name);
    ENCODE_TYPE_WITH_KEY(encodedData, String, file, input.file);
    ENCODE_TYPE_WITH_KEY(encodedData, String, desc, input.desc);
    ENCODE_TYPE_WITH_KEY(encodedData, Vector<MimeClassInfo>, mimes, input.mimes);
    ENCODE_TYPE_WITH_KEY(encodedData, bool, isApplicationPlugin, input.isApplicationPlugin);
    ENCODE_TYPE_WITH_KEY(encodedData, PluginLoadClientPolicy, clientLoadPolicy, static_cast<PluginLoadClientPolicy>(input.clientLoadPolicy));
#if PLATFORM(MAC)
    ENCODE_TYPE_WITH_KEY(encodedData, String, bundleIdentifier, input.bundleIdentifier);
    ENCODE_TYPE_WITH_KEY(encodedData, String, versionString, input.versionString);
#endif

    return encodedData;
}

template<>
bool EncodingTraits<PluginInfo>::decodeValue(EncodedValue& encodedData, PluginInfo& input)
{
    PluginInfo info;

    DECODE_TYPE_WITH_KEY_TO_LVALUE(encodedData, String, name, info.name);
    DECODE_TYPE_WITH_KEY_TO_LVALUE(encodedData, String, file, info.file);
    DECODE_TYPE_WITH_KEY_TO_LVALUE(encodedData, String, desc, info.desc);
    DECODE_TYPE_WITH_KEY_TO_LVALUE(encodedData, Vector<MimeClassInfo>, mimes, info.mimes);
    DECODE_TYPE_WITH_KEY_TO_LVALUE(encodedData, bool, isApplicationPlugin, info.isApplicationPlugin);
    DECODE_TYPE_WITH_KEY_TO_LVALUE(encodedData, PluginLoadClientPolicy, clientLoadPolicy, info.clientLoadPolicy);
#if PLATFORM(MAC)
    DECODE_TYPE_WITH_KEY_TO_LVALUE(encodedData, String, bundleIdentifier, input.bundleIdentifier);
    DECODE_TYPE_WITH_KEY_TO_LVALUE(encodedData, String, versionString, input.versionString);
#endif

    input = info;
    return true;
}

EncodedValue EncodingTraits<SecurityOrigin>::encodeValue(RefPtr<SecurityOrigin> input)
{
    return EncodedValue::createString(input->toString());
}

bool EncodingTraits<SecurityOrigin>::decodeValue(EncodedValue& encodedValue, RefPtr<SecurityOrigin>& input)
{
    input = SecurityOrigin::createFromString(encodedValue.convertTo<String>());
    return true;
}

EncodedValue EncodingTraits<URL>::encodeValue(const URL& input)
{
    return EncodedValue::createString(input.string());
}

bool EncodingTraits<URL>::decodeValue(EncodedValue& encodedValue, URL& input)
{
    input = URL(WebCore::ParsedURLString, encodedValue.convertTo<String>());
    return true;
}

} // namespace JSC

#endif // ENABLE(WEB_REPLAY)
