1/*
2 * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies)
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this program; see the file COPYING.LIB.  If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 *
19 */
20
21#include "config.h"
22#include "QtWebPagePolicyClient.h"
23
24#include "WKFrame.h"
25#include "WKURLQt.h"
26#include "qquickwebview_p_p.h"
27#include "qwebnavigationrequest_p.h"
28#include <QtCore/QObject>
29#include <WKFramePolicyListener.h>
30#include <WKRetainPtr.h>
31#include <WKURLRequest.h>
32
33namespace WebKit {
34
35QtWebPagePolicyClient::QtWebPagePolicyClient(WKPageRef pageRef, QQuickWebView* webView)
36    : m_webView(webView)
37{
38    WKPagePolicyClient policyClient;
39    memset(&policyClient, 0, sizeof(WKPagePolicyClient));
40    policyClient.version = kWKPagePolicyClientCurrentVersion;
41    policyClient.clientInfo = this;
42    policyClient.decidePolicyForNavigationAction = decidePolicyForNavigationAction;
43    policyClient.decidePolicyForResponse = decidePolicyForResponse;
44    WKPageSetPagePolicyClient(pageRef, &policyClient);
45}
46
47void QtWebPagePolicyClient::decidePolicyForNavigationAction(const QUrl& url, Qt::MouseButton mouseButton, Qt::KeyboardModifiers keyboardModifiers, QQuickWebView::NavigationType navigationType, WKFramePolicyListenerRef listener)
48{
49    // NOTE: even though the C API (and the WebKit2 IPC) supports an asynchronous answer, this is not currently working.
50    // We are expected to call the listener immediately. See the patch for https://bugs.webkit.org/show_bug.cgi?id=53785.
51    QWebNavigationRequest navigationRequest(url, mouseButton, keyboardModifiers, navigationType);
52    emit m_webView->navigationRequested(&navigationRequest);
53
54    switch (navigationRequest.action()) {
55    case QQuickWebView::IgnoreRequest:
56        WKFramePolicyListenerIgnore(listener);
57        return;
58    case QQuickWebViewExperimental::DownloadRequest:
59        WKFramePolicyListenerDownload(listener);
60        return;
61    case QQuickWebView::AcceptRequest:
62        WKFramePolicyListenerUse(listener);
63        return;
64    }
65    ASSERT_NOT_REACHED();
66}
67
68static inline QtWebPagePolicyClient* toQtWebPagePolicyClient(const void* clientInfo)
69{
70    ASSERT(clientInfo);
71    return reinterpret_cast<QtWebPagePolicyClient*>(const_cast<void*>(clientInfo));
72}
73
74static Qt::MouseButton toQtMouseButton(WKEventMouseButton button)
75{
76    switch (button) {
77    case kWKEventMouseButtonLeftButton:
78        return Qt::LeftButton;
79    case kWKEventMouseButtonMiddleButton:
80        return Qt::MiddleButton;
81    case kWKEventMouseButtonRightButton:
82        return Qt::RightButton;
83    case kWKEventMouseButtonNoButton:
84        return Qt::NoButton;
85    }
86    ASSERT_NOT_REACHED();
87    return Qt::NoButton;
88}
89
90static Qt::KeyboardModifiers toQtKeyboardModifiers(WKEventModifiers modifiers)
91{
92    Qt::KeyboardModifiers qtModifiers = Qt::NoModifier;
93    if (modifiers & kWKEventModifiersShiftKey)
94        qtModifiers |= Qt::ShiftModifier;
95    if (modifiers & kWKEventModifiersControlKey)
96        qtModifiers |= Qt::ControlModifier;
97    if (modifiers & kWKEventModifiersAltKey)
98        qtModifiers |= Qt::AltModifier;
99    if (modifiers & kWKEventModifiersMetaKey)
100        qtModifiers |= Qt::MetaModifier;
101    return qtModifiers;
102}
103
104static QQuickWebView::NavigationType toQuickWebViewNavigationType(WKFrameNavigationType navigationType)
105{
106    switch (navigationType) {
107    case kWKFrameNavigationTypeLinkClicked:
108        return QQuickWebView::LinkClickedNavigation;
109    case kWKFrameNavigationTypeFormSubmitted:
110        return QQuickWebView::FormSubmittedNavigation;
111    case kWKFrameNavigationTypeBackForward:
112        return QQuickWebView::BackForwardNavigation;
113    case kWKFrameNavigationTypeReload:
114        return QQuickWebView::ReloadNavigation;
115    case kWKFrameNavigationTypeFormResubmitted:
116        return QQuickWebView::FormResubmittedNavigation;
117    case kWKFrameNavigationTypeOther:
118        return QQuickWebView::OtherNavigation;
119    }
120    ASSERT_NOT_REACHED();
121    return QQuickWebView::OtherNavigation;
122}
123
124void QtWebPagePolicyClient::decidePolicyForNavigationAction(WKPageRef, WKFrameRef frame, WKFrameNavigationType navigationType, WKEventModifiers modifiers, WKEventMouseButton mouseButton, WKURLRequestRef request, WKFramePolicyListenerRef listener, WKTypeRef, const void* clientInfo)
125{
126    WKRetainPtr<WKURLRef> frameURL(AdoptWK, WKFrameCopyURL(frame));
127    WKRetainPtr<WKURLRef> requestURL(AdoptWK, WKURLRequestCopyURL(request));
128    QUrl qUrl = WKURLCopyQUrl(requestURL.get());
129    toQtWebPagePolicyClient(clientInfo)->decidePolicyForNavigationAction(qUrl, toQtMouseButton(mouseButton), toQtKeyboardModifiers(modifiers), toQuickWebViewNavigationType(navigationType), listener);
130}
131
132void QtWebPagePolicyClient::decidePolicyForResponse(WKPageRef page, WKFrameRef frame, WKURLResponseRef response, WKURLRequestRef, WKFramePolicyListenerRef listener, WKTypeRef, const void*)
133{
134    String type = toImpl(response)->resourceResponse().mimeType();
135    type.makeLower();
136    bool canShowMIMEType = toImpl(frame)->canShowMIMEType(type);
137
138    if (WKPageGetMainFrame(page) == frame) {
139        if (canShowMIMEType) {
140            WKFramePolicyListenerUse(listener);
141            return;
142        }
143
144        // If we can't use (show) it then we should download it.
145        WKFramePolicyListenerDownload(listener);
146        return;
147    }
148
149    // 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.
150    // It makes the browser intentionally behave differently when it comes to text(application)/xml content in subframes vs. mainframe.
151    if (!canShowMIMEType && !(type == "text/xml" || type == "application/xml")) {
152        WKFramePolicyListenerIgnore(listener);
153        return;
154    }
155
156    WKFramePolicyListenerUse(listener);
157}
158
159} // namespace WebKit
160
161