/*
 * Copyright (C) 2015 Igalia S.L.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2,1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include "config.h"

#include "WebViewTest.h"
#include <gtk/gtk.h>
#include <webkit2/webkit2.h>

class ConsoleMessageTest : public WebViewTest {
public:
    MAKE_GLIB_TEST_FIXTURE(ConsoleMessageTest);

    // This should be keep in sync with the public enums in WebKitConsoleMessage.h.
    enum class MessageSource { JavaScript, Network, ConsoleAPI, Security, Other };
    enum class MessageLevel { Info, Log, Warning, Error, Debug };
    struct ConsoleMessage {
        bool operator==(const ConsoleMessage& other)
        {
            return source == other.source
                && level == other.level
                && message == other.message
                && lineNumber == other.lineNumber
                && sourceID == other.sourceID;
        }

        MessageSource source;
        MessageLevel level;
        CString message;
        unsigned lineNumber;
        CString sourceID;
    };

    static void consoleMessageReceivedCallback(WebKitUserContentManager*, WebKitJavascriptResult* message, ConsoleMessageTest* test)
    {
        g_assert(message);
        GUniquePtr<char> messageString(WebViewTest::javascriptResultToCString(message));
        GRefPtr<GVariant> variant = g_variant_parse(G_VARIANT_TYPE("(uusus)"), messageString.get(), nullptr, nullptr, nullptr);
        g_assert(variant.get());

        unsigned source, level, lineNumber;
        const char* messageText;
        const char* sourceID;
        g_variant_get(variant.get(), "(uu&su&s)", &source, &level, &messageText, &lineNumber, &sourceID);
        test->m_consoleMessage = { static_cast<ConsoleMessageTest::MessageSource>(source), static_cast<ConsoleMessageTest::MessageLevel>(level), messageText, lineNumber, sourceID };

        g_main_loop_quit(test->m_mainLoop);
    }

    ConsoleMessageTest()
        : WebViewTest(webkit_user_content_manager_new())
    {
        WebKitUserContentManager* manager = webkit_web_view_get_user_content_manager(m_webView);
        webkit_user_content_manager_register_script_message_handler(manager, "console");
        g_signal_connect(manager, "script-message-received::console", G_CALLBACK(consoleMessageReceivedCallback), this);
    }

    ~ConsoleMessageTest()
    {
        WebKitUserContentManager* manager = webkit_web_view_get_user_content_manager(m_webView);
        g_signal_handlers_disconnect_matched(manager, G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, this);
        webkit_user_content_manager_unregister_script_message_handler(manager, "console");
    }

    void waitUntilConsoleMessageReceived()
    {
        g_main_loop_run(m_mainLoop);
    }

    ConsoleMessage m_consoleMessage;
};

static void testWebKitConsoleMessageConsoleAPI(ConsoleMessageTest* test, gconstpointer)
{
    ConsoleMessageTest::ConsoleMessage referenceMessage = { ConsoleMessageTest::MessageSource::ConsoleAPI, ConsoleMessageTest::MessageLevel::Log, "Log Console Message", 1, "http://foo.com/bar" };
    test->loadHtml("<html><body onload='console.log(\"Log Console Message\");'></body></html>", "http://foo.com/bar");
    test->waitUntilConsoleMessageReceived();
    g_assert(test->m_consoleMessage == referenceMessage);

    referenceMessage.level = ConsoleMessageTest::MessageLevel::Info;
    referenceMessage.message = "Info Console Message";
    test->loadHtml("<html><body onload='console.info(\"Info Console Message\");'></body></html>", "http://foo.com/bar");
    test->waitUntilConsoleMessageReceived();
    g_assert(test->m_consoleMessage == referenceMessage);

    referenceMessage.level = ConsoleMessageTest::MessageLevel::Warning;
    referenceMessage.message = "Warning Console Message";
    test->loadHtml("<html><body onload='console.warn(\"Warning Console Message\");'></body></html>", "http://foo.com/bar");
    test->waitUntilConsoleMessageReceived();
    g_assert(test->m_consoleMessage == referenceMessage);

    referenceMessage.level = ConsoleMessageTest::MessageLevel::Error;
    referenceMessage.message = "Error Console Message";
    test->loadHtml("<html><body onload='console.error(\"Error Console Message\");'></body></html>", "http://foo.com/bar");
    test->waitUntilConsoleMessageReceived();
    g_assert(test->m_consoleMessage == referenceMessage);

    referenceMessage.level = ConsoleMessageTest::MessageLevel::Debug;
    referenceMessage.message = "Debug Console Message";
    test->loadHtml("<html><body onload='console.debug(\"Debug Console Message\");'></body></html>", "http://foo.com/bar");
    test->waitUntilConsoleMessageReceived();
    g_assert(test->m_consoleMessage == referenceMessage);
}

static void testWebKitConsoleMessageJavaScriptException(ConsoleMessageTest* test, gconstpointer)
{
    ConsoleMessageTest::ConsoleMessage referenceMessage = { ConsoleMessageTest::MessageSource::JavaScript, ConsoleMessageTest::MessageLevel::Error,
        "ReferenceError: Can't find variable: foo", 1, "http://foo.com/bar" };
    test->loadHtml("<html><body onload='foo()'></body></html>", "http://foo.com/bar");
    test->waitUntilConsoleMessageReceived();
    g_assert(test->m_consoleMessage == referenceMessage);
}

static void testWebKitConsoleMessageNetworkError(ConsoleMessageTest* test, gconstpointer)
{
    ConsoleMessageTest::ConsoleMessage referenceMessage = { ConsoleMessageTest::MessageSource::Network, ConsoleMessageTest::MessageLevel::Error,
        "Failed to load resource: Error opening file: No such file or directory", 0, "file:///foo/not-found.css" };
    test->loadHtml("<html><head><link rel='stylesheet' href='not-found.css' type='text/css'></head><body></body></html>", "file:///foo/bar");
    test->waitUntilConsoleMessageReceived();
    g_assert(test->m_consoleMessage == referenceMessage);
}

static void testWebKitConsoleMessageSecurityError(ConsoleMessageTest* test, gconstpointer)
{
    ConsoleMessageTest::ConsoleMessage referenceMessage = { ConsoleMessageTest::MessageSource::Security, ConsoleMessageTest::MessageLevel::Error,
        "Not allowed to load local resource: file:///foo/bar/source.png", 1, "http://foo.com/bar" };
    test->loadHtml("<html><body><img src=\"file:///foo/bar/source.png\"/></body></html>", "http://foo.com/bar");
    test->waitUntilConsoleMessageReceived();
    g_assert(test->m_consoleMessage == referenceMessage);
}

void beforeAll()
{
    ConsoleMessageTest::add("WebKitConsoleMessage", "console-api", testWebKitConsoleMessageConsoleAPI);
    ConsoleMessageTest::add("WebKitConsoleMessage", "js-exception", testWebKitConsoleMessageJavaScriptException);
    ConsoleMessageTest::add("WebKitConsoleMessage", "network-error", testWebKitConsoleMessageNetworkError);
    ConsoleMessageTest::add("WebKitConsoleMessage", "security-error", testWebKitConsoleMessageSecurityError);
}

void afterAll()
{
}
