/*
 * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program 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 program; 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 "QtWebPagePolicyClient.h"

#include "WKFrame.h"
#include "WKURLQt.h"
#include "qquickwebview_p_p.h"
#include "qwebnavigationrequest_p.h"
#include <QtCore/QObject>
#include <WKFramePolicyListener.h>
#include <WKRetainPtr.h>
#include <WKURLRequest.h>

namespace WebKit {

QtWebPagePolicyClient::QtWebPagePolicyClient(WKPageRef pageRef, QQuickWebView* webView)
    : m_webView(webView)
{
    WKPagePolicyClientV1 policyClient;
    memset(&policyClient, 0, sizeof(WKPagePolicyClientV0));
    policyClient.base = { 1, this };
    policyClient.decidePolicyForNavigationAction = decidePolicyForNavigationAction;
    policyClient.decidePolicyForResponse = decidePolicyForResponse;
    WKPageSetPagePolicyClient(pageRef, &policyClient.base);
}

void QtWebPagePolicyClient::decidePolicyForNavigationAction(const QUrl& url, Qt::MouseButton mouseButton, Qt::KeyboardModifiers keyboardModifiers, QQuickWebView::NavigationType navigationType, bool isMainFrame, WKFramePolicyListenerRef listener)
{
    // NOTE: even though the C API (and the WebKit2 IPC) supports an asynchronous answer, this is not currently working.
    // We are expected to call the listener immediately. See the patch for https://bugs.webkit.org/show_bug.cgi?id=53785.
    QWebNavigationRequest navigationRequest(url, mouseButton, keyboardModifiers, navigationType, isMainFrame);
    emit m_webView->navigationRequested(&navigationRequest);

    switch (navigationRequest.action()) {
    case QQuickWebView::IgnoreRequest:
        WKFramePolicyListenerIgnore(listener);
        return;
    case QQuickWebViewExperimental::DownloadRequest:
        WKFramePolicyListenerDownload(listener);
        return;
    case QQuickWebView::AcceptRequest:
        WKFramePolicyListenerUse(listener);
        return;
    }
    ASSERT_NOT_REACHED();
}

static inline QtWebPagePolicyClient* toQtWebPagePolicyClient(const void* clientInfo)
{
    ASSERT(clientInfo);
    return reinterpret_cast<QtWebPagePolicyClient*>(const_cast<void*>(clientInfo));
}

static Qt::MouseButton toQtMouseButton(WKEventMouseButton button)
{
    switch (button) {
    case kWKEventMouseButtonLeftButton:
        return Qt::LeftButton;
    case kWKEventMouseButtonMiddleButton:
        return Qt::MiddleButton;
    case kWKEventMouseButtonRightButton:
        return Qt::RightButton;
    case kWKEventMouseButtonNoButton:
        return Qt::NoButton;
    }
    ASSERT_NOT_REACHED();
    return Qt::NoButton;
}

static Qt::KeyboardModifiers toQtKeyboardModifiers(WKEventModifiers modifiers)
{
    Qt::KeyboardModifiers qtModifiers = Qt::NoModifier;
    if (modifiers & kWKEventModifiersShiftKey)
        qtModifiers |= Qt::ShiftModifier;
    if (modifiers & kWKEventModifiersControlKey)
        qtModifiers |= Qt::ControlModifier;
    if (modifiers & kWKEventModifiersAltKey)
        qtModifiers |= Qt::AltModifier;
    if (modifiers & kWKEventModifiersMetaKey)
        qtModifiers |= Qt::MetaModifier;
    return qtModifiers;
}

static QQuickWebView::NavigationType toQuickWebViewNavigationType(WKFrameNavigationType navigationType)
{
    switch (navigationType) {
    case kWKFrameNavigationTypeLinkClicked:
        return QQuickWebView::LinkClickedNavigation;
    case kWKFrameNavigationTypeFormSubmitted:
        return QQuickWebView::FormSubmittedNavigation;
    case kWKFrameNavigationTypeBackForward:
        return QQuickWebView::BackForwardNavigation;
    case kWKFrameNavigationTypeReload:
        return QQuickWebView::ReloadNavigation;
    case kWKFrameNavigationTypeFormResubmitted:
        return QQuickWebView::FormResubmittedNavigation;
    case kWKFrameNavigationTypeOther:
        return QQuickWebView::OtherNavigation;
    }
    ASSERT_NOT_REACHED();
    return QQuickWebView::OtherNavigation;
}

void QtWebPagePolicyClient::decidePolicyForNavigationAction(WKPageRef page, WKFrameRef frame, WKFrameNavigationType navigationType, WKEventModifiers modifiers, WKEventMouseButton mouseButton, WKFrameRef originatingFrame, WKURLRequestRef request, WKFramePolicyListenerRef listener, WKTypeRef, const void* clientInfo)
{
    WKRetainPtr<WKURLRef> frameURL(AdoptWK, WKFrameCopyURL(frame));
    WKRetainPtr<WKURLRef> requestURL(AdoptWK, WKURLRequestCopyURL(request));
    QUrl qUrl = WKURLCopyQUrl(requestURL.get());
    bool isMainFrame = (WKPageGetMainFrame(page) == frame);
    toQtWebPagePolicyClient(clientInfo)->decidePolicyForNavigationAction(qUrl, toQtMouseButton(mouseButton), toQtKeyboardModifiers(modifiers), toQuickWebViewNavigationType(navigationType), isMainFrame, listener);
}

void QtWebPagePolicyClient::decidePolicyForResponse(WKPageRef page, WKFrameRef frame, WKURLResponseRef response, WKURLRequestRef, bool canShowMIMEType, WKFramePolicyListenerRef listener, WKTypeRef, const void*)
{
    String type = toImpl(response)->resourceResponse().mimeType();
    type.convertToASCIILowercase(); // QTFIXME: See also FrameLoaderClientQt

    if (WKPageGetMainFrame(page) == frame) {
        if (canShowMIMEType) {
            WKFramePolicyListenerUse(listener);
            return;
        }

        // If we can't use (show) it then we should download it.
        WKFramePolicyListenerDownload(listener);
        return;
    }

    // We should ignore downloadable top-level content for subframes, with an exception for text/xml and application/xml so we can still support Acid3 test.
    // It makes the browser intentionally behave differently when it comes to text(application)/xml content in subframes vs. mainframe.
    if (!canShowMIMEType && !(type == "text/xml" || type == "application/xml")) {
        WKFramePolicyListenerIgnore(listener);
        return;
    }

    WKFramePolicyListenerUse(listener);
}

} // namespace WebKit
