/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtWebEngine module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/

import QtQuick 2.0
import QtTest 1.0
import QtWebEngine 1.3
import QtWebEngine.testsupport 1.0
import QtQuick.Window 2.0
import "../../qmltests/data" 1.0

TestWebEngineView {
    id: webEngineView
    width: 200
    height: 400

    testSupport: WebEngineTestSupport {
        property var errorPageLoadStatus: null

        function waitForErrorPageLoadSucceeded() {
            var success = _waitFor(function() { return testSupport.errorPageLoadStatus == WebEngineView.LoadSucceededStatus })
            testSupport.errorPageLoadStatus = null
            return success
        }

        errorPage.onLoadingChanged: {
            errorPageLoadStatus = loadRequest.status
        }
    }

    function removeFaviconProviderPrefix(url) {
        return url.toString().substring(16)
    }

    function getFaviconPixel(faviconImage) {
        var grabImage = Qt.createQmlObject("
                import QtQuick 2.5\n
                Image { }", testCase)
        var faviconCanvas = Qt.createQmlObject("
                import QtQuick 2.5\n
                Canvas { }", testCase)

        testCase.tryVerify(function() { return faviconImage.status == Image.Ready });
        faviconImage.grabToImage(function(result) {
                grabImage.source = result.url
            });
        testCase.tryVerify(function() { return grabImage.status == Image.Ready });

        faviconCanvas.width = faviconImage.width;
        faviconCanvas.height = faviconImage.height;
        var ctx = faviconCanvas.getContext("2d");
        ctx.drawImage(grabImage, 0, 0, grabImage.width, grabImage.height);
        var imageData = ctx.getImageData(Math.round(faviconCanvas.width/2),
                                         Math.round(faviconCanvas.height/2),
                                         faviconCanvas.width,
                                         faviconCanvas.height);

        grabImage.destroy();
        faviconCanvas.destroy();

        return imageData.data;
    }

    SignalSpy {
        id: iconChangedSpy
        target: webEngineView
        signalName: "iconChanged"
    }

    Image {
        id: favicon
        source: webEngineView.icon
    }

    TestCase {
        id: testCase
        name: "WebEngineFavicon"
        when: windowShown


        function init() {
            // It is worth to restore the initial state with loading a blank page before all test functions.
            webEngineView.url = 'about:blank'
            verify(webEngineView.waitForLoadSucceeded())
            iconChangedSpy.clear()
        }

        function test_faviconLoad() {
            compare(iconChangedSpy.count, 0)

            var url = Qt.resolvedUrl("favicon.html")
            webEngineView.url = url
            verify(webEngineView.waitForLoadSucceeded())

            iconChangedSpy.wait()
            compare(iconChangedSpy.count, 1)

            compare(favicon.width, 48)
            compare(favicon.height, 48)
        }

        function test_faviconLoadEncodedUrl() {
            compare(iconChangedSpy.count, 0)

            var url = Qt.resolvedUrl("favicon2.html?favicon=load should work with#whitespace!")
            webEngineView.url = url
            verify(webEngineView.waitForLoadSucceeded())

            iconChangedSpy.wait()
            compare(iconChangedSpy.count, 1)

            compare(favicon.width, 16)
            compare(favicon.height, 16)
        }

        function test_noFavicon() {
            compare(iconChangedSpy.count, 0)

            var url = Qt.resolvedUrl("test1.html")
            webEngineView.url = url
            verify(webEngineView.waitForLoadSucceeded())

            compare(iconChangedSpy.count, 0)

            var iconUrl = webEngineView.icon
            compare(iconUrl, Qt.resolvedUrl(""))
        }

        function test_aboutBlank() {
            compare(iconChangedSpy.count, 0)

            var url = Qt.resolvedUrl("about:blank")
            webEngineView.url = url
            verify(webEngineView.waitForLoadSucceeded())

            compare(iconChangedSpy.count, 0)

            var iconUrl = webEngineView.icon
            compare(iconUrl, Qt.resolvedUrl(""))
        }

        function test_unavailableFavicon() {
            compare(iconChangedSpy.count, 0)

            var url = Qt.resolvedUrl("favicon-unavailable.html")
            webEngineView.url = url
            verify(webEngineView.waitForLoadSucceeded())

            compare(iconChangedSpy.count, 0)

            var iconUrl = webEngineView.icon
            compare(iconUrl, Qt.resolvedUrl(""))
        }

        function test_errorPageEnabled() {
            WebEngine.settings.errorPageEnabled = true

            compare(iconChangedSpy.count, 0)

            var url = Qt.resolvedUrl("http://url.invalid")
            webEngineView.url = url
            verify(webEngineView.waitForLoadFailed(20000))
            verify(webEngineView.testSupport.waitForErrorPageLoadSucceeded())

            compare(iconChangedSpy.count, 0)

            var iconUrl = webEngineView.icon
            compare(iconUrl, Qt.resolvedUrl(""))
        }

        function test_errorPageDisabled() {
            WebEngine.settings.errorPageEnabled = false

            compare(iconChangedSpy.count, 0)

            var url = Qt.resolvedUrl("http://url.invalid")
            webEngineView.url = url
            verify(webEngineView.waitForLoadFailed(20000))

            compare(iconChangedSpy.count, 0)

            var iconUrl = webEngineView.icon
            compare(iconUrl, Qt.resolvedUrl(""))
        }

        function test_bestFavicon() {
            compare(iconChangedSpy.count, 0)
            var url, iconUrl

            url = Qt.resolvedUrl("favicon-misc.html")
            webEngineView.url = url
            verify(webEngineView.waitForLoadSucceeded())

            iconChangedSpy.wait()
            compare(iconChangedSpy.count, 1)

            iconUrl = removeFaviconProviderPrefix(webEngineView.icon)
            // Touch icon is ignored
            compare(iconUrl, Qt.resolvedUrl("icons/qt32.ico"))
            compare(favicon.width, 32)
            compare(favicon.height, 32)

            iconChangedSpy.clear()

            url = Qt.resolvedUrl("favicon-shortcut.html")
            webEngineView.url = url
            verify(webEngineView.waitForLoadSucceeded())

            iconChangedSpy.wait()
            verify(iconChangedSpy.count >= 1)
            iconUrl = removeFaviconProviderPrefix(webEngineView.icon)

            // If the icon URL is empty we have to wait for
            // the second iconChanged signal that propagates the expected URL
            if (iconUrl == Qt.resolvedUrl("")) {
                tryCompare(iconChangedSpy, "count", 2)
                iconUrl = removeFaviconProviderPrefix(webEngineView.icon)
            }

            compare(iconUrl, Qt.resolvedUrl("icons/qt144.png"))
            compare(favicon.width, 144)
            compare(favicon.height, 144)
        }

        function test_touchIcon() {
            compare(iconChangedSpy.count, 0)

            var url = Qt.resolvedUrl("favicon-touch.html")
            webEngineView.url = url
            verify(webEngineView.waitForLoadSucceeded())

            compare(iconChangedSpy.count, 0)

            var iconUrl = webEngineView.icon
            compare(iconUrl, Qt.resolvedUrl(""))
            compare(favicon.width, 0)
            compare(favicon.height, 0)

            WebEngine.settings.touchIconsEnabled = true

            url = Qt.resolvedUrl("favicon-touch.html")
            webEngineView.url = url
            verify(webEngineView.waitForLoadSucceeded())

            iconChangedSpy.wait()
            iconUrl = removeFaviconProviderPrefix(webEngineView.icon)
            compare(iconUrl, Qt.resolvedUrl("icons/qt144.png"))
            compare(iconChangedSpy.count, 1)
            compare(favicon.width, 144)
            compare(favicon.height, 144)
        }

        function test_multiIcon() {
            compare(iconChangedSpy.count, 0)

            var url = Qt.resolvedUrl("favicon-multi.html")
            webEngineView.url = url
            verify(webEngineView.waitForLoadSucceeded())

            iconChangedSpy.wait()
            compare(iconChangedSpy.count, 1)
            compare(favicon.width, 64)
            compare(favicon.height, 64)
        }

        function test_faviconProvider_data() {
            return [
                   { tag: "multi 8x8", url: Qt.resolvedUrl("favicon-multi-gray.html"), size: 8, value: 16 },
                   { tag: "multi 16x16", url: Qt.resolvedUrl("favicon-multi-gray.html"), size: 16, value: 16 },
                   { tag: "multi 17x17", url: Qt.resolvedUrl("favicon-multi-gray.html"), size: 17, value: 32 },
                   { tag: "multi 31x31", url: Qt.resolvedUrl("favicon-multi-gray.html"), size: 31, value: 32 },
                   { tag: "multi 32x32", url: Qt.resolvedUrl("favicon-multi-gray.html"), size: 32, value: 32 },
                   { tag: "multi 33x33", url: Qt.resolvedUrl("favicon-multi-gray.html"), size: 33, value: 64 },
                   { tag: "multi 64x64", url: Qt.resolvedUrl("favicon-multi-gray.html"), size: 64, value: 64 },
                   { tag: "multi 128x128", url: Qt.resolvedUrl("favicon-multi-gray.html"), size: 128, value: 128 },
                   { tag: "multi 255x255", url: Qt.resolvedUrl("favicon-multi-gray.html"), size: 255, value: 255 },
                   { tag: "multi 256x256", url: Qt.resolvedUrl("favicon-multi-gray.html"), size: 256, value: 255 },
                   { tag: "candidate 8x8", url: Qt.resolvedUrl("favicon-candidates-gray.html"), size: 8, value: 16 },
                   { tag: "candidate 16x16", url: Qt.resolvedUrl("favicon-candidates-gray.html"), size: 16, value: 16 },
                   { tag: "candidate 17x17", url: Qt.resolvedUrl("favicon-candidates-gray.html"), size: 17, value: 32 },
                   { tag: "candidate 31x31", url: Qt.resolvedUrl("favicon-candidates-gray.html"), size: 31, value: 32 },
                   { tag: "candidate 32x32", url: Qt.resolvedUrl("favicon-candidates-gray.html"), size: 32, value: 32 },
                   { tag: "candidate 33x33", url: Qt.resolvedUrl("favicon-candidates-gray.html"), size: 33, value: 64 },
                   { tag: "candidate 64x64", url: Qt.resolvedUrl("favicon-candidates-gray.html"), size: 64, value: 64 },
                   { tag: "candidate 128x128", url: Qt.resolvedUrl("favicon-candidates-gray.html"), size: 128, value: 128 },
                   { tag: "candidate 255x255", url: Qt.resolvedUrl("favicon-candidates-gray.html"), size: 255, value: 255 },
                   { tag: "candidate 256x256", url: Qt.resolvedUrl("favicon-candidates-gray.html"), size: 256, value: 255 },
            ];
        }

        function test_faviconProvider(row) {
            var faviconImage = Qt.createQmlObject("
                    import QtQuick 2.5\n
                    Image { sourceSize: Qt.size(width, height) }", testCase)

            compare(iconChangedSpy.count, 0)

            webEngineView.url = row.url
            verify(webEngineView.waitForLoadSucceeded())

            iconChangedSpy.wait()
            compare(iconChangedSpy.count, 1)

            faviconImage.width = row.size / Screen.devicePixelRatio
            faviconImage.height = row.size  / Screen.devicePixelRatio
            faviconImage.source = webEngineView.icon

            var pixel = getFaviconPixel(faviconImage);
            compare(pixel[0], row.value)

            faviconImage.destroy()
        }

        function test_dynamicFavicon() {
            var faviconImage = Qt.createQmlObject("
                    import QtQuick 2.5\n
                    Image { width: 16; height: 16; sourceSize: Qt.size(width, height); }", testCase)
            faviconImage.source = Qt.binding(function() { return webEngineView.icon; });

            var colors = [
                {"url": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8DwHwAFBQIAX8jx0gAAAABJRU5ErkJggg==", "r": 255, "g": 0, "b": 0},
                {"url": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M/wHwAEBgIApD5fRAAAAABJRU5ErkJggg==", "r": 0, "g": 255, "b": 0},
                {"url": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPj/HwADBwIAMCbHYQAAAABJRU5ErkJggg==", "r": 0, "g": 0, "b": 255},
            ];
            var pixel;

            compare(iconChangedSpy.count, 0);
            webEngineView.loadHtml(
                        "<html>" +
                        "<link rel='icon' type='image/png' href='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII='/>" +
                        "</html>"
            );
            verify(webEngineView.waitForLoadSucceeded());
            tryCompare(iconChangedSpy, "count", 1);

            pixel = getFaviconPixel(faviconImage);
            compare(pixel[0], 0);
            compare(pixel[1], 0);
            compare(pixel[2], 0);

            for (var i = 0; i < colors.length; ++i) {
                iconChangedSpy.clear();
                runJavaScript("document.getElementsByTagName('link')[0].href = 'data:image/png;base64," + colors[i]["url"] + "';");
                tryCompare(faviconImage, "source", "image://favicon/data:image/png;base64," + colors[i]["url"]);
                compare(iconChangedSpy.count, 1);

                pixel = getFaviconPixel(faviconImage);
                compare(pixel[0], colors[i]["r"]);
                compare(pixel[1], colors[i]["g"]);
                compare(pixel[2], colors[i]["b"]);
            }

            faviconImage.destroy()
        }

        function test_touchIconWithSameURL()
        {
            WebEngine.settings.touchIconsEnabled = false;

            var icon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=";

            webEngineView.loadHtml(
                        "<html>" +
                        "<link rel='icon' type='image/png' href='" + icon + "'/>" +
                        "<link rel='apple-touch-icon' type='image/png' href='" + icon + "'/>" +
                        "</html>"
            );
            verify(webEngineView.waitForLoadSucceeded());

            // The default favicon has to be loaded even if its URL is also set as a touch icon while touch icons are disabled.
            tryCompare(iconChangedSpy, "count", 1);
            compare(webEngineView.icon.toString().replace(/^image:\/\/favicon\//, ''), icon);

            iconChangedSpy.clear();

            webEngineView.loadHtml(
                        "<html>" +
                        "<link rel='apple-touch-icon' type='image/png' href='" + icon + "'/>" +
                        "</html>"
            );
            verify(webEngineView.waitForLoadSucceeded());

            // This page only has a touch icon. With disabled touch icons we don't expect any icon to be shown even if the same icon
            // was loaded previously.
            tryCompare(iconChangedSpy, "count", 1);
            verify(!webEngineView.icon.toString().replace(/^image:\/\/favicon\//, ''));
        }
    }
}
